【问题标题】:When to use React setState callback何时使用 React setState 回调
【发布时间】:2017-06-21 16:06:57
【问题描述】:

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

【问题讨论】:

  • 目前还不清楚您在问什么。你能包含一些代码吗?
  • setState 回调适用于在状态已确定更改后您想要执行的任何操作。由于 setState 是异步的,如果你想调用一个 fx 并确保新状态已加载,那么这就是回调的用途
  • setState 回调的用例非常清楚。当您希望函数在特定状态更新后运行时,您可以使用它。如果你把这个函数放在render() 中,它会在每次更新任何状态时运行,这可能不是你想要的。这也会降低您的代码的可读性和逻辑性。

标签: reactjs callback setstate


【解决方案1】:

是的,因为setStateasynchronous 的方式工作。这意味着在调用 setState 之后,this.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 更改时调用,或者因为某些 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
}
....

【讨论】:

  • 我知道它本质上是异步的。我的问题是是否有一些特定的东西只能使用 setState 回调,可能渲染方法体可能不支持(除了让我们说更好的代码可读性。)
  • @SahilJain 验证是正确的例子,你不会想在 render() 函数中处理它,因为这样每次你在 render() 中进行任何更改时都会调用它仅当只有输入发生变化并因此在函数本身中发生变化时
  • React 禁止在渲染过程中更改状态。所以将验证放入回调中是正确的。
  • if (this.title.length === 0) { 应该是this.state.title.length,对吧?
  • 第一个用例可能不是一个好主意。 setState 回调在重新渲染后触发,因此您无缘无故地导致了双重渲染。这正是函数参数(更新程序)的目的。您只需运行setState(state => state.title.length ? { titleError: "Title can't be blank" } : null),更改就会叠加。无需双重渲染。
【解决方案2】:
this.setState({
    name:'value' 
},() => {
    console.log(this.state.name);
});

【讨论】:

  • 感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation would greatly improve its long-term value 通过展示为什么这是一个很好的解决问题的方法,并将使其对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
  • 当你想在状态改变后调用一个函数,你可以使用该方法。
  • 如果您想设置多个状态属性,例如姓名、名字等,该怎么办?
【解决方案3】:

我想到的 1. 用例是一个 api 调用,它不应该进入渲染,因为它会在 each 状态变化时运行。并且 API 调用应该只在特殊状态更改时执行,而不是在每个渲染时执行。

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

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

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

非常糟糕的做法,因为render-方法应该是纯的,这意味着不应该执行任何操作、状态更改、api 调用,只需复合您的视图并返回它。应仅对某些事件执行操作。渲染不是一个事件,而是componentDidMount 例如。

【讨论】:

    【解决方案4】:

    考虑 setState 调用

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

    想法

    setState 可以在异步函数中调用

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

    错误代码:会导致数据损坏

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

    带有回调函数的 setState 的正确代码:

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

    因此,每当我们需要根据属性刚刚拥有的值将当前状态更新到下一个状态并且所有这些都以异步方式发生时,使用 setState 作为回调函数是个好主意。

    我在这里尝试用 codepen 解释一下 CODE PEN

    【讨论】:

      【解决方案5】:

      有时我们需要一个代码块,我们需要在 setState 之后立即执行一些操作,以确保状态正在更新。这就是 setState 回调发挥作用的地方

      例如,有一个场景,我需要为 20 个客户中的 2 个客户启用一个模式,对于我们启用它的客户,有一组时间进行 API 调用,所以看起来像这样

      async componentDidMount() {
        const appConfig = getCustomerConfig();
        this.setState({enableModal: appConfig?.enableFeatures?.paymentModal }, async 
         ()=>{
           if(this.state.enableModal){
             //make some API call for data needed in poput
           }
        });
      }
      

      在渲染函数的 UI 块中也需要 enableModal 布尔值,这就是我在这里设置 setState 的原因,否则,可以只检查一次条件,然后调用 API 集或不调用。

      【讨论】:

        猜你喜欢
        • 2021-04-21
        • 2020-01-16
        • 1970-01-01
        • 2019-11-18
        • 2018-10-08
        • 1970-01-01
        • 2020-11-27
        • 2020-08-30
        • 2019-01-28
        相关资源
        最近更新 更多