From 3f4ad920f51fe345fb00bb39faffaff886d82c69 Mon Sep 17 00:00:00 2001 From: YogaLin Date: Tue, 30 Nov 2021 00:29:23 +0800 Subject: [PATCH 1/9] docs(cn): preserving-and-resetting-state --- .../learn/preserving-and-resetting-state.md | 342 +++++++++--------- 1 file changed, 171 insertions(+), 171 deletions(-) diff --git a/beta/src/pages/learn/preserving-and-resetting-state.md b/beta/src/pages/learn/preserving-and-resetting-state.md index 6356630fa1..7dfaff5351 100644 --- a/beta/src/pages/learn/preserving-and-resetting-state.md +++ b/beta/src/pages/learn/preserving-and-resetting-state.md @@ -1,36 +1,36 @@ --- -title: Preserving and Resetting State +title: 如何控制状态的保留或重置 --- -State is isolated between components. React keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders. +状态与组件之间是隔离的。根据组件在 UI 树中的位置,React 可以跟踪组件所属的状态。你可以控制在 re-render 之间保留还是重置状态。 -* How React "sees" component structures -* When React chooses to preserve or reset the state -* How to force React to reset component's state -* How keys and types affect whether the state is preserved +* React 眼中的组件结构 +* React 保留或重置状态的条件 +* 如何强制 React 重置组件的状态 +* Key 和组件切换对状态保留的影响 -## The UI tree {/*the-ui-tree*/} +## UI 树 {/*the-ui-tree*/} -Browsers use many tree structures to model UI. The [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) represents HTML elements, the [CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model) does the same for CSS. There's even an [Accessibility tree](https://developer.mozilla.org/docs/Glossary/Accessibility_tree)! +浏览器使用许多树形结构来建模 UI 。[DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) 用于表示 HTML 元素,[CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model) 则表示 CSS 元素。甚至还有 [Accessibility tree](https://developer.mozilla.org/docs/Glossary/Accessibility_tree)! -React also uses tree structures to manage and model the UI you make. React makes **UI trees** from your JSX. Then React DOM updates the browser DOM elements to match that UI tree. (React Native translates these trees into elements specific to mobile platforms.) +React 也使用树形结构来对你创造的 UI 进行管理和建模。React 根据 JSX 生成了 **UI 树** 。React DOM 根据 UI 树去更新浏览器的 DOM 元素。(React Native 则在不同的移动端平台将 UI 树转换为相应的元素) -React takes components, turns them into UI tree structures, and ReactDOM turns them into HTML in your browser using the DOM. +React 获取组件后将它们转换为 UI 树结构,然后 ReactDOM 在浏览器中使用 DOM 将它们转换为 HTML。 -## State is tied to a position in the tree {/*state-is-tied-to-a-position-in-the-tree*/} +## 状态与其在树中的位置相关联 {/*state-is-tied-to-a-position-in-the-tree*/} -When you give a component state, you might think the state "lives" inside the component. But the state is actually held inside React. React associates each piece of state it's holding with the correct component by where that component sits in the UI tree. +当你创建了一个组件状态,你可能会觉得这个状态存在于组件内。但实际上,状态在 React 内部。根据组件在 UI 树中的位置,React 将它所持有的每个状态与正确的组件关联起来。 -Here, there is only one `` JSX tag, but it's rendered at two different positions: +下面只定义了一个 `` JSX 标签,但将它渲染在了两个不同的位置: @@ -64,7 +64,7 @@ function Counter() { >

{score}

); @@ -94,15 +94,15 @@ label {
-Here's how these look as a tree: +下面是它们在树中的样子: -The JSX become a tree. +JSX 被转换为一棵树 -**These are two separate counters because each is rendered at its own position in the tree.** You don't usually have to think about these positions to use React, but it can be useful to understand how it works. +**这是两个独立的 counter,因为它们在树中被渲染在了各自的位置** 一般情况下你不用去考虑这些位置来使用 React,但知道它们是如何工作的是很有用的。 -In React, each component on the screen has fully isolated state. For example, if you render two `Counter` components side by side, each of them will get its own, independent, `score` and `hover` states. +在 React 中,屏幕中的每个组件都有完全独立的状态。举个例子,当你并排渲染了两个 `Counter` 组件时,它们都拥有各自独立的 `score` 和 `hover` 状态。 -Try clicking both counters and notice they don't affect each other: +试试点击两个 counter 你会发现它们互不影响: @@ -135,7 +135,7 @@ function Counter() { >

{score}

); @@ -160,7 +160,7 @@ function Counter() {
-React will only keep the state around for as long as you render the same component at the same position. To see this, increment both counters, then clear "Render the second counter" checkbox, and then tick it again: +只有当相同的组件被渲染在了相同的位置,React 才会一直保留着组件的状态。想要验证这一点,可以增加两个计数器的值,清除“渲染第二个计数器”的复选框,然后重新勾选: @@ -181,7 +181,7 @@ export default function App() { setShowB(e.target.checked) }} /> - Render the second counter + 渲染第二个计数器 ); @@ -204,7 +204,7 @@ function Counter() { >

{score}

); @@ -234,19 +234,19 @@ label {
-Notice how the moment you stop rendering the second counter, its state disappears completely. That's because when React removes a component, it destroys its state. +注意,当停止渲染第二个计数器的那一刻,它的状态完全消失了。这是因为当 React 删除一个组件时, React 会销毁它的状态。 -React removes a component from the tree, it destroys its state as well +React 从树中移出组件的时候,也会同时移除它的状态 -When you tick "Render the second counter," a second `Counter` and its state are initialized from scratch (`score = 0`) and added to the DOM. +当你重新勾选“渲染第二个计数器”复选框时,另一个计数器及其状态从头开始初始化(`score = 0`)并添加到 DOM 中。 -When React adds UI to the DOM tree, it starts with all new state. +React 新增 UI 到 DOM 树时, 它会用新的状态进行初始化。 -**React preserves a component's state for as long as it's being rendered at its position in the UI tree.** If it gets removed, or a different component gets rendered at the same position, React discards its state. +**只要一个组件还被渲染在 UI 树的相同位置,React 就会保留它的状态。** 如果它被删除,或者一个不同的组件在相同的位置被渲染,React 将丢弃它的状态。 -## Same component at the same position preserves state {/*same-component-at-the-same-position-preserves-state*/} +## 同一组件在相同位置会保留状态 {/*same-component-at-the-same-position-preserves-state*/} -In this example, there are two different `` tags: +在这个例子中,有两个不同的 `` 标签: @@ -270,7 +270,7 @@ export default function App() { setIsFancy(e.target.checked) }} /> - Use fancy styling + 使用好看的样式 ); @@ -296,7 +296,7 @@ function Counter({ isFancy }) { >

{score}

); @@ -331,18 +331,18 @@ label {
-When you tick or clear the checkbox, the counter state does not get reset. Whether `isFancy` is `true` or `false`, you always have a `` as the first child of the `div` returned from the root `App` component: +当你勾选或清除复选框的时候,计数器状态没有被重置。不管 `isFancy` 是 `true` 还是 `false`,根 `App` 组件返回的 `div` 的第一个子元素总是 ``: -React only sees the component and its position in the UI tree on render. +React 只关注组件以及它在 UI 树中渲染的位置。 -It's the same component at the same position, so from React's perspective, it's the same counter. +在相同位置的同一个组件,所以对 React 来说,它是同一个计数器。 - + -Remember that **it's the position in the UI tree--not in the JSX markup--that matters to React!** This component has two `return` clauses with different `` JSX tags inside and outside the `if`: +记住 **对 React 来说重要的是组件在 UI 树中的位置,而不是在 JSX 的位置!** 这个组件有两个 `return` 语句,它们在 `if` 内外有不同的 `` JSX 标签: @@ -363,7 +363,7 @@ export default function App() { setIsFancy(e.target.checked) }} /> - Use fancy styling + 使用好看的样式 ); @@ -379,7 +379,7 @@ export default function App() { setIsFancy(e.target.checked) }} /> - Use fancy styling + 使用好看的样式 ); @@ -405,7 +405,7 @@ function Counter({ isFancy }) { >

{score}

); @@ -440,15 +440,15 @@ label {
-You might expect the state to reset when you tick checkbox, but it doesn't! This is because **both of these `` tags are rendered at the same position.** React doesn't know where you place the conditions in your function. All it "sees" is the tree you return. In both cases, the `App` component returns a `
` with `` as a first child. This is why React considers them as _the same_ ``. +你可能认为当你勾选复选框的时候状态会被重置,但它没有!这是因为**两个 `` 标签被渲染在了相同的位置。** React 不知道你的函数里是如何进行条件判断的,它只关注你返回的树。在这两种条件下,`App` 组件都返回了一个包裹着 `` 作为第一个子元素的 `div`。这就是 React 认为它们是 _同一个_ `` 的原因。 -You can think of them as having the same "address": the first child of the first child of the root. This is how React matches them up between the previous and next renders, regardless of how you structure your logic. +你可以认为它们有相同的“地址”:根节点的第一个子节点的第一个子节点。不管你的逻辑是怎么组织的,这就是 React 在上下两次渲染间将它们匹配的方式。 -## Different components at the same position reset state {/*different-components-at-the-same-position-reset-state*/} +## 相同位置的不同组件将重置状态 {/*different-components-at-the-same-position-reset-state*/} -In this example, ticking the checkbox will replace `` with a `

`: +在这个例子中,勾选复选框会将 `` 替换为一个 `

`: @@ -460,7 +460,7 @@ export default function App() { return (

{isPaused ? ( -

See you later!

+

待会见!

) : ( )} @@ -472,7 +472,7 @@ export default function App() { setIsPaused(e.target.checked) }} /> - Take a break + 休息一下
); @@ -495,7 +495,7 @@ function Counter() { >

{score}

); @@ -525,11 +525,11 @@ label { -Here, you switch between _different_ component types at the same position. Initially, the first child of the `
` contained a `Counter`. But when you swapped in a `p`, React removed the `Counter` from the UI tree and destroyed its state. +示例中,你在相同位置用 _不同类型_ 的组件进行切换。初始化时,`
` 的第一个子元素是一个 `Counter`。但是当你切换成一个 `p` 时,React 将 `Counter` 从 UI 树中移除并销毁了它的状态。 -Removing a component from the UI tree destroys its state. +将一个组件从 UI 树中移除会销毁它的状态。 -Also, **when you render a different component in the same position, it resets the state of its entire subtree**. To see how this works, increment the counter and then tick the checkbox: +并且,**当你在相同位置渲染了不同的组件时,组件的整个子树都会被重置** 。验证这一点,可以增加计数器的值然后勾选复选框: @@ -557,7 +557,7 @@ export default function App() { setIsFancy(e.target.checked) }} /> - Use fancy styling + 使用好看的样式
); @@ -583,7 +583,7 @@ function Counter({ isFancy }) { >

{score}

); @@ -618,17 +618,17 @@ label { -The counter state gets reset when you click the checkbox. Although you render a `Counter`, the first child of the `div` changes from a `div` to a `section`. When the child `div` was removed from the DOM, the whole tree below it (including the `Counter` and its state) was destroyed as well. +当你勾选复选框后计数器的状态被重置了。即使只是渲染一个 `Counter` , `div` 的第一个子元素从 `div` 变成了 `section` 。当子 `div` 被从 DOM 中移除的时候,它底下的整颗树(包含 `Counter` 以及它的状态)也都被销毁了。 -If the first child isn't the same, forget about it! +如果第一个子元素不同,就回不去了! -As a rule of thumb, **if you want to preserve the state between re-renders, the structure of your tree needs to "match up"** from one render to another. If the structure is different, the state gets destroyed because React destroys state when it removes a component from the tree. +一般来说,**如果你想在重新渲染之间保持状态,树的结构应该“匹配”** 于两次渲染之间。如果结构不同会导致状态的销毁,因为 React 会在组件从树中移除后销毁它的状态。 -This is why you should not nest component function definitions. +以下是为什么不应将组件的定义进行嵌套的原因。 -Here, the `MyTextField` component function is defined *inside* `MyComponent`: +示例中, `MyTextField` 组件被定义在了 `MyComponent` 内: @@ -654,7 +654,7 @@ export default function MyComponent() { + }}>点击了 {counter} 次 ); } @@ -663,13 +663,13 @@ export default function MyComponent() { -Every time you click the button, the input state disappears! This is because a *different* `MyTextField` function is created for every render of `MyComponent`. You're rendering a *different* component in the same position, so React resets all state below. This leads to bugs and performance problems. To avoid this problem, **always declare component functions at the top level, and don't nest their definitions.** +每次点击按钮后,输入框的状态都消失了!这是因为每次渲染后都创建了一个 _不同_ 的 `MyTextField` 函数。在相同位置渲染的是 _不同_ 的组件,所以 React 将它相关的状态都重置了。这样会导致 bug 以及性能问题。为了避免这个问题, **总是将组件定义在最外层并且不要嵌套定义。** -## Resetting state at the same position {/*resetting-state-at-the-same-position*/} +## 在相同位置重置状态 {/*resetting-state-at-the-same-position*/} -By default, React preserves state of a component while it stays at the same position. Usually, this is exactly what you want, so it makes sense as the default behavior. But sometimes, you may want to reset a component's state. Consider this app that lets two players keep track of their scores during each turn: +默认情况下,React 会保留还在相同位置的组件的状态。通常这就是我们想要的,所以它作为默认行为很合理。但有时候,你可能想要重置一个组件的状态。考虑一下这个应用,它可以让两个玩家在每个回合中记录他们的得分: @@ -688,7 +688,7 @@ export default function Scoreboard() { ); @@ -709,9 +709,9 @@ function Counter({ person }) { onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > -

{person}'s score: {score}

+

{person}的分数:{score}

); @@ -739,21 +739,21 @@ h1 {
-Currently, when you change the player, the score is preserved. The two `Counter`s appear in the same position, so React sees them as *the same* `Counter` whose `person` prop has changed. +目前你切换了玩家后,分数还是没有变化。这两个 `Counter` 出现于相同位置,所以 React 认为是 _同一个_ `Counter` ,只是传了不同的 `person` prop。 - + -But conceptually, in this app they should be two separate counters. They might appear in the same place in the UI, but one is a counter for Taylor, and another is a counter for Sarah. +概念上讲,这个应用的两个计数器应该是分离的。它们虽然 UI 上的位置相同,但是一个是 Taylor 的技术,一个是 Sarah 的计数器。 -There are two ways to reset state when switching between them: +有两个方法可以在它们之间切换时重置状态: -1. Render components in different positions -2. Give each component an explicit identity with `key` +1. 将组件渲染在不同的位置 +2. 给每个组件一个明确的标识 `key` -### Option 1: Rendering a component in different positions {/*option-1-rendering-a-component-in-different-positions*/} +### 方法一:将组件渲染在不同的位置 {/*option-1-rendering-a-component-in-different-positions*/} -If you want these two `Counter`s to be independent, you can render them in two different positions: +如果想要两个 `Counter` 是独立的,可以将他们渲染在不同的位置: @@ -773,7 +773,7 @@ export default function Scoreboard() { ); @@ -796,7 +796,7 @@ function Counter({ person }) { >

{person}'s score: {score}

); @@ -824,22 +824,22 @@ h1 {
-* Initially, `isPlayerA` is `true`. So the first position contains `Counter` state, and the second one is empty. -* When you click the "Next player" button the first position clears but the second one now contains a `Counter`. +* 初始化时, `isPlayerA` 的值是 `true` 。所以第一个位置包含了 `Counter` 的状态,而第二个位置是空的。 +* 点击了“下一位玩家”按钮后,第一个位置被清空了但是第二个位置现在包含了一个 `Counter` 。  -> Each `Counter`'s state gets destroyed each time its removed from the DOM. This is why they reset every time you click the button. +> 每次 `Counter` 被从 DOM 中移除时它的状态会被销毁。这就是每次点击了按钮后它们被重置的原因。 -This solution is convenient when you only have a few independent components rendered in the same place. In this example, you only have two, so it's not a hassle to render both separately in the JSX. +如果只有少数独立的组件在相同的位置渲染,这个解决方案很方便。这个例子中只有 2 个组件,所以在 JSX 里分开进行渲染并不麻烦。 -### Option 2: Resetting state with a key {/*option-2-resetting-state-with-a-key*/} +### 方法二:使用 key 来重置状态 {/*option-2-resetting-state-with-a-key*/} -There is also another, more generic, way to reset a component's state. +还有另一种更通用的方法来重置组件的状态。 -You might have seen `key`s when [rendering lists](/learn/rendering-lists#keeping-list-items-in-order-with-key). Keys aren't just for lists! You can use keys to make React distinguish between any components. By default, React uses order within the parent ("first counter", "second counter") to discern between components. But keys let you tell React that this is not just a *first* counter, or a *second* counter, but a specific counter--for example, *Taylor's* counter. This way, React will know *Taylor's* counter wherever it appears in the tree! +你可能已经在[列表渲染](/learn/rendering-lists#keeping-list-items-in-order-with-key)章节看过了 `key` 。但 key 不只是用于列表!你可以使用 key 来让 React 区分任何组件。默认情况下,React 使用父级中的顺序(“第一个计数器”、“第二个计数器”)来区分组件。但是 key 可以告诉 React 这不仅仅是 *第一个* 或者 *第二个* 计数器,而是一个特定的计数器————例如,*Taylor* 的计数器。这样不管它出现在哪里 React 都会知道它是 *Taylor* 的计数器! -In this example, the two ``s don't share state even though they appear in the same place in JSX: +在这个例子中,两个 `` 不会共享状态,即使它们出现在 JSX 中的相同位置: @@ -858,7 +858,7 @@ export default function Scoreboard() { ); @@ -879,9 +879,9 @@ function Counter({ person }) { onPointerEnter={() => setHover(true)} onPointerLeave={() => setHover(false)} > -

{person}'s score: {score}

+

{person}的分数:{score}

); @@ -909,7 +909,7 @@ h1 {
-Switching between Taylor and Sarah does not preserve the state. This is because **you gave them different `key`s:** +在 Taylor 和 Sarah 之间切换不会保留状态。因为 **你给它们赋了不同的 `key`:** ```js {isPlayerA ? ( @@ -919,17 +919,17 @@ Switching between Taylor and Sarah does not preserve the state. This is because )} ``` -Specifying a `key` tells React to use the `key` itself as part of the position, instead of their order within the parent. This is why, even though you render them in the same place in JSX, from React's perspective, these are two different counters. As a result, they will never share state. Every time a counter appears on the screen, its state is created. Every time it is removed, its state is destroyed. Toggling between them resets their state over and over. +指定 `key` 告诉 React 去使用 `key` 作为其位置信息的一部分,而不是它们在父元素中的顺序。这就是为什么尽管你用 JSX 将组件渲染在相同位置,但在 React 看来它们是两个不同的计数器。因此它们不会共享状态。每次一个计数器出现在屏幕上时,它的状态会被创建。每次它被移除了,它的状态会被销毁。在它们之间切换会一次又一次地重置它们的状态。 -> Remember that keys are not globally unique. They only specify the position *within the parent*. +> 请记住 key 不是全局唯一的。它们只能在同一父元素内指定顺序。 -### Resetting a form with a key {/*resetting-a-form-with-a-key*/} +### 使用 key 重置表单 {/*resetting-a-form-with-a-key*/} -Resetting state with a key is particularly useful when dealing with forms. +使用 key 来重置状态在处理表单时特别有用。 -In this chat app, the `` component contains the text input state: +在这个聊天应用中, `` 组件包含文本输入状态: @@ -992,11 +992,11 @@ export default function Chat({ contact }) {