最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Component doesn't re-render when Redux state is changed - React - Stack Overflow

programmeradmin0浏览0评论

I have RowsCont with renderRows

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,
    }
}

Row Component:

    render(){

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

Rows is a collection of objects, each like this: {id: 5, value: 'some value'}

I have reducer, which changes the value of an object with specific 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 
});

Everything works, but, if state is changed, the ponent doesn't re-render. I am sure that state is changed, because I see the new state in mapStateToProps with console.log, every time when it is changed, but I don't see console.log from renderRows.

Each row ponent has and onHover. If I hover the row, it is re-rendered and I see the new state for the row with the new value.

I have RowsCont with renderRows

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,
    }
}

Row Component:

    render(){

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

Rows is a collection of objects, each like this: {id: 5, value: 'some value'}

I have reducer, which changes the value of an object with specific 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 
});

Everything works, but, if state is changed, the ponent doesn't re-render. I am sure that state is changed, because I see the new state in mapStateToProps with console.log, every time when it is changed, but I don't see console.log from renderRows.

Each row ponent has and onHover. If I hover the row, it is re-rendered and I see the new state for the row with the new value.

Share Improve this question edited May 4, 2019 at 20:29 gdfgdfg asked May 4, 2019 at 20:06 gdfgdfggdfgdfg 3,5768 gold badges46 silver badges85 bronze badges 2
  • 1 In this line result.push(<Rowkey={row[key].id} , I notices that it should be rows instead of row should it be result.push(<Rowkey={rows[key].id} – acarlstein Commented May 4, 2019 at 20:24
  • Yes, it is rows. * Edited. – gdfgdfg Commented May 4, 2019 at 20:29
Add a ment  | 

2 Answers 2

Reset to default 3

Your ponent is not rerendering because your object is the same as it was before

In a rendering cycle, React does a diff check on the new ining props. If anything is different, it re-renders. In your case, the object is the same object as before.

As an example:

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

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

React does a diff check between the rows and the oldRows and sees that the object itself is the same! Since the diff check sees equality, no re-rendering occurs.

The easiest way around this is to update prop values by their primitives within the object.

For example this will force a rerendering cycle when a change occurs:

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

If an object is absolutely necessary to be passed to your ponent, you can create a new object in your redux stores. Something like state.rows = Object.assign({}, oldRows) should do the trick.

in redux you should never mutate the state, so currentRows[changedIndex].value = action.value; is forbidden

Here is a possible solution :

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

    return {...state, rows};

In your solution, you don't have a new reference for the prop state.rows after executing the case SET_NEW_ROW_VALUE. As react-redux use shallow parison (=== operator) to detect if an object has changed, with your solution the ponent will not be re-rendered as state.rows will be the same object (same reference) than before

发布评论

评论列表(0)

  1. 暂无评论