You Probably Don't Need Derived State(也许,你并不需要派生 state)

目录

  1. 1. When to Use Derived State(什么时候使用派生状态)
  2. 2. Common Bugs When Using Derived State(使用派生状态常见的 Bugs)
    1. 2.1. Anti-pattern: Unconditionally copying props to state(反模式:无条件的使用 props 更新状态)
    2. 2.2. Anti-pattern: Erasing state when props change(反模式:当 props 变化时擦除 state)
  3. 3. preferred-solutions(首选解决方案)
    1. 3.1. Recommendation: Fully controlled component(建议:完全控制组件)
    2. 3.2. Recommendation: Fully uncontrolled component with a key(建议:带 key 的完全不受控的组件)
      1. 3.2.1. Alternative 1: Reset uncontrolled component with an ID prop(备选 1:使用 ID prop 重置不受控组件)
      2. 3.2.2. Alternative 2: Reset uncontrolled component with an instance method(备选 2:使用实例方法重置不受控组件)
    3. 3.3. Recap(概括)
  4. 4. What about memoization?(什么是 memoization)
  5. 5. In closing(结束语)

翻译自: React Docs: You Probably Don’t Need Derived State

React 16.4 included a bugfix for getDerivedStateFromProps which caused some existing bugs in React components to reproduce more consistently. If this release exposed a case where your application was using an anti-pattern and didn’t work properly after the fix, we’re sorry for the churn. In this post, we will explain some common anti-patterns with derived state and our preferred alternatives.

React 16.4 包含了 getDerivedStateFromProps 的错误修正,这导致了 React 组件中的一些现有错误更加一致地出现。如果你的应用程序使用了反模式,在 Bugs 修复后无法正常工作,我们很抱歉。在这篇文章中,我们将解释一些常见的使用了派生状态的反模式以及我们推荐的首选替代方案。

For a long time, the lifecycle componentWillReceiveProps was the only way to update state in response to a change in props without an additional render. In version 16.3, we introduced a replacement lifecycle, getDerivedStateFromProps to solve the same use cases in a safer way. At the same time, we’ve realized that people have many misconceptions about how to use both methods, and we’ve found anti-patterns that result in subtle and confusing bugs. The getDerivedStateFromProps bugfix in 16.4 makes derived state more predictable, so the results of misusing it are easier to notice.

长期以来, componentWillReceiveProps 生命周期方法是唯一能够根据 props 的改变更新 state,且没有额外渲染过程的方法。在版本 16.3 中,我们引入了替换的生命周期 getDerivedStateFromProps 以更安全的方式解决这样的用例。与此同时,我们意识到人们对如何使用这两种方法存在许多误解,并且我们发现反模式会导致细微而混乱的错误。在 16.4 中的 Bug 修正 使派生状态更加可预测 ,这也使得滥用它的结果更容易被注意到。

Note

All of the anti-patterns described in this post apply to both the older componentWillReceiveProps and the newer getDerivedStateFromProps.

本文中描述的所有反模式都适用于旧的 componentWillReceiveProps 和新的getDerivedStateFromProps 方法。

This blog post will cover the following topics:

本文主要包含以下主题:

  • When to use derived state(什么时候使用派生状态)
  • Common bugs when using derived state(使用派生状态常见的 Bugs)
    • Anti-pattern: Unconditionally copying props to state(反模式:当 props 变化时擦除 state)
    • Anti-pattern: Erasing state when props change(反模式:当 props 变化时擦除 state)
  • Preferred solutions(首选解决方案)
  • What about memoization?(什么是 memoization)

When to Use Derived State(什么时候使用派生状态)

getDerivedStateFromProps exists for only one purpose. It enables a component to update its internal state as the result of changes in props. Our previous blog post provided some examples, like recording the current scroll direction based on a changing offset prop or loading external data specified by a source prop.

getDerivedStateFromProps 仅被设计用于一个目的:组件能够根据 props 的改变 更新其内部维护的 state。我们之前的博客文章提供了一些示例,例如 根据传入的 offset props 的变化更新滚动的方向根据 props 指定的数据源加载外部数据

We did not provide many examples, because as a general rule, derived state should be used sparingly. All problems with derived state that we have seen can be ultimately reduced to either (1) unconditionally updating state from props or (2) updating state whenever props and state don’t match. (We’ll go over both in more detail below.)

