在 React 组件中多次使用 this.setState 会发生什么?

IT技术 javascript reactjs
2021-01-15 22:16:50

我想检查多次使用 this.setState 时会发生什么(为了讨论,两次)。我认为该组件将被渲染两次,但显然它只被渲染一次。我的另一个期望是对 setState 的第二次调用可能会超过第一个,但您猜对了 - 工作正常。

链接到JSfiddle

var Hello = React.createClass({
  render: function() {
    return (
      <div>
        <div>Hello {this.props.name}</div>
        <CheckBox />
      </div>
    );
  }
});

var CheckBox = React.createClass({
  getInitialState: function() {
    return {
      alex: 0
    };
  },

  handleChange: function(event) {
    this.setState({
      value: event.target.value
    });
    this.setState({
      alex: 5
    });
  },

  render: function() {
    alert('render');
    return (
      <div>
        <label htmlFor="alex">Alex</label>
        <input type="checkbox" onChange={this.handleChange} name="alex" />
        <div>{this.state.alex}</div>
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

如您所见,每次渲染时都会弹出一个提示“渲染”。

你对它为什么正常工作有解释吗?

2个回答

React 批处理发生在事件处理程序和生命周期方法中的状态更新。因此,如果您在<div onClick />处理程序中多次更新状态,React 将在重新渲染之前等待事件处理完成。

需要明确的是,这仅适用于 React 控制的合成事件处理程序和生命周期方法。例如,状态更新不会在 AJAX 和setTimeout事件处理程序中进行批处理

谢谢,这是有道理的,知道它是如何在幕后完成的吗?
2021-03-13 22:16:50
由于 React 完全控制合成事件处理程序(如<div onClick />)和组件生命周期方法,它知道它可以安全地setState在调用期间更改行为以批量状态更新并等待刷新。setTimeout相比之下,在 AJAX 或处理程序中,React 无法知道我们的处理程序何时完成执行。从技术上讲,React 在两种情况下都将状态更新排入队列,但会立即在 React 控制的处理程序之外刷新。
2021-03-13 22:16:50
@BenjaminToueg 我相信您可以在 React 事件处理程序之外ReactDOM.unstable_batchedUpdates(function)进行批处理setState顾名思义,您将使保修失效。
2021-03-23 22:16:50
2021-03-24 22:16:50
@neurosnap 我认为文档没有对此进行过多详细说明。它在状态和生命周期部分以及setState 文档中抽象地提到查看ReactDefaultBatchingStrategy的代码,这是目前唯一的官方批处理策略。
2021-04-06 22:16:50

setState() 方法不会立即更新组件的状态,它只是将更新放入队列以供稍后处理。React 可以将多个更新请求一起批处理,以提高渲染效率。因此,当您尝试根据组件的先前状态更新状态时,必须采取特殊的预防措施。

例如,即使调用了 4 次,以下代码也只会将状态值属性增加 1:

 class Counter extends React.Component{
   constructor(props){
     super(props)
    //initial state set up
     this.state = {value:0}
   }
   componentDidMount(){
    //updating state
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
   }
   render(){
    return <div>Message:{this.state.value}</div>
   }
}

为了在更新后使用状态,请执行回调参数中的所有逻辑:

//this.state.count is originally 0
this.setState({count:42}, () => {
  console.log(this.state.count)
//outputs 42
})

setState(updater,[callback]) 方法可以将更新程序函数作为其第一个参数,以根据先前的状态和属性更新状态。updater 函数的返回值将与之前的组件状态进行浅合并。该方法异步更新状态,因此有一个选项回调,一旦状态完全完成更新,就会调用该回调。

例子:

this.setState((prevState, props) => { 
return {attribute:"value"}
})

以下是如何根据先前状态更新状态的示例:

    class Counter extends React.Component{
      constructor(props) {
        super(props)
    //initial state set up
        this.state = {message:"initial message"}
    }
      componentDidMount() {
    //updating state
        this.setState((prevState, props) => {
          return {message: prevState.message + '!'}
        })
     }
     render(){
       return <div>Message:{this.state.message}</div>
     }
  }
对我来说,关键是声明previousState参数并在我的更新程序函数中使用它。
2021-03-17 22:16:50
知道 React 如何决定是批量更新还是立即应用它?我正在谈论它如何在代码级别内部执行此操作。
2021-03-17 22:16:50
啊,我不知道setState(updater,[callback]),这就像一个不那么冗长的Object.assign感谢!
2021-03-28 22:16:50
@Biboswan,嗨,我是 React 的新手,想问你,CompountDidMount 方法中的所有四个 setState 都合并了,并且只有最后一个 setState 会被执行,而其他 3 个会被忽略吗?
2021-03-28 22:16:50
@darKnight 它主要基于事件循环的概念,16 毫秒进入 1 帧(每秒 60 帧)。为了获得最佳讨论,我建议您在不和谐的 reactiflux 服务器的内部频道中询问。
2021-04-05 22:16:50