0

Problem

If I click a number, the number should increase but not increase.

As you can see, child components are not re-rendered. (If I change the key of the 'li' element to Math.random() it works fine.)

How can I solve this situation?

An example was posted on https://codesandbox.io/s/p5q30rxk47

Thanks for reading.

Source code

The source code is roughly as follows.

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);
left click
  • 894
  • 10
  • 21

5 Answers5

3

This is because keys helps you to figure if that element has changed when it gets compared in the virtual Dom, so if the id is same for the elements, ie : 1 for first li, 2 for second li, the dom will never know since the element updates based on change in virtual dom , even though the values are changing possible solution could be to use id's different from 1, 2, 3 and update the id along with the value so that the Dom is able to figure out the change.

one Possible hack could be

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

now the key will update everytime with the value but should not increase since it will be same key of the 2nd li and the dom will give you an error so everytime 1 goes to 2 its id will go -- ie 1-1 =0

here is the working Sample
https://codesandbox.io/s/mz6zy5rq28
Shruti Singh
  • 181
  • 1
  • 11
  • Thank you so much. :) Currently, I was also using a similar approach because of the burden of re-rendering all child components and making deep copies. However, if the
  • elements have the same id, I think I'm having trouble adding the
  • element, and I'm using a decimal point.
  • – left click Dec 31 '17 at 06:09