# React Step8

https://zh-hans.react.dev/learn/reacting-to-input-with-state


在组件间共享状态: 就是状态提升

对于每个独特的状态，都应该存在且只存在于一个指定的组件中作为 state。这一原则也被称为拥有 “可信单一数据源”。

只要一个组件还被渲染在 UI 树的相同位置，React 就会保留它的 state。 如果它被移除，或者一个不同的组件被渲染在相同的位置，那么 React 就会丢掉它的 state。

记住 **对 React 来说重要的是组件在 UI 树中的位置**,而不是在 JSX 中的位置！

:::tip
核心就是组件在树的位置已经是不是同一个组件，来决定是否需要重置state
:::


永远要将组件定义在最上层并且不要把它们的定义嵌套起来.否则会导致state丢失，因为嵌套起来,每次处罚,都是新的，自然会丢失

你可能在 渲染列表 时见到过 key。但 key 不只可以用于列表！你可以使用 key 来让 React 区分任何组件。默认情况下，React 使用父组件内部的顺序来区分组件。
```js
    {isPlayerA ? (
    <Counter key="Taylor" person="Taylor" />
    ) : (
    <Counter key="Sarah" person="Sarah" />
    )}
    // 请记住 key 不是全局唯一的。它们只能指定 父组件内部 的顺序。
```

使用 key 来重置 state 在处理表单时特别有用。

#### 为被移除的组件保留 state
- 与其只渲染现在这一个聊天，你可以把 所有 聊天都渲染出来，但用 CSS 把其他聊天隐藏起来。这些聊天就不会从树中被移除了，所以它们的内部 state 会被保留下来。这种解决方法对于简单 UI 非常有效。但如果要隐藏的树形结构很大且包含了大量的 DOM 节点，那么性能就会变得很差。
- 你可以进行 状态提升 并在父组件中保存每个收件人的草稿消息。这样即使子组件被移除了也无所谓，因为保留重要信息的是父组件。这是最常见的解决方法。
- 除了 React 的 state，你也可以使用其他数据源。例如，也许你希望即使用户不小心关闭页面也可以保存一份信息草稿。要实现这一点，你可以让 Chat 组件通过读取 localStorage 对其 state 进行初始化，并把草稿保存在那里。

### 迁移状态逻辑至 Reducer 中
对于拥有许多状态更新逻辑的组件来说，过于分散的事件处理程序可能会令人不知所措。对于这种情况，你可以将组件的所有状态更新逻辑整合到一个外部函数中，这个函数叫作 reducer。

Reducer 是处理状态的另一种方式。你可以通过三个步骤将 useState 迁移到 useReducer：
1. 将设置状态的逻辑 修改 成 dispatch 的一个 action；
2. 编写 一个 reducer 函数；
3. 在你的组件中 使用 reducer。


使用 Immer 简化 reducer: https://zh-hans.react.dev/learn/extracting-state-logic-into-a-reducer#writing-concise-reducers-with-immer

### 使用 Context 深层传递参数
通常来说，你会通过 props 将信息从父组件传递到子组件。但是，如果你必须通过许多中间组件向下传递 props，或是在你应用中的许多组件需要相同的信息，传递 props 会变的十分冗长和不便。**Context 允许父组件向其下层无论多深的任何组件提供信息，而无需通过 props 显式传递**。

Context：传递 props 的另一种方法 。Context 让父组件可以为它下面的整个组件树提供数据。

```js
<XxxContext.Provider value={xxValue}>
```

Context 的工作方式可能会让你想起 CSS 属性继承。在 CSS 中，你可以为一个 <div> 手动指定 color: blue，并且其中的任何 DOM 节点，无论多深，都会继承那个颜色，除非中间的其他 DOM 节点用 color: green 来覆盖它。类似地，在 React 中，覆盖来自上层的某些 context 的唯一方法是将子组件包裹到一个提供不同值的 context provider 中。


在 CSS 中，诸如 color 和 background-color 之类的不同属性不会覆盖彼此。你可以设置所有 <div> 的 color 为红色，而不会影响 background-color。类似地，**不同的 React context 不会覆盖彼此**。你通过 createContext() 创建的每个 context 都和其他 context 完全分离，只有使用和提供 那个特定的 context 的组件才会联系在一起。一个组件可以轻松地使用或者提供许多不同的 context。


#### 在使用 context 之前，你可以考虑以下几种替代方案：

1. 从 **传递 props** 开始。 如果你的组件看起来不起眼，那么通过十几个组件向下传递一堆 props 并不罕见。这有点像是在埋头苦干，但是这样做可以让哪些组件用了哪些数据变得十分清晰！维护你代码的人会很高兴你用 props 让数据流变得更加清晰。
2.抽象组件并 将 JSX 作为 children 传递 给它们。 如果你通过很多层不使用该数据的中间组件（并且只会向下传递）来传递数据，这通常意味着你在此过程中忘记了抽象组件。举个例子，你可能想传递一些像 posts 的数据 props 到不会直接使用这个参数的组件，类似 `<Layout posts={posts} />`。取而代之的是，让 Layout 把 children 当做一个参数，然后渲染 `<Layout><Posts posts={posts} /></Layout>`。这样就减少了定义数据的组件和使用数据的组件之间的层级。


#### Context 的使用场景 
- 主题： 如果你的应用允许用户更改其外观（例如暗夜模式），你可以在应用顶层放一个 context provider，并在需要调整其外观的组件中使用该 context。
- 当前账户： 许多组件可能需要知道当前登录的用户信息。将它放到 context 中可以方便地在树中的任何位置读取它。某些应用还允许你同时操作多个账户（例如，以不同用户的身份发表评论）。在这些情况下，将 UI 的一部分包裹到具有不同账户数据的 provider 中会很方便。
- 路由： 大多数路由解决方案在其内部使用 context 来保存当前路由。这就是每个链接“知道”它是否处于活动状态的方式。如果你创建自己的路由库，你可能也会这么做。
- 状态管理： 随着你的应用的增长，最终在靠近应用顶部的位置可能会有很多 state。许多遥远的下层组件可能想要修改它们。通常 将 reducer 与 context 搭配使用来管理复杂的状态并将其传递给深层的组件来避免过多的麻烦。

Context 不局限于静态值。如果你在下一次渲染时传递不同的值，React 将会更新读取它的所有下层组件！这就是 context 经常和 state 结合使用的原因。

一般而言，如果树中不同部分的远距离组件需要某些信息，context 将会对你大有帮助。


### 使用 Reducer 和 Context 拓展你的应用
Reducer 可以整合组件的状态更新逻辑。Context 可以将信息深入传递给其他组件。你可以组合使用它们来共同管理一个复杂页面的状态。

将state和函数都放入context