在setInterval和onClick中,react setstate接受的对象是不同的

时间:2017-12-02 03:51:37

标签: javascript reactjs

我对setState()接受的对象感到困惑。代码链接在https://codepen.io/DRL9/pen/jadbWq,代码如下:

class Counter extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        intervalCount: 1,
        buttonCount: 1
    };
    this.increment = 1;
    this.intervalId = null;
}
tick() {
    this.setState({
        intervalCount: this.state.intervalCount + this.increment
    });
    this.setState({
        intervalCount: this.state.intervalCount + this.increment
    });
}
onClick() {
    this.setState({
        buttonCount: this.state.buttonCount + this.increment
    });
    this.setState({
        buttonCount: this.state.buttonCount + this.increment
    });
}
componentDidMount() {
    this.intervalId = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
    clearInterval(this.intervalId);
}
render() {
    return <div>
        <div>
            interval counter: {this.state.intervalCount}
        </div>
        <button onClick={this.onClick.bind(this)}>increment</button>
        <div>
            button counter: {this.state.buttonCount}
        </div>
    </div>;
}
}

我希望intervalCount会增加 1 ,就像我点击增量按钮时的行为一样。但是,每增加一个增量 2 。 唯一不同的是,一个更新的是setInterval函数,另一个更新的是onClick函数。

为什么他们的行为不同?

2 个答案:

答案 0 :(得分:2)

根据定义,我们不能绝对谈论setState的时间,根据定义,不可预测。状态更改可能会延迟到将来某个时间,并且此行为可能会有所不同,具体取决于您使用的React版本。

在提供的示例中,React延迟状态更新,直到onClick处理程序完成运行。 React知道这个处理程序何时完成运行,因为我们通过JSX&#39; s onClick传递处理程序(然后由React内部处理):

// React processes the onClick handler below
<button id="btn" onClick={this.onClick.bind(this)}>increment</button> 

如果我们自己检测onClick逻辑,可以手动从DOM中抓取button元素并添加一个调用我们的onClick处理程序的click事件监听器,{{1与button完全相同的更新(React并不知道我们在点击处理程序中更新状态,所以它选择不优化将调用批处理到setInterval)。 / p>

请参阅此 codepen ,其中按钮计数器在setState函数中手动添加了一个点击处理程序,而不是使用JSX&#39; s {{1 }}。请注意,按钮计数器现在以间隔2而不是1递增。

我想强调这种行为不确定,您绝不应在componentDidMount函数中使用onClick。相反,您希望使用接受包含先前状态的更新程序功能的this.state变体。然后,从传递给更新程序的状态构建新状态:

setState

请参阅此 codepen ,它使用更新程序更新按钮计数器,产生以2为间隔更新按钮计数器的预期效果。

有关setState的详情,请参阅 official documentation

答案 1 :(得分:1)

来自documentation for setState

  

将setState()视为请求而不是更新组件的立即命令。为了获得更好的感知性能,React可能会延迟它,然后在一次通过中更新几个组件。 React不保证立即应用状态更改。

这是说当您使用this.state.buttonCount更改后立即引用状态数据(this.state.intervalCountsetState)时(正如您在第二个函数中所做的那样) setState命令)行为将是不可预测的。也许setState会立即更新状态数据,因为它似乎与intervalCount一样,并且可能setState等待更新状态数据,以便稍后可以批量处理,因为它似乎是与buttonCount合作。作为开发人员,我们应该避免在我们想要在同一事件中多次修改状态时使用其他变量让自己暴露于这种不可预测的行为。

至于为什么intervalCount被立即相当一致地更新(并因此第二次递增)并且buttonCount一直被批量处理(并且仅对两次调用{{1}增加一次}我的猜测是这样的:setState是由用户交互触发的,所以React引擎可能猜测在用户交互期间很多状态可能正在改变,所以它将调用批处理到setState,可能直到事件完全传播。另一方面,onClick由内部回调触发,没有任何用户交互被处理,因此React引擎可能会猜测它可以安全地立即更新状态而无需批处理。