更改Redux状态时,组件不会重新渲染-React

时间:2019-05-04 20:06:36

标签: javascript reactjs redux react-redux

我有RowsContrenderRows

renderRows = () => {

    let result = [];

    const {rows} = this.props;
    console.log('RENDER');
    console.log(rows);
    for(let key in rows){
        result.push(<Row={rows[key].id} 
            onHover={this.onHover}
            row={rows[key]}/>);
        }
    return result;
};
....
render(){<div>{this.renderRows()}</div>}

....
function mapStateToProps(state) {
    console.log('mapState');
    console.log(state.rows.rows);
    return  {
        rows: state.rows.rows,
    }
}

行组件:

    render(){

        return (
            <div>
                <div>{this.props.row.id}</div>
                <div>{this.props.row.value}</div>
            </div>
        )
    }

行是对象的集合,每个对象都是这样的:{id: 5, value: 'some value'}

我有reducer,它可以更改具有特定ID的对象的值:

case 'SET_NEW_ROW_VALUE':
        let currentRows = state.rows;
        const changedIndex = currentRows .findIndex((obj) => obj.id === action.id);

        currentRows [changedIndex].value = action.value;

        return Object.assign({}, state, {
            rows: currentRows 
});

一切正常,但是,如果状态发生变化,则组件不会重新渲染。 我确定状态已更改,因为每次更改时,我都会在mapStateToProps中看到console.log中的新状态,但是看不到{{1}中的console.log }。

每个行组件都具有和renderRows。如果我将鼠标悬停在该行上,则会重新渲染该行,并看到具有新值的行的新状态。

2 个答案:

答案 0 :(得分:1)

您的组件未重新呈现,因为您的对象与以前相同

在渲染周期中,React对新传入的道具进行差异检查。如果有任何不同,它将重新渲染。在您的情况下,该对象与以前的对象相同。

例如:

var rows = {id: 1, value: 'someVal'};
var oldRows = rows;
rows.id = 2;
rows.value = 'someNewVal';

console.log(rows === oldRows); // => true

React在rowsoldRows之间进行差异检查,发现对象本身是相同的!由于diff检查发现相等,因此不会发生任何重新渲染。

解决此问题的最简单方法是在对象内通过属性值更新属性值

例如,当发生更改时,这将强制重新渲染周期:

function mapStateToProps(state) {
    console.log('mapState');
    console.log(state.rows.rows);
    return  {
        id: state.rows.rows.id,
        value: state.rows.rows.value
    }
}

如果绝对需要将对象传递给组件,则可以在redux存储中创建一个新对象。像state.rows = Object.assign({}, oldRows)之类的方法就可以解决问题。

答案 1 :(得分:1)

在还原中,您应该从不更改状态,因此currentRows[changedIndex].value = action.value;被禁止

这是一个可能的解决方案:

case 'SET_NEW_ROW_VALUE':
    const rows = state.rows.map(
      (row, index) => row.id !== action.id ? 
          row : 
          {...row, value: action.value}
    );

    return {...state, rows};

在您的解决方案中,执行案例state.rows后,您没有针对道具SET_NEW_ROW_VALUE的新引用。由于react-redux使用shallow comparison===运算符)来检测对象是否已更改,因此您的解决方案将不会重新呈现该组件,因为state.rows将是同一对象(参考相同)