【问题标题】:When to use functional setState何时使用函数式 setState
【发布时间】:2018-10-05 21:47:37
【问题描述】:

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

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

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

有人可以解释一下使用这两种方法的优点吗?以后想和代码保持一致,处理props和states等,所以当然首选最通用的方法。

【问题讨论】:

  • 这两段代码不等价 - 版本 1 将 state.pictures 设置为 pics,而版本 2 将 state.pictures 设置为之前的 state.pictures,并在末尾附加了 pics
  • @JoeClay,不,不是因为它不是回调,而是 setState 的更新函数
  • @ShubhamKhatri:哎呀,是的,你是对的。撤回了我的近距离投票。

标签: javascript html reactjs


【解决方案1】:

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

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() - Other APIs - React.Component – React.

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

Demo Codesandbox

【讨论】:

  • 用于演示。一个演示值一千字。
【解决方案2】:

TL;DR: 功能性setState 主要有助于在短时间内触发多个setStates 时预期正确的状态更新,而传统setState 进行协调并且可以导致意外状态。

请在Functional setState查看这篇精彩的文章以获得清晰的了解

这里是WORKING DEMO

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

假设 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)
}))

而在上面的 sn-p 中,this.state.pictures 将包含以前的图片+图片,即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 }));
}

【讨论】:

  • 但是如果我在 increaseScoreBy3 中只有一个 setState 调用,this.setState({score : this.state.score + 1}) 应该按预期工作吗?在这种情况下,我相信使用函数式回调不会产生任何影响。
  • 对,在这种情况下,您的假设是正确的。然而,与其每次都决定使用哪种形式的 setState,我们可以选择更不容易出错的函数式方法并消除这种决策疲劳。
  • 所以所有迹象都指向使用函数 setState 。我不想处理路上的虫子,谁呢?但是,要付出什么代价呢?我想批处理集状态有助于提高性能。关于何时使用一种与另一种,有什么好的规则可以遵循吗?
  • where functional callback shines because reconciliation does not happen on functions and executing the below code will do things as expected 你能解释一下吗? @南都
  • 我同意@CrhistianRamirez,有人知道两者的性能差异吗?
【解决方案3】:

快速回答:

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

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

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

另一种方法

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

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

【讨论】:

  • 始终使用 setState() 的函数形式。这是个坏建议。以不需要多次更新状态的方式编写更好的代码。
  • @AmitJS94 该建议是针对新手的。如果您知道自己在做什么,请使用其他通用形式。
猜你喜欢
  • 2020-04-13
  • 1970-01-01
  • 2020-02-10
  • 1970-01-01
  • 2020-01-10
  • 1970-01-01
相关资源
最近更新 更多