我们没有提供很多示例,因为一般来说,派生状态应该被谨慎的使用。我们所看到的派生状态的所有问题最终可以简化为(1)无条件地从根据 props 更新 state 或(2)即使 props 变化和 state 不匹配也更新状态。(我们将在下面详细介绍这两点。)

  • If you’re using derived state to memoize some computation based only on the current props, you don’t need derived state. See What about memoization? below.
  • 如果你使用的派生状态仅需要根据当前 props 记录计算结果,则不需要派生状态。请参阅后面的 What about memoization? 一节。
  • If you’re updating derived state unconditionally or updating it whenever props and state don’t match, your component likely resets its state too frequently. Read on for more details.
  • 如果你无条件地更新派生状态不管 props 和 state 是否匹配,则组件可能会过于频繁地重置其状态。继续往下阅读以了解更多。

Common Bugs When Using Derived State(使用派生状态常见的 Bugs)

The terms “controlled” and “uncontrolled” usually refer to form inputs, but they can also describe where any component’s data lives. Data passed in as props can be thought of as controlled (because the parent component controls that data). Data that exists only in internal state can be thought of as uncontrolled (because the parent can’t directly change it).

术语 “受控”“不受控制” 通常用于表单输入的元素,但它们也可以被用来描述组件的数据存在位置。作为 props 被传入组件的数据可以被认为是受控的(因为父组件控制该数据)。仅存在于组件内部 state 中的数据可以被认为是不受控制的(因为父级不能直接更改它)。

The most common mistake with derived state is mixing these two; when a derived state value is also updated by setState calls, there isn’t a single source of truth for the data. The external data loading example mentioned above may sound similar, but it’s different in a few important ways. In the loading example, there is a clear source of truth for both the “source” prop and the “loading” state. When the source prop changes, the loading state should always be overridden. Conversely, the state is overridden only when the prop changes and is otherwise managed by the component.

派生 state 最常见的错误是混合这两个点,即派生 state 的值也会被 setState 的调用更新,即数据没有单一的事实来源。上面提到的 外部数据加载示例 看起来与之很相似,但是有几个很重要的不同点。在 “外部数据加载示例 ” 的示例中,“source” props 和 “loading” state 这两个字段都有明确的来源。当 “source” props 改变时,“loading” state 始终应被复写。而且,“loading” state 只有在 “source” props 改变时才被修改,而不是由组件管理 “loading” state 的变化。

Problems arise when any of these constraints are changed. This typically comes in two forms. Let’s take a look at both.

当上面提到的约束被改变时,就会出现问题。通常有两种形式的错误。我们来看看。

Anti-pattern: Unconditionally copying props to state(反模式:无条件的使用 props 更新状态)

A common misconception is that getDerivedStateFromProps and componentWillReceiveProps are only called when props “change”. These lifecycles are called any time a parent component rerenders, regardless of whether the props are “different” from before. Because of this, it has always been unsafe to unconditionally override state using either of these lifecycles. Doing so will cause state updates to be lost.

一个常见的误解是,仅当 props “更改” 时,才会调用 getDerivedStateFromPropscomponentWillReceiveProps。实际上,这两个生命周期方法都会在父组件重新渲染时被调用,无论 props 是否和之前不同。因此,始终在这两个生命周期方法中无条件地覆盖 state 是不安全的。这样做会导致状态更新丢失

Let’s consider an example to demonstrate the problem. Here is a EmailInput component that “mirrors” an email prop in state:

让我们看一个例子来证明这个问题。这里有一个 EmailInput 组件,用于“克隆” email props 到 state 中:

class EmailInput extends Component {
state = { email: this.props.email };

render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}

handleChange = event => {
this.setState({ email: event.target.value });
};

componentWillReceiveProps(nextProps) {
// This will erase any local state updates!
// Do not do this.
// 这会擦除本地修改的 state ,不要这样做
this.setState({ email: nextProps.email });
}
}

At first, this component might look okay. State is initialized to the value specified by props and updated when we type into the <input>. But if our component’s parent rerenders, anything we’ve typed into the <input> will be lost! (See this demo for an example.) This holds true even if we were to compare nextProps.email !== this.state.email before resetting.

最开始,这个组件可能看起来没问题。State 被初始化为 props 指定的值,并当我们在 <input> 中输入内容时改变。但是如果组件的父级重新渲染,那么我们输入到 <input> 中的任何内容都将丢失!(查看此演示)即使我们在设置 state 之前比较了 nextProps.email !== this.state.email 也是如此。

