我正在跟踪this tutorial,然后在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参数有什么意义呢?
答案 0 :(得分:3)
只有可以直接写入this.state
的地方应该是Components构造函数。在所有其他地方,您应该使用this.setState
函数,该函数将接受一个Object,该Object最终将合并到Components当前状态。
从技术上说,可以通过直接写入this.state
来更改状态,但是这不会导致组件使用新数据重新呈现,并且通常会导致状态不一致。
此外,this.setState()
是一个异步函数,因此如果在调用新状态后尝试尝试console.log()
,则不会立即看到更改。相反,您可以传递回调函数this.setState({var: newVar}, () => {console.log(this.state)})
。
答案 1 :(得分:2)
react软件包的this行代码之后:
setState
方法仅调用enqueueSetState
方法。而这又使特定反应组件的更新入队。更新是通过react-dom
程序包(或react-native
)进行处理
如果您看一下forceUpdate
方法,您会发现它实际上在做同样的事情(调用enqueueSetState
)。并且此方法仅触发reconciliation进程。
因此,简而言之,不必从纯粹的触发重新呈现前瞻性来看是否必须处于 mutate 状态。但从optimization开始,并且明确的数据流前瞻性非常必要。如果在使用state
时依靠setState
引用,则当某些组件看到旧状态时,您可能会遇到棘手的行为。
您只需要传递需要更新的值。 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 }
并通过调用对帐过程请求更新组件。就是这样,不要忘记:
P.S。 Here是Dan Abramov(React团队的核心成员)的精彩文章
答案 2 :(得分:1)
通常,您永远不想直接改变状态。始终使用setState
。
您的问题的答案,为什么我们需要指定密钥(在您的示例中为 count ): 因为这就是我们想要改变的。 setState 不仅会重新渲染组件,而且我们也不会仅出于重新渲染的目的指定键。它会更改状态,然后重新渲染组件,以便可以使用状态中的新值。
答案 3 :(得分:1)
从技术上讲,您可以更改状态并使用随机参数或对象调用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为整数,布尔值或字符串的情况下可以工作,因为它们是不可变的,但不适用于对象。这样的情况很难调试,并且可能导致意外的行为。
答案 4 :(得分:1)
您正在做的事情叫做巧合编程。
您的代码只是由于react的内部工作而起作用,但是它是未记录的行为,并且将来可能会在没有警告的情况下停止工作。
建议的方法是使用this.setState({...})
并传递对状态所做的所有更改的对象。对于您而言,您可以这样做:
this.setState({
totalCount: this.state.totalCount + 1
tags: {
...this.state.tags,
[key]: this.state.tags[key] + 1
}
});
这将更新状态并获得与您的代码相同的副作用。
但是,将当前状态视为不变是非常重要的,因为例如,如果将来某个时候您决定实施shouldComponentUpdate
方法,并且在状态改变时拒绝状态改变如果是手动突变,则由于之前的状态与您期望的不一致,将会产生意想不到的后果。
答案 5 :(得分:0)
除了其他答案,如果要访问先前的状态,还可以将其作为参数传递给setState。
例如:
this.setState((prevState) => ({
totalCount: prevState.totalCount + 1
tags: {
...prevState.tags,
[key]: prevState.tags[key] + 1
}
}));
建议这样做,因为状态可能会异步更新。