React.js: setState overwriting, not merging

后端 未结 3 1189
抹茶落季
抹茶落季 2020-12-04 14:13

I\'m quite new to React.JS and I am in the process of experimenting by building a masonry-style layout.

I render each element to the DOM, then I need to loop over eac

相关标签:
3条回答
  • 2020-12-04 14:41

    If your state is an object:

    getInitialState: function() {
      return { x: 0, y: 0 };
    }
    

    you can use setState to set individual keys on that object:

    this.setState({ x: 1 }); // y still == 0
    

    React does no intelligent merging of your state; for example, this does not work:

    getInitialState: function() {
      return {
        point: { x: 0, y: 0 },
        radius: 10
      };
    }
    
    this.setState({point: {x: 1}});
    // state is now == {point: {x: 1}, radius: 10} (point.y is gone)
    

    [Edit]

    As mentioned by @ssorallen, you can use the immutability helpers to get the effect you're after:

    var newState = React.addons.update(this.state, {
      point: { x: {$set: 10} }
    });
    this.setState(newState);
    

    See this JSFiddle for an example: http://jsfiddle.net/BinaryMuse/HW6w5/

    0 讨论(0)
  • 2020-12-04 15:00

    Something like:

    getInitialState: function() {
        return {
            something: { x: 0, y: 0 },
            blah: 10
        };
    }
    
    var state = Object.assign(this.state, {
        something: Object.assign(this.state.something, { y: 50 }),
    });
    
    this.setState(state);
    

    Would be better if it was recursive/deep rather than hard coding the tree, but I will leave that up to the reader :)

    0 讨论(0)
  • 2020-12-04 15:01

    The merging is shallow, so this.setState({point}) leaves (ed: this.state.radius) intact, but completely replaces (ed: this.state.point).

    https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-are-merged

    To offer an ES7+ perspective on the answers already given, using transform-object-rest-spread instead of Object.assign():

    class MyComponent extends React.Component {
        state = {
            point: { 
                x: 0, 
                y: 0,
            },
            radius: 10,
        }
    
        handleChange = () => {
            this.setState((prevState, props) => ({
                point: {
                    // rest operator (...) expands out to:
                    ...prevState.point, // x:0, y:0,
                    y: 1, // overwrites old y
                },
                // radius is not overwritten by setState
            }));
        }
    
        render() {
            // omitted
        }
    }
    

    .babelrc (also requires transform-class-properties from babel preset stage 2)

    {
        "presets": ["es2015", "stage-2", "react"],
        "plugins": ["transform-object-rest-spread"],
    }
    

    Updated 2018-04-22

    As @sheljohn points out (thanks!), referring to this.state inside setState is unreliable:

    Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

    ...

    To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument

    https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

    0 讨论(0)
提交回复
热议问题