何时使用功能setState

时间:2018-01-11 14:31:37

标签: javascript html reactjs

过去几天我一直在学习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)
}))

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

3 个答案:

答案 0 :(得分:18)

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

之间的区别
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()调用的所有对象来完成批处理,将它们合并在一起形成一个对象,然后使用该单个对象进行{{1 }}。根据setState文档

  

如果您尝试在项目中多次增加项目数量   相同的周期,将导致相当于:

setState()
     

后续调用将覆盖先前调用中的值   循环,所以数量只会递增一次。如果下一个   state取决于之前的状态,我们建议使用updater   功能形式,而不是

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

时发生一次

<强> Demo Codesandbox

答案 1 :(得分:5)

TL; DR:功能setState主要有助于在短暂的时间间隔内触发多个setState时期望正确的状态更新{{1}进行和解并可能导致意外状态。

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

这是WORKING DEMO

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

让我们说this.state.pictures包含setState,图片包含[1, 2, 3]

[4, 5, 6]

在上述情况下,this.state.pictures将包含照片,this.setState({pictures: pics})

this.state.pictures = [4, 5, 6]

在上面的代码片段中,this.state.pictures将包含之前的图片+ pics,this.setState(prevState => ({ pictures: prevState.pictures.concat(pics) }))

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

常规setState VS功能setState

现在,您的问题主要可能是将this.state.pictures = [1, 2, 3, 4, 5, 6]与对象或函数一起使用。

人们出于不同的原因同时使用它们,现在功能setState被认为是安全的,因为setState做了一个称为对帐过程的事情,其中​​合并了React内的多个对象频繁或同时触发并一次性应用更改,类似批处理。这可能会在以下情况下导致一些意外结果。

示例:

setState

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

这是函数回调闪耀的情况之一,因为函数上没有进行协调,执行下面的代码会按预期执行操作,即更新得分3

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

答案 2 :(得分:1)

快速解答:

始终使用setState()的功能形式。

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

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

另一种方法

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

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