何时使用函数式 setState

新手上路,请多包涵

在过去的几天里,我一直在学习 React,查看了一些关于编写不同元素的不同方式的教程和解释。然而,有一个我一直很好奇 - setState 更新/覆盖组件的 state 属性的函数。

例如,假设我有一个包含以下内容的类:

 class Photos extends React.Component {
    constructor() {
        super()
        state = {
            pictures: []
        }
    }

   componentDidMount() {
      // This is where the fetch and setState will occur (see below)
   }

    render() {
       return {
          <div className="container">
             {this.state.pictures}
          </div>
       }
    }
}

这个例子让我从 API 获取图像。

鉴于我已经执行了此函数的提取、映射和返回 - 然后我将使用 API 调用中获得的结果更新 pictures: [] 状态数组。

我的问题源于我所看到的关于如何更新/覆盖图片状态属性的不同方法。

我看到它以两种不同的方式写成:

1) 这似乎是一个非常简单易读的方法

this.setState({pictures: pics})

2) 这更复杂,但我看到它被描述为更安全的方法

this.setState(prevState => ({
   pictures: prevState.pictures.concat(pics)
}))

有人可以解释一下使用其中任何一个的优点吗?我想在未来与代码保持一致,处理道具和状态等,所以最通用的方法当然是首选。

原文由 physicsboy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 270
2 个回答

首先,在您的情况下,这两种语法完全不同,您可能要寻找的是两者之间的区别

this.setState({pictures: this.state.picture.concat(pics)})

this.setState(prevState => ({
   pictures: prevState.pictures.concat(pics)
}))

要理解为什么第二种方法是首选方法,您需要了解 React 在内部使用 setState() 做什么。

React 将首先将您传递给 setState() 的对象合并到当前状态。然后它将开始那个和解的事情。由于调用 setState() 可能不会立即更新您的状态。

React 可以将多个 setState() 调用成一个更新以获得更好的性能。

考虑简单的情况,为了理解这一点,在您的函数中您可能会多次调用 setState() ,例如:

 myFunction = () => {

   ...
   this.setState({pictures: this.state.picture.concat(pics1)})
   this.setState({pictures: this.state.picture.concat(pics1)})
   this.setState({pictures: this.state.picture.concat(pics1)})

   ...
}

这在一个简单的应用程序中不是一个有效的用例,但随着应用程序变得复杂,多个 setState() 调用可能从多个地方发生,做同样的事情。

所以现在要执行高效更新,React 通过提取传递给每个 setState() 调用的所有对象来执行批处理操作,将它们合并在一起形成一个对象,然后使用该对象执行 setState() 。根据 setState() 文档:

这种形式的 setState() 也是异步的,同一个周期内的多个调用可能会被batch在一起。例如,如果您尝试在同一个周期内多次增加一个项目数量,那将导致等价于:

 Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

后续调用将覆盖同一周期中先前调用的值,因此数量只会增加一次。如果下一个状态取决于当前状态,我们建议使用更新函数形式,而不是:

 this.setState((state) => {
  return {quantity: state.quantity + 1};
});

有关详细信息,请参阅:

setState() - 其他 API - React.Component – React

因此,如果任何对象包含相同的键,则存储具有相同键的最后一个对象的键值。因此更新只对最后一个值发生一次。

演示代码沙盒

原文由 Shubham Khatri 发布,翻译遵循 CC BY-SA 4.0 许可协议

TL;DR: 功能性 setState 主要有助于在短时间内触发多个 setState 时期望正确的状态更新,而常规 setState 并可能导致意想不到的状态。

请查看这篇关于 Functional setState 的 精彩文章以获得清晰的理解

这是 工作演示

首先,您尝试在每个案例中做不同的事情,您在一个案例中分配图片并在另一个案例中连接图片。

假设 this.state.pictures 包含 [1, 2, 3] 并且图片包含 [4, 5, 6]

this.setState({pictures: pics})

在上述情况下,this.state.pictures 将包含图片,即 this.state.pictures = [4, 5, 6]

 this.setState(prevState => ({
   pictures: prevState.pictures.concat(pics)
}))

而在上面的代码片段中,this.state.pictures 将包含以前的图片 + 图片即 this.state.pictures = [1, 2, 3, 4, 5, 6]

无论哪种方式,您都可以调整它以满足您的 API 需求,如果它像分页响应,您最好将每个请求的结果连接起来,否则如果您每次都获得整个数组,只需分配整个数组。

常规 setState VS 函数式 setState

现在,您的问题主要可能是将 setState 用于对象还是函数。

人们出于不同的原因使用它们,现在功能性的 setState 据说是安全的,因为 React 执行称为协调过程的操作,其中合并了多个对象 setState 34 --- 当频繁或同时触发并一次性应用更改时,有点像批处理过程。这可能会在如下场景中导致一些意想不到的结果。

例子:

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

正如您期望分数增加 3,但 React 所做的是合并所有对象并仅更新分数一次,即增加 1

这是函数回调的一种情况,因为协调不会在函数上发生,执行下面的代码将按预期执行操作,即更新分数 3

 increaseScoreBy3 () {
 this.setState(prevState => ({ score: prevState.score + 1 }));
 this.setState(prevState => ({ score: prevState.score + 1 }));
 this.setState(prevState => ({ score: prevState.score + 1 }));
}

原文由 Nandu Kalidindi 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题