何时使用React setState回调


191

当反应组件状态改变时,将调用render方法。因此,对于任何状态更改,都可以在渲染方法主体中执行操作。那么setState回调是否有特定的用例?


4
目前尚不清楚您要问什么。您可以包含一些代码吗?
戴文·崔顿

2
setState回调适用于在状态已定义更改后想要执行的任何操作。由于setState是异步的,因此如果您要调用fx并确保已加载新状态,则这就是回调的目的
Jayce444

3
setState回调的用例非常清楚。当您希望功能在特定状态更新后运行时,可以使用它。如果render()改用此函数 ,则它将在每次更新ANY状态时运行,这可能不是您想要的。这也将使代码的可读性和逻辑性降低。
M3RS '18 -4-3

Answers:


222

是的,因为它setStateasynchronous某种程度上起作用。打完电话后这意味着setStatethis.state变量不会立即改变。因此,如果要在状态变量上设置状态后立即执行操作,然后返回结果,则回调将很有用

考虑下面的例子

....
changeTitle: function changeTitle (event) {
  this.setState({ title: event.target.value });
  this.validateTitle();
},
validateTitle: function validateTitle () {
  if (this.state.title.length === 0) {
    this.setState({ titleError: "Title can't be blank" });
  }
},
....

上面的代码可能无法按预期方式工作,因为title在对其执行验证之前,变量可能尚未发生突变。现在,您可能想知道我们可以在render()函数本身中执行验证,但是如果我们可以在changeTitle函数本身中进行处理,那将是更好和更简洁的方法,因为这会使您的代码更井井有条,更易于理解

在这种情况下,回调是有用的

....
changeTitle: function changeTitle (event) {
  this.setState({ title: event.target.value }, function() {
    this.validateTitle();
  });

},
validateTitle: function validateTitle () {
  if (this.state.title.length === 0) {
    this.setState({ titleError: "Title can't be blank" });
  }
},
....

另一个示例将是您要dispatch在状态更改时采取行动。您将希望在回调中执行此操作,而不是在render()每次进行重新渲染时都会调用它,因此您可能需要回调的情况很多。

另一种情况是 API Call

当您需要基于特定的状态更改进行API调用时,可能会出现这种情况,如果您在render方法中执行此操作,则将在每次渲染onState更改时调用该API,或者因为某些Prop传递给该Child Component更改而被调用。

在这种情况下,您可能需要使用setState callback将更新后的状态值传递给API调用

....
changeTitle: function (event) {
  this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
  // Call API with the updated value
}
....

3
我了解它本质上是异步的。我的问题是,有什么特定的东西只能用于setState回调,这可能是渲染方法主体可能不支持的(除了可以说更好的代码可读性。)
Sahil Jain

@SahilJain Validation是正确的示例,您将不希望在render()函数中处理它,因为每当您对render()进行任何更改时,它都将被调用,因此仅当输入发生更改时才希望调用它。在功能本身中
Shubham Khatri

React禁止在渲染期间更改状态。因此将验证放入回调的权利。
webdeb

if (this.title.length === 0) {应该是this.state.title.length吧?
德米特里·明科夫斯基

4
第一个用例可能不是一个好主意。setState回调在重新渲染后触发,因此您没有充分的理由导致两次渲染。这正是函数参数(更新程序)的目的。您可以运行setState(state => state.title.length ? { titleError: "Title can't be blank" } : null),更改将堆叠。无需双重渲染。
R Esmond

46
this.setState({
    name:'value' 
},() => {
    console.log(this.state.name);
});

14
感谢您提供此代码段,它可能会提供一些有限的即时帮助。一个适当的解释,将大大提高其长期的价值通过展示为什么这是一个很好的解决了这个问题,并会使其与其他类似的问题,更有助于未来的读者。请编辑您的答案以添加一些解释,包括您所做的假设。
Machavity

1
如果要在状态更改后调用函数,可以使用方法。
Araz Babayev '18

如果您想设置多个国家名称,名字等名字怎么办?
Sumanth Varada

44

我想到的1.用例是一个api调用,不应进入渲染,因为它将运行以进行each状态更改。而且,API调用仅应在特殊的状态更改上执行,而不应在每个渲染上执行。

changeSearchParams = (params) => {
  this.setState({ params }, this.performSearch)
} 

performSearch = () => {
  API.search(this.state.params, (result) => {
    this.setState({ result })
  });
}

因此,对于任何状态更改,都可以在渲染方法主体中执行操作。

非常糟糕的做法,因为render-method应该是纯净的,这意味着不应执行任何操作,状态更改,api调用,只需合并视图并返回即可。仅应在某些事件上执行操作。渲染不是事件,而是componentDidMount例如。


25

考虑setState调用

this.setState({ counter: this.state.counter + 1 })

理念

setState可以在异步函数中调用

所以你不能依靠this。如果上述调用是在异步函数内部进行的,this则将在该时间点引用组件的状态,但我们希望这是在setState调用或异步任务开始时引用状态内部的属性。并且由于任务是异步调用,因此该属性可能已及时更改。因此,使用this关键字来引用状态的某些属性是不可靠的,因此我们使用回调函数,其参数为previousState和props,这意味着当异步任务完成并且是时候使用setState进行状态更新时,setv调用时prevState现在将在setState时引用状态尚未开始。确保nextState不会被破坏的可靠性。

错误的代码:将导致数据损坏

this.setState(
   {counter:this.state.counter+1}
 );

setState具有回调功能的正确代码:

 this.setState(
       (prevState,props)=>{
           return {counter:prevState.counter+1};
        }
    );

因此,每当我们需要立即基于属性所具有的值将当前状态更新为下一个状态并且所有这些都以异步方式发生时,最好将setState用作回调函数。

我试图在Codepen中解释它 CODE PEN

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.