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

javascript - Proper handling of multiple sequential state updates - Stack Overflow

programmeradmin0浏览0评论

This example is a bit contrived, but bear with me. Say I have two functions as members in a React ponent:

var OrderApp = React.createClass({
    getInitialState: function() {
        return {
            selectedOrders: {},
            selectedLineItems: {}
        };
    },

    setOrderSelected: function(order, newSelectedState) {
        var mutation = {};
        mutation[order.id] = {$set: newSelectedState};
        var newSelectedOrders = React.addons(this.state.selectedOrders, mutation);
        _.each(order.lineItems, function(lineItem) {
            setLineItemSelected(lineItem, newSelectedState);
        });
        this.setState({selectedOrders: newSelectedOrders});
    },

    setLineItemSelected: function(lineItem, newSelectedState) {
        var mutation = {};
        mutation[lineItem.id] = {$set: newSelectedState};
        var newSelectedLineItems = React.addons(this.state.selectedLineItems, mutation);
        this.setState({seelctedLineItems: newSelectedLineItems});
    }
});

At the pletion of calling OrderApp.setOrderSelected(order, true), only the last lineItem selected state appears to have changed in my ponent's state. I'm guessing this is because React is batching the calls to setState, and so when I'm calling:

var newSelectedLineItems = React.addons(this.state.selectedLineItems, mutation);

in the second function, I'm picking up the original state each time, and so only the last mutation takes effect.

Should I be collecting all of the state mutations in the parent function and only calling setState once? This feels a bit clunky to me, and I'm wondering if I'm missing a simpler solution.

This example is a bit contrived, but bear with me. Say I have two functions as members in a React ponent:

var OrderApp = React.createClass({
    getInitialState: function() {
        return {
            selectedOrders: {},
            selectedLineItems: {}
        };
    },

    setOrderSelected: function(order, newSelectedState) {
        var mutation = {};
        mutation[order.id] = {$set: newSelectedState};
        var newSelectedOrders = React.addons(this.state.selectedOrders, mutation);
        _.each(order.lineItems, function(lineItem) {
            setLineItemSelected(lineItem, newSelectedState);
        });
        this.setState({selectedOrders: newSelectedOrders});
    },

    setLineItemSelected: function(lineItem, newSelectedState) {
        var mutation = {};
        mutation[lineItem.id] = {$set: newSelectedState};
        var newSelectedLineItems = React.addons(this.state.selectedLineItems, mutation);
        this.setState({seelctedLineItems: newSelectedLineItems});
    }
});

At the pletion of calling OrderApp.setOrderSelected(order, true), only the last lineItem selected state appears to have changed in my ponent's state. I'm guessing this is because React is batching the calls to setState, and so when I'm calling:

var newSelectedLineItems = React.addons(this.state.selectedLineItems, mutation);

in the second function, I'm picking up the original state each time, and so only the last mutation takes effect.

Should I be collecting all of the state mutations in the parent function and only calling setState once? This feels a bit clunky to me, and I'm wondering if I'm missing a simpler solution.

Share Improve this question edited Oct 3, 2014 at 18:43 Brigand 86.3k20 gold badges167 silver badges173 bronze badges asked Oct 3, 2014 at 17:27 tnorwoodtnorwood 1392 silver badges10 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

You should never be calling this.setState more than once in a given thread of execution within a ponent.

So you have a couple of options:

Use forceUpdate instead of setState

You don't HAVE to use this.setState - you can modify the state directly and use this.forceUpdate after you're done.

The main thing to remember here is that you should treat this.state as tainted as soon as you start modifying it directly. IE: don't use the state, or properties you know you've modified until after you've called this.forceUpdate. This isn't usually a problem.

I absolutely prefer this method when, you're dealing with nested objects within this.state. Personally I'm not a huge fan of React.addons.update because I find the syntax extremely clunky. I prefer to modify directly and forceUpdate.

Create an object that you pass around until finally using it in setState

Collect an object of all your changes, to pass to this.setState at the end of your thread of execution.

Unfortunately, this is a problem with shallow merge. You do have to collect the updates, and apply them at one time.

feels a bit clunky to me

This is a really good sign that you should be using a mixin! This code looks pretty terrible, but you can just pretend it looks pretty when you're using it.

var React = require('react/addons');
var UpdatesMixin = {
    ponentDidMount: function(){
        this.__updates_list = [];
    },
    addUpdate: function(update){
        this.__updates_list.push(update);
        if (this.__updates_list.length === 1) {
         process.nextTick(function(){
          if (!this.isMounted()) {
            this.__updates_list = null;
            return;
          }

          var state = this.__updates_list.reduce(function(state, update){
            return React.addons.update(state, update);
          }, this.state);

          this.setState(state);
          this.__updates_list = [];     
        }.bind(this));
      }
    }
};

requirebin

It buffers all updates that occur synchronously, applies them to this.state, and then does a setState.

The usage looks like this (see the requirebin for a full demo).

this.addUpdate({a: {b: {$set: 'foo'}};
this.addUpdate({a: {c: {$set: 'bar'}};

There are some places it could be optimized (by merging the updates objects, for example), but you can do that later without changing your ponents.

发布评论

评论列表(0)

  1. 暂无评论