In this simple example, adding shouldComponentUpdate to rerender only when the email prop has changed could fix this. However in practice, components usually accept multiple props; another prop changing would still cause a rerender and improper reset. Function and object props are also often created inline, making it hard to implement a shouldComponentUpdate that reliably returns true only when a material change has happened. Here is a demo that shows that happening. As a result, shouldComponentUpdate is best used as a performance optimization, not to ensure correctness of derived state.

在这个例子中,添加 shouldComponentUpdate 在 email props 改变时才重新渲染可以解决上面提到的问题。然而实际上,组件通常接受多个 props ,另一个 prop 的改变仍然会导致组件的重新渲染和不正确的重置 state。函数和对象的 props 也经常被内联的创建,这使得实现一个只有发生 props 更改时才能可靠地返回 true 的 shouldComponentUpdate 很难。这个演示展示了 state 更新被 props 的改变重置。因此, shouldComponentUpdate 最好用来进行性能优化,而不是确保派生 state 的正确性。

Hopefully it’s clear by now why it is a bad idea to unconditionally copy props to state. Before reviewing possible solutions, let’s look at a related problematic pattern: what if we were to only update the state when the email prop changes?

希望你现在很清楚为什么无条件地将 props 复制到 state 是一个坏主意。在回顾可能的解决方案之前,我们来看看一个相关的问题模式:如果我们只是在 email prop 更改时更新 state 会怎样?

Anti-pattern: Erasing state when props change(反模式:当 props 变化时擦除 state)

Continuing the example above, we could avoid accidentally erasing state by only updating it when props.email changes:

继续上面的例子,我们可以通过仅在 props.email 更变化时更新它,避免意外地删除 state:

class EmailInput extends Component {
state = {
email: this.props.email
};

componentWillReceiveProps(nextProps) {
// Any time props.email changes, update state.
if (nextProps.email !== this.props.email) {
this.setState({
email: nextProps.email
});
}
}

// ...
}

Note

Even though the example above shows componentWillReceiveProps, the same anti-pattern applies to getDerivedStateFromProps.

尽管上面的示例使用了 componentWillReceiveProps,但相同的反模式也适用于getDerivedStateFromProps

We’ve just made a big improvement. Now our component will erase what we’ve typed only when the props actually change.

我们刚刚做了很大的改进。现在我们的组件将只有在 props 实际发生改变时才会删除我们输入的内容。

There is still a subtle problem. Imagine a password manager app using the above input component. When navigating between details for two accounts with the same email, the input would fail to reset. This is because the prop value passed to the component would be the same for both accounts! This would be a surprise to the user, as an unsaved change to one account would appear to affect other accounts that happened to share the same email. (See demo here.)

还有一个小问题。想象一个使用上述输入组件的密码管理器。当在使用了同一个邮件的两个帐户的详细信息之间导航时,输入的内容将无法被重置。这是因为传递给组件的 props 对于两个帐户都是相同的!这对用户来说很惊奇,因为对一个帐户的未保存更改似乎会影响碰巧使用同一电子邮件的其他帐户(查看这里的演示)。

This design is fundamentally flawed, but it’s also an easy mistake to make. (I’ve made it myself!) Fortunately there are two alternatives that work better. The key to both is that for any piece of data, you need to pick a single component that owns it as the source of truth, and avoid duplicating it in other components. Let’s take a look at each of the alternatives.

这种设计存在根本缺陷,但这也是一个容易犯的错误(我自己也犯错了)。幸运的是,有两种选择更好。它们的关键是,对于任何数据,你需要选择一个组件作为数据的事实来源,并避免在其他组件中重复修改它。我们来看看每个替代方案。

preferred-solutions(首选解决方案)

Recommendation: Fully controlled component(建议:完全控制组件)

One way to avoid the problems mentioned above is to remove state from our component entirely. If the email address only exists as a prop, then we don’t have to worry about conflicts with state. We could even convert EmailInput to a lighter-weight function component:

避免上述问题的一种方法是完全从组件中删除状态。如果 email 仅做为 props 存在,那么我们不必担心与 state 的冲突。我们甚至可以将 EmailInput 转换为更轻量级的函数式组件:

function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}

This approach simplifies the implementation of our component, but if we still want to store a draft value, the parent form component will now need to do that manually. (Click here to see a demo of this pattern.)

