【问题标题】:How to re-render child component In this situation?在这种情况下如何重新渲染子组件?
【发布时间】:2018-06-10 15:00:01
【问题描述】:

问题

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

如您所见,子组件不会重新渲染。 (如果我将 '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);

父.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);

【问题讨论】:

  • 你想让所有的值都加一吗?
  • @Sajith Dilshan 我不知道我是否正确理解了您的问题。我希望通过单击每个 元素来增加每个数字。

标签: reactjs redux immutability


【解决方案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);`

【讨论】:

  • numbers.push({ ... }) 将向数组添加更多项,这不是最初的想法。在action 中传递的目标数应该增加它的值。
【解决方案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

【讨论】:

  • 非常感谢。除了深拷贝之外,你碰巧知道什么吗?这是因为实际源代码中的对象有很多深度和长度。
  • 没有问题。我不确定是否还有其他方法......你可以看看这个SO question关于如何使用复杂(深度)状态对象,也许那里有一些有用的东西。但是如果你仍然使用深拷贝,你可以像state.arrayOfComplexObjects.map(obj =&gt; ({ ...obj }));这样的东西来避免手动拷贝。
  • 好的,谢谢。 :)
【解决方案3】:

你需要修改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

【讨论】:

    【解决方案4】:

    稍微修改了你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);
    

    【讨论】:

      【解决方案5】:

      这是因为键可以帮助您确定该元素是否 在虚拟 Dom 中进行比较时发生了变化, 因此,如果元素的 id 相同, 即:第一个 li 为 1,第二个 li 为 2, 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] }
          }
      

      现在键每次都会更新值,但应该 不会增加,因为它将是第二个 li 的相同键 dom会给你一个错误 所以每次 1 到 2 它的 id 都会去——即 1-1 =0

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

      【讨论】:

      • 非常感谢。 :) 目前,我也在使用类似的方法,因为需要重新渲染所有子组件并进行深度复制。但是,如果
      • 元素具有相同的 id,我想我在添加
      • 元素时遇到了问题,并且我使用了小数点。
      猜你喜欢
      相关资源
      最近更新 更多
      热门标签