[英]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
状态并使用随机参数或对象调用forceUpdate
或setState
,但您不应这样做,因为这会破坏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
}
}));
建议这样做,因为此状态可能会异步更新。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.