这种方法简化了组件的实现,但如果我们仍想存储中间编辑的草稿值,则父表单组件现在需要手动执行(点击此处查看此模式的演示)。

Recommendation: Fully uncontrolled component with a key(建议:带 key 的完全不受控的组件)

Another alternative would be for our component to fully own the “draft” email state. In that case, our component could still accept a prop for the initial value, but it would ignore subsequent changes to that prop:

另一种选择是我们的组件完全拥有 email state 的 “草稿” 值。在这种情况下,我们的组件仍然可以接受一个 初始值 的 prop,但它会忽略该 prop 的后续变化:

class EmailInput extends Component {
state = { email: this.props.defaultEmail };

handleChange = event => {
this.setState({ email: event.target.value });
};

render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}

In order to reset the value when moving to a different item (as in our password manager scenario), we can use the special React attribute called key. When a key changes, React will create a new component instance rather than update the current one. Keys are usually used for dynamic lists but are also useful here. In our case, we could use the user ID to recreate the email input any time a new user is selected:

为了在移动到不同的项目时重置值(如在我们的密码管理器场景中),我们可以使用一个名为 key 的特殊 React 属性。当 key 更改时,React 将 创建一个新的组件实例,而不是 更新 当前的组件实例。Keys 通常被用于动态列表,但在此处也很有用。在我们的例子中,我们可以在选择新用户时使用用户 ID 重新创建EmailInput

<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>

Each time the ID changes, the EmailInput will be recreated and its state will be reset to the latest defaultEmail value. (Click here to see a demo of this pattern.) With this approach, you don’t have to add key to every input. It might make more sense to put a key on the whole form instead. Every time the key changes, all components within the form will be recreated with a freshly initialized state.

每次 ID 更改时,都会重新创建 EmailInput,并将其状态重置为最新的 defaultEmail值(单击此处查看此模式的演示)。使用此方法,你不必为每个输入添加 key。将 key 放在整个表单上可能更有意义。每次 key 更改时,表单中的所有组件都将以新初始化状态重新创建。

In most cases, this is the best way to handle state that needs to be reset.

在大多数情况下,这是处理需要重置的状态的最佳方法。

Note

While this may sound slow, the performance difference is usually insignificant. Using a key can even be faster if the components have heavy logic that runs on updates since diffing gets bypassed for that subtree.

虽然这可能听起来很慢,但性能差异通常是微不足道的。如果组件具有繁重的更新逻辑,则使用 key 甚至可以更快,因为该子树可能会绕过了差异比较。

Alternative 1: Reset uncontrolled component with an ID prop(备选 1:使用 ID prop 重置不受控组件)

If key doesn’t work for some reason (perhaps the component is very expensive to initialize), a workable but cumbersome solution would be to watch for changes to “userID” in getDerivedStateFromProps:

如果 key 由于某种原因不起作用(可能组件的初始化代价非常昂贵),一个可行但很麻烦的解决方案是在 getDerivedStateFromProps 中监视 userID 的更改:

class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};

static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}

// ...
}

This also provides the flexibility to only reset parts of our component’s internal state if we so choose. (Click here to see a demo of this pattern.)

如果愿意,这还可以灵活地仅重置组件内部状态的一部分(点击此处查看此模式的演示)。

Note

Even though the example above shows getDerivedStateFromProps, the same technique can be used with componentWillReceiveProps.

尽管上面的示例使用了 getDerivedStateFromProps作为演示,但是 componentWillReceiveProps 也可以使用同样的模式。

Alternative 2: Reset uncontrolled component with an instance method(备选 2:使用实例方法重置不受控组件)

More rarely, you may need to reset state even if there’s no appropriate ID to use as key. One solution is to reset the key to a random value or autoincrementing number each time you want to reset. One other viable alternative is to expose an instance method to imperatively reset the internal state:

更少见的是,即使没有适当的 ID 用作key,你也可以重置状态。一种解决方案是每次要重置时将key重置为随机值或自动递增数字。另一个可行的替代方法是使用实例方法以强制重置内部状态:

class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};

resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}

// ...
}

The parent form component could then use a ref to call this method. (Click here to see a demo of this pattern.)

然后,父表单组件可以使用ref 来调用此方法点击此处查看此模式的演示)。

Refs can be useful in certain cases like this one, but generally we recommend you use them sparingly. Even in the demo, this imperative method is nonideal because two renders will occur instead of one.

