Reactjs 中是否有 setState() 的同步替代方案

IT技术 javascript reactjs
2021-03-09 05:29:53

根据文档中的解释

setState() 不会立即改变 this.state 而是创建一个挂起的状态转换。调用此方法后访问 this.state 可能会返回现有值。

无法保证对 setState 调用的同步操作,并且可能会批量调用以提高性能。

所以因为setState()是异步的,所以不能保证它的同步性能。有没有替代方案setState()是同步的。

例如

//initial value of cnt:0
this.setState({cnt:this.state.cnt+1})
alert(this.state.cnt);    //alert value:0

由于该alert值是先前的值,因此alert value:1使用setState().

Stackoverflow 上有几个问题与这个问题类似,但我找不到正确的答案。

6个回答

正如您从文档中读到的,没有同步替代方案,所描述的原因是性能提升。

但是,我认为您想在更改状态后执行操作,您可以通过以下方式实现:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     x: 1
    };
    
    console.log('initial state', this.state);
  }
  
  updateState = () => {
   console.log('changing state');
    this.setState({
      x: 2
    },() => { console.log('new state', this.state); })
  }
  
  render() {
    return (
      <div>
      <button onClick={this.updateState}>Change state</button>
    </div>
    );
   
  }
}

ReactDOM.render(
  <MyComponent />,
  document.getElementById("react")
);
<div id="react"></div>
<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>

你能给我举个例子吗
2021-04-24 05:29:53
太感谢了。正好在找这个。
2021-05-04 05:29:53
你拯救了我的一天!谢谢
2021-05-12 05:29:53

您可以包装setState在一个返回Promise的函数中,然后将此函数与await关键字一起使用,以使您的代码等待直到应用状态。

就个人而言,我永远不会在实际代码中执行此操作,而是将我希望在状态更新后执行的代码放入setState回调中。

不用担心,这是一个例子。

class MyComponent extends React.Component {

    function setStateSynchronous(stateUpdate) {
        return new Promise(resolve => {
            this.setState(stateUpdate, () => resolve());
        });
    }

    async function foo() {
        // state.count has value of 0
        await setStateSynchronous(state => ({count: state.count+1}));
        // execution will only resume here once state has been applied
        console.log(this.state.count);  // output will be 1
    }
} 

在 foo 函数中,await关键字会导致代码执行暂停,直到返回的 promisesetStateSynchronous被解析,这只会在调用传递给的回调setState时发生,只有在应用状态时才会发生。因此,一旦应用了状态更新,执行只会到达 console.log 调用。

async/await 的文档:
https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript /参考/运营商/等待

如果这是必需的,我建议在您的 setState 函数中使用回调(并且我还建议使用功能性 setState)。

状态更新后将调用回调。

例如,你的例子是

//initial value of cnt:0
this.setState(
    (state) => ({cnt: state.cnt+1}),
    () => { alert(this.state.cnt)}
)

根据此处的文档:https : //facebook.github.io/react/docs/react-component.html#setstate

注意:官方文档说,“通常我们建议使用 componentDidUpdate() 来代替这种逻辑。”

不,那里没有。React 会在它认为合适的时候更新状态,做一些事情,比如将setState调用一起批处理以提高效率。您可能会感兴趣的是,您可以将一个函数传入setState,它采用先前的状态,因此您可以在对先前状态有充分了解的情况下选择新状态。

听起来可能很奇怪,但是 setState 可以在 react 中同步工作。怎么会这样?这是我创建的 POC 来演示它。

粘贴唯一的应用程序 JS 代码。

也许我遗漏了一些东西,但这实际上发生在我的应用程序中,那时我才知道这种效果。

如果我不知道 React 中会出现这种行为,请纠正我。当主线程上有多个 setState 时, setState 会运行一个 Batch 组合主方法上的所有 setState。而当相同的东西进入异步函数时,场景就不同了。

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0
    }
    this.asyncMethod = this.asyncMethod.bind(this);
    this.syncMethod = this.syncMethod.bind(this);
  }

  asyncMethod() {
    console.log("*************************")
    console.log("This is a async Method ..!!")
    this.setState({
      counter: this.state.counter + 1
    }, () => {
      console.log("This is a async Method callback of setState. value of counter is---", this.state.counter);
    })
    console.log("This is a async Method on main thread. value of counter is---", this.state.counter);
    console.log("*************************")
  }

  syncMethod() {
    var that = this;
    console.log("*************************")
    console.log("This is a sync Method ..!!")
    that.setState({counter: "This value will never be seen or printed and render will not be called"});
    that.setState({counter: "This is the value which will be seen in render and render will be called"});
    setTimeout(() => {
      that.setState({counter: "This is part is synchronous. Inside the async function after this render will be called"});
      console.log("setTimeout setState");
      that.setState({counter: "This is part is aslso synchronous. Inside the async function after this render will be called"});
    }, 10)
    console.log("This is a sync Method on Main thread. value of counter is---", this.state.counter);
    console.log("*************************")
  }

  render() {
    console.log("Render..!!",this.state.counter);
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
        </header>
          <button onClick={this.asyncMethod}>AsyncMethod</button>
          <button onClick={this.syncMethod}>SyncMethod</button>
      </div>
    );
  }
}

export default App;
欢迎使用 stackoverflow!很高兴看到您花时间回答这个问题。但是我必须指出,在文档中(以及在问题描述中)它说“不保证同步”,这意味着您可能正在经历同步状态集,但您不应该依赖它。因此,您提到的行为对问题没有帮助。
2021-05-05 05:29:53