状态管理
随着应用的增长,更有意识地组织状态以及组件间的数据流动会很有帮助。冗余或重复的状态是常见的错误来源。在本章中,你将学习如何良好地构建状态,如何保持状态更新逻辑的可维护性,以及如何在相距较远的组件之间共享状态。
通过状态响应输入
使用 React 时,你不会直接从代码中修改 UI。例如,你不会编写诸如“禁用按钮”、“启用按钮”、“显示成功消息”等命令。相反,你将描述组件在不同视觉状态下(“初始状态”、“输入状态”、“成功状态”)希望看到的 UI,然后响应用户输入触发状态变化。这与设计师思考 UI 的方式类似。
这是一个使用 React 构建的测验表单。请注意它如何使用status 状态变量来决定是启用还是禁用提交按钮,以及是否显示成功消息。
选择状态结构
良好的状态结构可以区分出一个易于修改和调试的组件与一个不断产生错误的组件。最重要的原则是状态不应包含冗余或重复的信息。如果存在不必要的状态,很容易忘记更新它,从而引入错误!
例如,这个表单有一个冗余的fullName状态变量:
你可以移除它,并通过在组件渲染时计算fullName 来简化代码:
这看起来可能是一个小改动,但 React 应用中的许多错误都是通过这种方式修复的。
组件间共享状态
有时,你希望两个组件的状态始终保持同步。为此,可以从两个组件中移除状态,将其移动到它们最近的共同父组件中,然后通过 props 向下传递。这被称为“状态提升”,是编写 React 代码时最常见的事情之一。
在这个例子中,一次应该只有一个面板处于活动状态。为了实现这一点,父组件持有状态并为其子组件指定 props,而不是将活动状态保存在每个单独的面板内部。
保留与重置状态
当你重新渲染一个组件时,React 需要决定树的哪些部分需要保留(和更新),哪些部分需要丢弃或从头重新创建。在大多数情况下,React 的自动行为已经足够好。默认情况下,React 会保留与先前渲染的组件树“匹配”的树的部分。
然而,有时这并不是你想要的。在这个聊天应用中,输入消息然后切换收件人并不会重置输入框。这可能导致用户不小心将消息发送给错误的人:
React 允许你覆盖默认行为,强制组件通过传递一个不同的 key来重置其状态,例如<Chat key={email} />。这告诉 React,如果收件人不同,它应该被视为一个不同的Chat组件,需要根据新数据(以及输入框等 UI)从头开始重新创建。现在,切换收件人就会重置输入字段——即使你渲染的是同一个组件。
将状态逻辑提取到 reducer 中
对于拥有许多状态更新,且这些更新分散在许多事件处理函数中的组件来说,管理起来可能会很繁琐。对于这种情况,你可以将所有状态更新逻辑整合到组件外部的一个单一函数中,这个函数被称为 “reducer”。事件处理函数会变得简洁,因为它们只需要指定用户的 “动作”。在文件的底部,reducer 函数会指定每个动作应如何更新状态!
使用 Context 深层传递数据
通常,你会通过 props 将信息从父组件传递到子组件。但是,如果你需要通过许多组件传递某个 prop,或者许多组件需要相同的信息,传递 props 可能会变得不方便。Context 允许父组件使其下方的任何组件(无论多深)都能获取某些信息,而无需通过 props 显式传递。
在这里,Heading 组件通过“询问”最近的 Section来获取其标题级别。每个Section 通过询问父级 Section并加一来跟踪自己的级别。每个Section都向它下方的所有组件提供信息,而无需传递 props——它通过 context 来实现这一点。
结合 Reducer 和 Context 进行扩展
Reducer 让你可以整合组件的状态更新逻辑。Context 让你可以将信息深层传递给其他组件。你可以将 reducer 和 context 结合起来管理复杂屏幕的状态。
通过这种方法,具有复杂状态的父组件使用 reducer 来管理它。树中任何深度的其他组件都可以通过 context 读取其状态。它们还可以 dispatch 操作来更新该状态。
接下来是什么?
前往 使用状态响应输入 开始逐页阅读本章!
或者,如果您已经熟悉这些主题,为什么不阅读一下逃生舱?
