何时使用函数式 setState

When to use functional setState

提问人:physicsboy 提问时间:1/11/2018 最后编辑:Shubham Khatriphysicsboy 更新时间:10/9/2020 访问量:29541

问:

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

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

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: []

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

我看到它以 2 种不同的方式编写:

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

this.setState({pictures: pics})

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

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

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

JavaScript 的HTML ReactJS

评论

0赞 Joe Clay 1/11/2018
这两段代码并不等效 - 版本 1 设置为 ,而版本 2 设置为前一段代码,并在末尾附加。state.picturespicsstate.picturesstate.picturespics
0赞 Shubham Khatri 1/11/2018
@JoeClay,不,它不是因为它不是回调,而是 setState 的更新程序函数
0赞 Joe Clay 1/11/2018
@ShubhamKhatri:哎呀,是的,你是对的。收回了我的接近投票。

答:

17赞 Nandu Kalidindi 1/11/2018 #1

TL;DR:功能主要有助于在短时间内触发多个 s 时期望正确的状态更新,而按照传统,协调并可能导致意外状态。setStatesetStatesetState

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

这是工作演示

首先,您尝试在每种情况下做不同的事情,您在一种情况下分配图片并在另一种情况下连接图片。

假设 this.state.pictures 包含和 pics 包含[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

人们出于不同的原因使用它们,现在据说该功能是安全的,因为执行了一种称为协调过程的东西,其中在频繁或同时触发时合并内部的多个对象,并在一次中应用更改,有点像批处理过程。在以下情况下,这可能会导致一些意外结果。setStateReactsetState

例:

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 }));
}

评论

0赞 darKnight 8/13/2018
但是,如果我在 increaseScoreBy3 中只有一个 setState 调用,是否应该按预期工作?在这种情况下,我相信使用函数回调不会有任何区别。this.setState({score : this.state.score + 1})
0赞 Nitin Jadhav 11/21/2018
是的,在这种情况下,你的假设是正确的。但是,与其每次都决定使用哪种形式的 setState,不如选择不容易出错的函数方法并消除这种决策疲劳。
0赞 Crhistian 12/19/2018
因此,所有迹象都表明一直在使用函数 setState。我不想处理路上的虫子,谁来处理?但是,要付出什么代价呢?我认为批处理设置状态有助于提高性能。关于何时使用一个与另一个,有什么好的规则可以遵循吗?
1赞 WitVault 7/14/2019
where functional callback shines because reconciliation does not happen on functions and executing the below code will do things as expected你能解释一下吗?@Nandu
0赞 Michael Harley 2/26/2020
我同意@CrhistianRamirez,有人知道两者的性能差异吗?
57赞 Shubham Khatri 1/11/2018 #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()

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

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

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

有关详细信息,请参阅:

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

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

演示 Codesandbox

评论

4赞 Henok Tesfaye 3/19/2019
用于演示。一个演示胜过千言万语。
4赞 Nitin Jadhav 11/21/2018 #3

快速回答:

始终使用 setState() 的函数形式。

this.setState((state, props)=>({
    someValue: state.somevalue + 1
}));

这种方法不易出错,有助于消除一些非常难以发现的错误。即使 react 批处理多个此类更新,每个更新都是单独的函数调用,后续更新不会导致其中一些更新丢失。

另一种方法

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

如果频繁重复,可能会导致一些更新丢失,因为 React 会批处理这种频繁的更新。

评论

2赞 AmitJS94 7/19/2019
始终使用 setState() 的函数形式。这是个坏建议。以这样一种方式编写更好的代码,这样您就不需要多次更新状态。
0赞 Nitin Jadhav 7/20/2019
@AmitJS94这个建议是给新手的。如果您知道自己在做什么,请使用其他通用形式。