Refs 在某些情况下非常有用,但通常我们建议你谨慎使用它们。即使在演示中,这种命令式方法也是不理想的,因为会出现两次渲染而不是一次。

Recap(概括)

To recap, when designing a component, it is important to decide whether its data will be controlled or uncontrolled.

总而言之,在设计组件时,确定其数据是受控还是不受控是很重要的。

Instead of trying to “mirror” a prop value in state, make the component controlled, and consolidate the two diverging values in the state of some parent component. For example, rather than a child accepting a “committed” props.value and tracking a “draft” state.value, have the parent manage both state.draftValue and state.committedValue and control the child’s value directly. This makes the data flow more explicit and predictable.

与试图 “拷贝” prop 的值到 state相比,让组件变成受控组件是更好地想法,并在某个父组件的 state 中合并两个不同的值。例如,不让子孙组件接受 “已提交”状态的 props.value 同时跟踪“草稿状态的” state.value,而是让父组件管理 state.draftValuestate.committedValue 并直接控制其子孙组件的值。这会使数据流更加明确和可预测。

For uncontrolled components, if you’re trying to reset state when a particular prop (usually an ID) changes, you have a few options:

对于不受控的组件,如果你需要再特定 props(通常是 ID)发生变化时重置其状态,则可以选择以下几种方法:

  • Recomendation: To reset all internal state, use the key attribute.
  • 推荐:要重置所有内部状态,请使用 key 属性。
  • Alternative 1: To reset only certain state fields, watch for changes in a special property (e.g. props.userID).
  • 备选 1:要仅重置某些状态字段,请注意特殊属性(例如 props.userID )中的变化。
  • Alternative 2: You can also consider fall back to an imperative instance method using refs.
  • 备选 2:你还可以考虑使用 refs 回退到调用命令式的实例方法。

What about memoization?(什么是 memoization)

We’ve also seen derived state used to ensure an expensive value used in render is recomputed only when the inputs change. This technique is known as memoization.

我们还看到派生 state 用于确保在输入改变时重新计算渲染中使用的昂贵值。这种技术称为memoization

Using derived state for memoization isn’t necessarily bad, but it’s usually not the best solution. There is inherent complexity in managing derived state, and this complexity increases with each additional property. For example, if we add a second derived field to our component state then our implementation would need to separately track changes to both.

使用派生的 state 进行 memoization 并不一定是坏事,但它通常不是最佳解决方案。管理派生 state 存在固有的复杂性,并且这种复杂性随着每个附加属性而增加。例如,如果我们向组件的 state 添加第二个派生字段,那么我们需要单独跟踪对两者的更改。

Let’s look at an example of one component that takes one prop—a list of items—and renders the items that match a search query entered by the user. We could use derived state to store the filtered list:

让我们看一个组件的示例,该组件接收一个 prop ,一个项目列表 ,并呈现与用户输入的搜索查询匹配的项目。我们可以使用派生 state 来存储过滤后的列表:

