How can I remove an attribute from a Reactjs component's state object

后端 未结 10 1987
南笙
南笙 2020-12-06 09:17

If I have a react component that had a property set on it\'s state:

onClick() {
    this.setState({ foo: \'bar\' });
}

Is it possible to re

相关标签:
10条回答
  • 2020-12-06 09:29

    If you want to completely reset the state (removing a large number of items), something like this works:

    this.setState(prevState => {
        let newState = {};
        Object.keys(prevState).forEach(k => {
            newState[k] = undefined;
        });
        return newState;
    });
    

    Using this variant of setState allows you to access the whole state during the call, whereas this.state could be a little out of date (due to prior setState calls not yet having been fully processed).

    0 讨论(0)
  • 2020-12-06 09:29

    I think this is a nice way to go about it =>

    //in constructor
    let state = { 
       sampleObject: {0: a, 1: b, 2: c }
    }
    
    //method
    removeObjectFromState = (objectKey) => {
       let reducedObject = {}
       Object.keys(this.state.sampleObject).map((key) => {
          if(key !== objectKey) reducedObject[key] = this.state.sampleObject[key];
       })
       this.setState({ sampleObject: reducedObject });
    }
    
    0 讨论(0)
  • 2020-12-06 09:31

    Previous solution - is antipattern, because it change this.state. It is wrong!

    Use this (old way):

    let newState = Object.assign({}, this.state) // Copy state
    newState.foo = null // modyfy copyed object, not original state
    // newState.foo = undefined // works too
    // delete newState.foo // Wrong, do not do this
    this.setState(newState) // set new state
    

    Or use ES6 sugar:

    this.setState({...o, a:undefined})
    

    Pretty sweet, don't you? ))

    In old React syntax (original, not ES6), this has this.replaceState, that remove unnecessary keys in store, but now it is deprecated

    0 讨论(0)
  • 2020-12-06 09:33

    You can set foo to undefined, like so

    var Hello = React.createClass({
        getInitialState: function () {
            return {
                foo: 10,
                bar: 10
            }
        },
    
        handleClick: function () {
            this.setState({ foo: undefined });
        },
    
        render: function() {
            return (
                <div>
                    <div onClick={ this.handleClick.bind(this) }>Remove foo</div>
                    <div>Foo { this.state.foo }</div>
                    <div>Bar { this.state.bar }</div>
                </div>
            );
        }
    });
    

    Example

    Update

    The previous solution just remove value from foo and key skill exists in state, if you need completely remove key from state, one of possible solution can be setState with one parent key, like so

    var Hello = React.createClass({
      getInitialState: function () {
        return {
          data: {
            foo: 10,
            bar: 10
          }
        }
      },
        	
      handleClick: function () {
        const state = {
          data: _.omit(this.state.data, 'foo')
        };
        
        this.setState(state, () => {
          console.log(this.state);
        });
      },
            
      render: function() {
        return (
          <div>
            <div onClick={ this.handleClick }>Remove foo</div>
            <div>Foo { this.state.data.foo }</div>
            <div>Bar { this.state.data.bar }</div>
          </div>
        );
      }
    });
    
    ReactDOM.render(<Hello />, document.getElementById('container'))
    <script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="container"></div>

    0 讨论(0)
  • 2020-12-06 09:42
    var Hello = React.createClass({
    getInitialState: function () {
        return {
            foo: 10,
            bar: 10
        }
    },
    
    handleClick: function () {
        let state = {...this.state};
        delete state.foo;
        this.setState(state);
    },
    
    render: function() {
        return (
            <div>
                <div onClick={ this.handleClick.bind(this) }>Remove foo</div>
                <div>Foo { this.state.foo }</div>
                <div>Bar { this.state.bar }</div>
            </div>
        );
    }
    

    });

    0 讨论(0)
  • 2020-12-06 09:43

    In ReactCompositeComponent.js in the React source on GitHub is a method called _processPendingState, which is the ultimate method which implements merging state from calls to component.setState;

    ``` _processPendingState: function(props, context) { var inst = this._instance; var queue = this._pendingStateQueue; var replace = this._pendingReplaceState; this._pendingReplaceState = false; this._pendingStateQueue = null;

    if (!queue) {
      return inst.state;
    }
    
    if (replace && queue.length === 1) {
      return queue[0];
    }
    
    var nextState = replace ? queue[0] : inst.state;
    var dontMutate = true;
    for (var i = replace ? 1 : 0; i < queue.length; i++) {
      var partial = queue[i];
      let partialState = typeof partial === 'function'
        ? partial.call(inst, nextState, props, context)
        : partial;
      if (partialState) {
        if (dontMutate) {
          dontMutate = false;
          nextState = Object.assign({}, nextState, partialState);
        } else {
          Object.assign(nextState, partialState);
        }
      }
    }
    

    ```

    In that code you can see the actual line that implements the merge;

    nextState = Object.assign({}, nextState, partialState);
    

    Nowhere in this function is there a call to delete or similar, which means it's not really intended behaviour. Also, completely copying the stat, deleting the property, and calling setState won't work because setState is always a merge, so the deleted property will just be ignored.

    Note also that setState does not work immediately, but batches changes, so if you try to clone the entire state object and only make a one-property change, you may wipe over previous calls to setState. As the React document says;

    React may batch multiple setState() calls into a single update for performance.

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

    What you could look to do is actually add more info;

    this.setState({ xSet: true, x: 'foo' });
    this.setState({ xSet: false, x: undefined });
    

    This is ugly, granted, but it gives you the extra piece of info you need to differentiate between a value set to undefined, and a value not set at all. Plus it plays nice with React's internals, transactions, state change batching, and any other horror. Better to take a bit of extra complexity here than try to second-guess Reacts internals, which are full of horrors like transaction reconciliation, managing deprecated features like replaceState, etc

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