理论上,React 状态和纯命令式程序中的状态有什么区别?
- 反应状态描述事物应该如何呈现。
- 命令式状态枚举从状态 A 到状态 B 所需的步骤(我猜你已经说过,当你说“命令式程序最重要的特征是状态及其修改”)。
在 React 中更新状态(使用this.setState)的全部目的是通知组件状态已更新,需要重新渲染。
但是,最终,如果您想在 React 中从状态 A 更改为状态 B,您可以在事件处理程序中执行此操作,如果您愿意,可以强制执行,但必须在不实际更改的情况下完成 初始状态。例如
// OK
let foo = this.state.foo;
foo = deriveSomeValue(foo);
this.setState({ foo });
// BAD
this.state.foo = deriveSomeValue(foo);
this.setState(this.state);
在第一个例子中(OK),当然,你可以看到某种程度的杂质; foo被修改了,但至少原来的状态没有。
在第二个例子(BAD)中,我们的杂质水平相同,但更糟;我们直接修改当前状态,这可能会产生不可预测的结果。我看到太多 Stack Overflow 帖子,有人问“为什么我用 this.state 执行 X 时渲染不正确???”换句话说,这种不变性范式是出于务实的原因而被强制执行的;也就是说,这就是 React 的设计方式,而任何其他方式都会产生未定义的行为(根据具体情况)。
另一个可能产生不可预知结果的例子:
const foo = this.state.foo;
foo.someProperty = getSomeValue();
this.setState({ foo });
在这种情况下,状态仍然会在通知 React 之前更新。 (这与对象是 explicitly referenced 的事实有关,而不是被隐式复制。)
当然,您可以在 React 中以命令式的方式在事件处理程序中从状态 A 转到状态 B,但您必须确保初始状态不会被直接修改。如果您绝对看到需要进行一些复杂的修改,那么我建议您对对象执行deep copy,然后以这种方式进行必要的修改。以下是完全没问题的:
const foo = cloneObject(this.state.foo);
// `cloneObject` is an entirely contrived function. You'll have to pick a
// deep copying library. Google around for "JavaScript deep copying"
foo.someProperty = getSomeValue();
this.setState({ foo });
或者,您可以使用不可变数据结构,这样如果您(在语义上可以称为)直接修改属性,它会产生一个全新的对象,而不必修改原始对象。一个好的库是(正如你提到的)ImmutableJS。
let foo = this.state.foo;
foo = foo.set('someProperty', getSomeValue());
this.setState({ foo });