何时使用函数式 setState

IT技术 javascript html reactjs
2021-01-20 02:49:15

过去几天我一直在学习 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 获取图像。

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

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

我已经看到它以两种不同的方式编写:

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

this.setState({pictures: pics})

2) 这更复杂,但我认为它是一种更安全的方法

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

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

3个回答

首先,在您的情况下,这两种语法完全不同,您可能正在寻找的是

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

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

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

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

setState()为了更好的性能,React 可以将多个调用批处理为单个更新。

考虑一个简单的情况,要理解这一点,在您的函数中您可能会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},
  ...
)

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

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

有关更多详细信息,请参阅:

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

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

演示代码沙盒

用于演示。一个value一千字的演示。
2021-03-12 02:49:15

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

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

这是工作演示

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

假设 this.state.pictures 包含[1, 2, 3]和 pics 包含[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 将包含以前的图片 + 图片 ie this.state.pictures = [1, 2, 3, 4, 5, 6]

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

常规 setState VS 函数式 setState

现在,您的问题主要可能是setState与对象一起使用还是与函数一起使用。

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

例子:

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 }));
}
对,在那种情况下,你的假设是正确的。然而,与其每次都决定使用哪种形式的 setState,我们可以选择不太容易出错的函数方法并消除这种决策疲劳。
2021-03-12 02:49:15
我同意 @CrhistianRamirez ,有人知道两者之间的性能差异吗?
2021-03-15 02:49:15
where functional callback shines because reconciliation does not happen on functions and executing the below code will do things as expected你能解释更多吗?@南都
2021-03-26 02:49:15
所以所有迹象都表明一直在使用函数 setState。我不想处理路上的虫子,谁来处理?但是,代价是什么呢?我想批处理集状态有助于提高性能。关于何时使用一个和另一个有什么好的规则可以遵循吗?
2021-03-31 02:49:15
但是如果我在increaseScoreBy3中只有一个setState调用,this.setState({score : this.state.score + 1})应该按预期工作吗?在这种情况下,我相信使用函数式回调不会有任何区别。
2021-04-09 02:49:15

快速回答:

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

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

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

另一种方法

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

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

@AmitJS94 该建议适用于新手。如果您知道自己在做什么,请使用其他通用形式。
2021-03-13 02:49:15
始终使用 setState() 的函数形式。这是一个糟糕的建议。以不需要多次更新状态的方式编写更好的代码。
2021-03-14 02:49:15