连续setState不能按预期工作

时间:2018-06-29 14:32:09

标签: javascript reactjs setstate

我下面有一段代码-

class Sum extends React.Component {
    constructor(props) {
      super(props)
      this.state = { a : 0 }
    }

    // let's call this ADD-1
    add = () => {
      this.setState({ a: this.state.a + 1 })
      this.setState({ a: this.state.a + 2 })
      this.setState({ a: this.state.a + 3 })
    } 

    render() {
      return (<div>
        <button onClick={this.add}> click me  </button>
        <div> sum  {this.state.a} </div>
      </div>)
    }
}

点击按钮

sum = 3

我希望它可以呈现sum = 6的位置,即1 + 2 + 3

另外,如果我将add的方法更改为适合prevState竞赛条件的某种方法,则

  // let's call this ADD-2
  add = () => {
    this.setState({ a: this.state.a + 1 })
    this.setState({ a: this.state.a + 2 })
    this.setState(prevState => ({ a: prevState.a + 1 }))
    this.setState(prevState => ({ a: prevState.a + 4 }))
  }

它呈现sum = 7,而我希望sum = 8即(1 + 2 + 1 + 4)

现在我想到两个问题:-

1)为什么我们将结果视为上述结果而不是我所期望的结果?

2)为什么在UI中看不到添加的过渡? 假设我们考虑标记为ADD-1的方法,我应该看到类似sum = 1然后sum = 3然后sum = 6的内容。是因为批处理了更新,但批处理却将它们排入了执行队列,在我看来,它不会覆盖任何内容。

4 个答案:

答案 0 :(得分:1)

“ React可以将多个setState()调用批处理为单个更新,以提高性能。”

Docs

答案 1 :(得分:1)

请确保在更新依赖于当前状态时将函数传递给setState,以免后续的setState覆盖它。

示例

class App extends React.Component {
  state = { a: 0 };

  add = () => {
    this.setState(previousState => {
      return { a: previousState.a + 1 };
    });
    this.setState(previousState => {
      return { a: previousState.a + 2 };
    });
    this.setState(previousState => {
      return { a: previousState.a + 3 };
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.add}> click me </button>
        <div> sum {this.state.a} </div>
      </div>
    );
  }
}

答案 2 :(得分:1)

状态更新可能是异步的。选中此answer

丹·阿布拉莫夫(Dan abramov)的answer中说,在一个事件调用中进行状态更新只会在事件结束时产生一次重新渲染。

  

无论您在多少个组件中调用了多少setState()   在React事件处理程序中,它们只会产生一个   在活动结束时重新渲染。

而且批处理仅发生在React事件处理程序中的状态更新上,即批处理不在AJAX调用内发生

promise.then(() => {
  // We're not in an event handler, so these are flushed separately.
  this.setState({a: true}); // Re-renders with {a: true, b: false }
  this.setState({b: true}); // Re-renders with {a: true, b: true }
  this.props.setParentState(); // Re-renders the parent
});

但是您可以通过将回调传递给setState方法来实现您想要的

add = () => {
      this.setState({ a: this.state.a + 1 },
        () => {
        this.setState({ a: this.state.a + 2 },
          () => {
          this.setState({ a: this.state.a + 3 })
        })
      })    
    }

以上将返回sum = 6。

何时在setState中不使用回调:

  

PureComponentshouldComponentUpdate可用于调优   组件的性能。他们通过防止生命周期方法来工作   在道具和状态未改变时开火。

     

无论setState是什么,都会触发shouldComponentUpdate回调   返回。因此,即使状态尚未发生,setState回调也会触发   改变了。

答案 3 :(得分:0)

状态更改只能通过setState发生,但是如果在setState(this.state.a + 1)之前访问和修改this.state,则不会反映出来。因此,每次您访问this.state.a时,它将获得0,即初始值。

add = () => {
  this.setState({ a: this.state.a + 1 }) // 0 + 1
  this.setState({ a: this.state.a + 2 }) // 0 + 2
  this.setState({ a: this.state.a + 3 }) // 0 + 3
} 

this.setState((previousState) => { // change state using previousState} );

这是必须更新状态的方式