class Example extends Component {
state = {
filterText: "",
};

// *******************************************************
// NOTE: this example is NOT the recommended approach.
// See the examples below for our recommendations instead.
// 注意,这个示例是不推荐的方法,下一个示例是我们推荐使用方法
// *******************************************************

static getDerivedStateFromProps(props, state) {
// Re-run the filter whenever the list array or filter text change.
// 每当 list 数组和过滤文本改变时都需要重新运行过滤器
// Note we need to store prevPropsList and prevFilterText to detect changes.
// 注意到我们需要存储之前的 prevPropsList 和 prevFilterText 用于探测改变
if (
props.list !== state.prevPropsList ||
state.prevFilterText !== state.filterText
) {
return {
prevPropsList: props.list,
prevFilterText: state.filterText,
filteredList: props.list.filter(item => item.text.includes(state.filterText))
};
}
return null;
}

handleChange = event => {
this.setState({ filterText: event.target.value });
};

render() {
return (
<Fragment>
<input onChange={this.handleChange} value={this.state.filterText} />
<ul>{this.state.filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
</Fragment>
);
}
}

This implementation avoids recalculating filteredList more often than necessary. But it is more complicated than it needs to be, because it has to separately track and detect changes in both props and state in order to properly update the filtered list. In this example, we could simplify things by using PureComponent and moving the filter operation into the render method:

这种实现需要更频繁地重新计算过滤列表。它比我们的需求更复杂,因为它必须单独跟踪和检测 props 和 state 的变化,以便正确更新过滤后的列表。在这个例子中,我们可以通过使用 PureComponent 并将过滤器操作移动到 render 方法来简化操作:

// PureComponents only rerender if at least one state or prop value changes.
// PureComponents仅在至少一个 state 或道 prop 值发生变化时才会重新渲染。
// Change is determined by doing a shallow comparison of state and prop keys.
// 通过对 state 和 prop 进行浅层比较来确定更改。
//
class Example extends PureComponent {
// State only needs to hold the current filter text value:
state = {
filterText: ""
};

handleChange = event => {
this.setState({ filterText: event.target.value });
};

render() {
// The render method on this PureComponent is called only if
// props.list or state.filterText has changed.
// PureComponent 的渲染方法只会在 props.list 或 state.filterText 改变时被调用
const filteredList = this.props.list.filter(
item => item.text.includes(this.state.filterText)
)

return (
<Fragment>
<input onChange={this.handleChange} value={this.state.filterText} />
<ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
</Fragment>
);
}
}

The above approach is much cleaner and simpler than the derived state version. Occasionally, this won’t be good enough—filtering may be slow for large lists, and PureComponent won’t prevent rerenders if another prop were to change. To address both of these concerns, we could add a memoization helper to avoid unnecessarily re-filtering our list:

上述方法比派生的 state 版本的代码更清晰,更简单。这仍然不够好——对于大型列表来说可能会很慢,如果另一个 prop 发生变化,PureComponent 也不会阻止重新渲染。为了解决这两个问题,我们可以添加一个 memoization 工具,以避免不必要地重复运行过滤列表的操作:

import memoize from "memoize-one";

class Example extends Component {
// State only needs to hold the current filter text value:
state = { filterText: "" };

// Re-run the filter whenever the list array or filter text changes:
filter = memoize(
(list, filterText) => list.filter(item => item.text.includes(filterText))
);

handleChange = event => {
this.setState({ filterText: event.target.value });
};

render() {
// Calculate the latest filtered list. If these arguments haven't changed
// since the last render, `memoize-one` will reuse the last return value.
const filteredList = this.filter(this.props.list, this.state.filterText);

return (
<Fragment>
<input onChange={this.handleChange} value={this.state.filterText} />
<ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
</Fragment>
);
}
}

This is much simpler and performs just as well as the derived state version!

这更简单,并且与派生的 state 版本的代码一样好!

When using memoization, remember a couple of constraints:

使用 memoization 时,请记住几个约束:

  1. In most cases, you’ll want to attach the memoized function to a component instance. This prevents multiple instances of a component from resetting each other’s memoized keys. 在大多数情况下,你需要将 memoized 函数附加到组件实例。这可以防止组件的多个实例彼此重置影响其他实例。
  2. Typically you’ll want to use a memoization helper with a limited cache size in order to prevent memory leaks over time. (In the example above, we used memoize-one because it only caches the most recent arguments and result.) 通常,你需要使用有限制的缓存 的 memoization 工具,以防止内存泄漏。(在上面的例子中,我们使用了 memoize-one ,因为它只缓存了最新的参数和结果。)
  3. None of the implementations shown in this section will work if props.list is recreated each time the parent component renders. But in most cases, this setup is appropriate. 如果每次父组件渲染时都重新创建 props.list,则本节中的任何实现都不起作用。但在大多数情况下,这种设置是合适的。

In closing(结束语)

In real world applications, components often contain a mix of controlled and uncontrolled behaviors. This is okay! If each value has a clear source of truth, you can avoid the anti-patterns mentioned above.

在实际应用中,组件通常包含受控和不受控的行为。这没关系!如果每个值都有明确的真正来源,你可以避免上面提到的反模式。

It is also worth re-iterating that getDerivedStateFromProps (and derived state in general) is an advanced feature and should be used sparingly because of this complexity. If your use case falls outside of these patterns, please share it with us on GitHub or Twitter!

值得重新考虑的是,getDerivedStateFromProps(以及一般的派生状态)是一种高级功能,因为其复杂性应该谨慎使用。如果你的用例不在这些模式范围之内,请在 GitHubTwitter 上与我们分享!

知识共享许可协议 知识共享许可协议 知识共享许可协议 本网站原创内容(非转载文章)采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。