在Reactjs中是否存在setState()的同步替代

时间:2017-02-03 06:49:16

标签: javascript reactjs

根据docs中的解释:

  

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:1setState()提供的替代方案是什么。

Stackoverflow上几乎没有与此问题相似的问题,但没有我能找到正确答案的地方。

9 个答案:

答案 0 :(得分:49)

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

但是我假设你想在改变状态后执行一个动作,你可以通过以下方式实现:



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")
);
&#13;
<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>
&#13;
&#13;
&#13;

答案 1 :(得分:5)

您可以将setState包装在一个返回诺言的函数中,然后将该函数与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导致代码执行暂停,直到setStateSynchronous返回的承诺被解决为止,只有在调用传递给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/Reference/Operators/await

答案 2 :(得分:2)

不,没有。 React将在其认为合适时更新状态,执行诸如将setState调用一起批处理以提高效率。您可能感兴趣的是,您可以将一个函数传递给setState而不是之前的状态,因此您可以选择一个对前一个状态有充分了解的新状态。

答案 3 :(得分:2)

如果这是必需的,我建议在你的setState函数中使用回调(我还建议使用函数setState)。

在状态更新后将调用回调。

例如,您的示例是

public ActionResult Products_Read([DataSourceRequest] DataSourceRequest request)
{
    return Json(productService.Read().ToDataSourceResult(request));
}

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

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

答案 4 :(得分:0)

对你的问题的简短回答是 - 不,反应没有同步方法setState

答案 5 :(得分:0)

是的,有一种方法可以用来制作我们的同步setState。但它的表现可能并不像通常那样好 例如,我们有

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     data: 0
    };
  }

  changeState(){
   console.log('in change state',this.state.data);
   this.state.data = 'new value here'
   this.setState({});
   console.log('in change state after state change',this.state.data);
  }

  render() {
    return (
      <div>
      <p>{this.state.data}</p>
      <a onClick={this.changeState}>Change state</a>
    </div>
    );

  }
}  

在此示例中,我们首先更改状态,然后渲染我们的组件。

答案 6 :(得分:0)

这听起来很怪异,但是setState可以在react中同步工作。 为何如此?这是我创建来演示的POC。

粘贴唯一的应用JS代码。

也许我可能遗漏了一些东西,但这实际上是在我了解这种效果的应用程序中发生的。

更正我,如果我不知道在React中会发生这种行为。 当主线程上有多个setState时,setState将运行main上所有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;

答案 7 :(得分:0)

改为使用React Hooks:

function MyComponent() {
  const [cnt, setCnt] = useState(0)

  const updateState = () => {
    setCnt(cnt + 1)
  }

  useEffect(() => {
    console.log('new state', cnt)
  }, [cnt])

  return (
    <div>
      <button onClick={updateState}>Change state</button>
    </div>
  )
}

答案 8 :(得分:-1)

我能够通过将代码包装在setState中来欺骗React同步调用setTimeout(() => {......this.setState({ ... });....}, 0);。由于setTimeout将内容放在JavaScript事件队列的末尾,我认为React检测到setState在其中,并且知道它不能依赖于批量setState调用(这将被添加到队列的结尾)。