如何在这种情况下重新渲染子组件?

时间:2017-12-30 14:45:37

标签: reactjs redux immutability

问题

如果我点击一个数字,数字应该增加但不会增加。

如您所见,子组件不会重新渲染。 (如果我将' li'元素的键更改为Math.random(),它可以正常工作。)

我该如何解决这种情况?

一个例子发布在https://codesandbox.io/s/p5q30rxk47

感谢阅读。

源代码

源代码大致如下。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import Parent from './Parent';

import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './redux';

const body = document.querySelector('body'),
      store = createStore(reducer);

ReactDOM.render(<Provider store={store}><Parent/></Provider>, body);

Parent.js

import React from 'react';
import { connect } from 'react-redux';
import Child from './Child';

class Parent extends React.PureComponent {
    get_numbers () {
        return this.props.numbers.map((number) => (
            <li key={number.id}>
                <span>number : </span><br/>
                <Child number={number} />
            </li>
        ));
    }

    render () {
        return (
            <ul>
                {this.get_numbers()}
            </ul>
        );
    }
}


function mapStateToProps(state) {
    return { numbers: state.numbers };
}

Parent = connect(mapStateToProps)(Parent);

export default Parent;

Child.js

import React from 'react';
import { connect } from 'react-redux';
import { increase_number } from './redux';

class Child extends React.PureComponent {
    render() {
        return (
            <span onClick={() => this.props.increase_number(this.props.number)}>{this.props.number.value}</span>
        );
    }
}

function mapDispatchToProps(dispatch) {
    return {
        increase_number: (number) => dispatch(increase_number({ number }))
    };
}

Child = connect(undefined, mapDispatchToProps)(Child);

export default Child;

redux.js

import { createAction, handleActions } from 'redux-actions';

export const increase_number = createAction('increase_number');

const initial_state = {
    numbers: [
        { id: 1, value: 1 },
        { id: 2, value: 2 },
        { id: 3, value: 3 }
    ]
};

export default handleActions({
    increase_number: (state, action) => {
        // console.log(action.payload.number.value);
        action.payload.number.value++;
        // console.log(action.payload.number.value);
        return { ...state, numbers: [...state.numbers] }
    }
}, initial_state);

5 个答案:

答案 0 :(得分:3)

这是因为键可以帮助您确定该元素  在虚拟Dom中进行比较时发生了变化,  所以如果元素的id相同,  即:1表示第一个li,2表示第二个li,  自元素更新以来,dom永远不会知道  基于虚拟dom的变化,即使是值  正在改变  可能的解决方案可能是使用不同于1,2,3的id并更新id和值,以便Dom能够找出变化。

一个可能的黑客可能是

increase_number: (state, action) => {
         //console.log(action.payload.number.value);
        action.payload.number.value++;
        action.payload.number.id--;
        //console.log(action.payload.number.value);
        return { ...state, numbers: [...state.numbers] }
    }

现在密钥会随时更新值,但应该更新 不会增加,因为它将是第二李的同一把钥匙 而dom会给你一个错误     所以每次1到2时它的id都会去 - 即1-1 = 0

here is the working Sample
https://codesandbox.io/s/mz6zy5rq28

答案 1 :(得分:2)

您需要获取numbers数组的深层副本,然后增加payload中传递的数字的值,如下所示:

export default handleActions({
  increase_number: (state, action) => {
      let numberToModify = action.payload.number;

      // make a new array with new objects
      let newNumbers = state.numbers.map(n => ({ id: n.id, value: n.value }));

      // find the one that needs to be modified
      let existingNumber = newNumbers.find((number) => (number.id === numberToModify.id));
      if (existingNumber) {
        existingNumber.value++;
      }

      return { ...state, numbers: newNumbers };
  }
}, initial_state);

制作了一个实例here

答案 2 :(得分:1)

您需要修改handleAction,如下所示

export default handleActions({
increase_number: (state, action) => {
   action.payload.number.value++
   const currentNum = action.payload.number;

   let newLst = state.numbers.map((num) => {
     return num.id === currentNum.id ? {...action.payload.number} : num
    })

    return { ...state, numbers: newLst }
  }
}, initial_state);

这是工作样本 https://codesandbox.io/s/4jor55xz5w

答案 3 :(得分:1)

略微修改了你handleActions

export default handleActions({
    increase_number: (state, action) => {
      action.payload.number.value++;

      const currNumber = action.payload.number;

      const numbers = state.numbers.map((num) => {
        return num.id === currNumber.id ? { ...action.payload.number } : num
      });

      return { ...state, numbers }
    }
}, initial_state);

答案 4 :(得分:0)

要将值增加1,您应该在redux.js文件中进行更改,如下所示:

import { createAction, handleActions } from 'redux-actions';

export const increase_number = createAction('increase_number');

const initial_state = {
    numbers: [
        { id: 1, value: 1 },
        { id: 2, value: 2 },
        { id: 3, value: 3 }
    ]
};

export default handleActions({
    increase_number: (state, action) => {
        console.log(action.payload.number.value);
        // action.payload.numbers.value++;
        const numbers = [...state.numbers];
        numbers.push({
          id: action.payload.number.value++,
          value: action.payload.number.value++
        })
        // console.log(action.payload.number.value);
        return { ...state, numbers: numbers }
    }
}, initial_state);`