繁体   English   中英

反应JS setState行为

[英]React JS setState behaviour

我正在学习本教程 ,并在1:10:18(链接中包含时间步长)上,他写了this.setState({ count: this.state.count + 1 }); (其中count是状态对象内的原始值,单击按钮后将显示在页面上)从导师的话中,我了解到我们需要在setState内部指定count:以便React将在状态对象中更新此值。 但是我注意到我可以在setState中写一些废话,例如:

  handleIncrement = key => {
    this.state.totalCount += 1;
    this.state.tags[key] += 1;
    this.setState({ countBlabla: 23213123 });
  };

而且HTML仍将正确更新,而无需重新呈现所有内容。 (我已经签入开发人员工具,只有已更改的值会更新,其他所有内容都保持不变)

我也尝试过: this.setState({}); 效果很好。 但是this.setState(); 才不是。

那么将setState传递给参数有什么意义呢?

您可以直接写入this.state唯一位置应该是Components构造函数。 在所有其他地方,您应该使用this.setState函数,该函数将接受一个Object,该Object最终将合并到Components当前状态。

尽管在技术上可以通过直接写入this.state来更改状态,但是这不会导致使用新数据重新渲染组件,并且通常会导致状态不一致。

另外, this.setState()是一个异步函数,因此,如果您只是在调用新状态后尝试console.log()新状态,就不会立即看到更改。 相反,您可以传递一个回调函数this.setState({var: newVar}, () => {console.log(this.state)})

遵循以下行react软件包的代码:

setState方法只调用enqueueSetState方法。 而这又使特定反应组件的更新入队。 更新是通过react-dom包(或react-native )处理的

如果您看一下forceUpdate方法,您会发现它实际上在做同样的事情(调用enqueueSetState )。 而且此方法仅触发对帐过程。

因此,简而言之,没有必要从纯触发前瞻性的状态中改变状态,也可以不改变状态。 但是从优化和清晰的数据流角度出发,这是非常必要的。 如果在使用setState时依赖state引用,则当某些组件看到旧状态时,您可能会遇到棘手的行为。

您只需要传递需要更新的值。 React会将当前状态传播到新状态。 如果您现在的状态为{ count: 1, prop: 2 }

// if you will make 
this.setState({ count: 11 })

// react will do
{ ...currentState, count: 11 }

// and result will be
{ prop: 2, count: 11 }

并通过调用对帐过程请求更新组件。 就是这样,不要忘记:

只是不要改变状态:)

PS 是Dan Abramov(React团队的核心成员)的精彩文章

通常,您永远不想直接改变状态。 始终使用setState

在回答你的问题,为什么我们需要指定键(在您的示例 ):因为这是我们要改变。 setState不仅会重新渲染组件,而且我们也不只是为了重新渲染而指定键。 它更改状态,然后重新渲染组件,以便可以使用状态中的新值。

从技术上讲,您可以forceUpdate状态并使用随机参数或对象调用forceUpdatesetState ,但您不应这样做,因为这会破坏React组件设计的自然工作流程。

如果将对象传递给setState,它将在内部合并该对象并导致重新渲染;如果未传递任何内容,它将跳过该重新渲染,这是您观察到的行为

现在让我们看一下上面代码的缺点

handleIncrement = key => {
    this.state.totalCount += 1;
    this.state.tags[key] += 1;
    this.setState({ countBlabla: 23213123 });
  };

在上述情况下,假设您将totalCount和标签作为道具传递给子组件,并基于要进行API调用的totalCount更改或标签更改在子组件中传递。 根据React流,您可以在componentDidUpdate生命周期方法中执行此操作

componentDidUpdate(prevProps) {
    if (prevProps.totalCount !== this.props.totalCount) {
        // make api cal;
    }
}

但是,尽管以上代码在totalCount为整数,布尔值或字符串的情况下都可以使用,因为它们是不可变的,但不适用于对象。 这样的情况将很难调试,并可能导致意外的行为。

您正在做的事情叫做巧合编程。

您的代码恰好由于react的内部工作而起作用,但是它是未记录的行为,将来可能会在没有警告的情况下停止工作。

建议的方法是使用this.setState({...})并传递对状态所做的所有更改的对象。 您的情况是:

  this.setState({ 
      totalCount: this.state.totalCount + 1 
      tags: {
           ...this.state.tags,
          [key]: this.state.tags[key] + 1 
       }  
  });

这将更新状态并获得与您的代码相同的副作用。

但是 ,将当前状态视为不可变是很重要的,因为例如,如果将来某个时候您决定实施shouldComponentUpdate方法并拒绝状态更改(如果手动更改了状态),由于先前的不一致,将会产生一些意想不到的后果陈述您的期望。

除其他答案外,如果要访问先前的状态,还可以将其作为参数传递给setState。

例如:

 this.setState((prevState) => ({
      totalCount: prevState.totalCount + 1 
      tags: {
           ...prevState.tags,
          [key]: prevState.tags[key] + 1 
       }  
  }));

建议这样做,因为此状态可能会异步更新。

更多信息: https//reactjs.org/docs/state-and-lifecycle.html

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM