From ceb9d26528936ec9f55c32109068f3799e90cadd Mon Sep 17 00:00:00 2001 From: zhaozhiwei Date: Thu, 4 Nov 2021 14:15:16 +0800 Subject: [PATCH 1/8] docs(cn): state-as-a-snapshot --- beta/src/pages/learn/state-as-a-snapshot.md | 155 ++++++++++---------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/beta/src/pages/learn/state-as-a-snapshot.md b/beta/src/pages/learn/state-as-a-snapshot.md index b21bf3a23c..6d7a10efb4 100644 --- a/beta/src/pages/learn/state-as-a-snapshot.md +++ b/beta/src/pages/learn/state-as-a-snapshot.md @@ -1,33 +1,35 @@ --- -title: State as a Snapshot +title: State 作为快照 +Translators: + - 7ooz --- -State variables might look like regular JavaScript variables that you can read and write to. However, state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render. +State 变量看起来和一般的可读写的 JavaScript 变量类似。但实际上,State 表现得更像是一张快照。设置它不会更改你已有的 state 变量,而是会触发重新渲染。 -* How setting state triggers re-renders -* When and how state updates -* Why state does not update immediately after you set it -* How event handlers access a "snapshot" of the state +* 设置 state 如何导致重新渲染 +* State 在何时以何种方式更新 +* 为什么 state 不在设置后立即更新 +* 事件处理函数如何获取 state 的一张“快照” -## Setting state triggers renders +## 设置 state 触发渲染 {#setting-state-triggers-renders} -You might think of your user interface as changing directly in response to the user input like a click. This may feel intuitive if you've been [storyboarding](https://wikipedia.org/wiki/Storyboard) your designs and interactions: +你可能认为,你的用户界面会直接响应用户的输入(如单击)而发生变化。这种感觉很直观,如果你一直在[故事板化](https://wikipedia.org/wiki/Storyboard)你的设计和交互的话: -In React, it works a little differently from this mental model. On the previous page, you saw that [setting state requests a re-render](/learn/render-and-commit#step-1-trigger-a-render) from React. This means that for an interface to react to the input, you need to set its state. +在 React 中,它的工作方式与这种思维模型略有不同。在上一页中,你看到了来自 React 的[设置 state 请求重新渲染](/learn/render-and-commit#step-1-trigger-a-render)。这意味着要使界面对输入做出反应,你需要设置其 state。 -In this example, when you press "send", `setIsSent(true)` tells React to re-render the UI: +在这个例子中,当你按下“send”时,`setIsSent(true)` 会通知 React 重新渲染 UI: @@ -67,45 +69,45 @@ label, textarea { margin-bottom: 10px; display: block; } -Here's what happens when you click the button: +单击按钮时会发生以下情况: -1. The `onSubmit` event handler executes. -2. `setIsSent(true)` sets `isSent` to `true`and queues a new render. -3. React re-renders the component according to the new `isSent` value. +1. 执行 `onSubmit` 事件处理函数。 +2. `setIsSent(true)` 将 `isSent` 设置为 `true` 并排列一个新的渲染。 +3. React 根据新的 `isSent` 值重新渲染组件。 -Let's take a closer look at the relationship between state and rendering. +让我们仔细看看 state 和渲染之间的关系。 -## Rendering takes a snapshot in time +## 渲染接收实时快照 {#rendering-takes-a-snapshot-in-time} -["Rendering"](/learn/render-and-commit#step-2-react-renders-your-components) means that React is calling your component, which is a function. The JSX you return from that function is like a snapshot of the UI in time. Its props, event handlers, and local variables were all calculated **using its state at the time of the render.** +["渲染"](/learn/render-and-commit#step-2-react-renders-your-components)表示 React 正在调用你的组件,组件是一个函数。你从该函数返回的 JSX 就像是一张及时的 UI 快照。它的 props、事件处理函数和局部变量都是**根据当前渲染下的 state 计算的。** -Unlike a photograph or a movie frame, the UI "snapshot" you return is interactive. It includes logic like event handlers that specify what happens in response to inputs. React then updates the screen to match this snapshot and connects the event handlers. As a result, pressing a button will trigger the click handler from your JSX. +与照片或电影画面不同,你返回的 UI “快照”是可交互的。它包含类似于事件处理函数的逻辑,用于指定输入时发生什么响应。 React 随后更新界面以匹配此快照,并绑定事件处理函数。因此,按下按钮将触发 JSX 中的 click 事件处理函数。 -When React re-renders a component: +当 React 重新渲染组件时: -1. React calls your function again. -2. Your function returns a new JSX snapshot. -3. React then updates the screen to match the snapshot you've returned. +1. React 再次调用你的函数 +2. 你的函数返回新的 JSX 快照 +3. React 更新界面来匹配返回的快照 - - - - + + + + -As a component's memory, state is not like a regular variable that disappears after your function returns. State actually "lives" in React itself--as if on a shelf!--outside of your function. When React calls your component, it gives you a snapshot of the state for that particular render. Your component returns a snapshot of the UI with a fresh set of props and event handlers in its JSX, all calculated **using the state values from that render!** +作为组件的内存,state 不同于在函数返回后就消失的一般变量。State 实际上“存在”于 React 当中——就像在架子上!——位于你的函数之外。当 React 调用你的组件时,它会为该次渲染提供一张 state 快照。你的组件在 JSX 中返回带有新 props 和事件处理函数的一张 UI 快照 ,其中所有的值都是**根据该次渲染中的 state 计算的!** - - - + + + -Here's a little experiment to show you how this works. In this example, you might expect that clicking the "+3" button would increment the counter three times because it calls `setNumber(number + 1)` three times. +这里有一个小例子,向你展示这是如何工作的。在这个例子中,你可能期望点击“+3”按钮会增加计数器三次,因为它调用了 `setNumber(number + 1)` 三次。 -See what happens when you click the "+3" button: +让我们看看点击“+3”按钮会发生什么: @@ -135,9 +137,9 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Notice that `number` only increments once per click! +我们注意到,每次点击只会让 `number`的值增加 1! -**Setting state only changes it for the *next* render.** During the first render, `number` was `0`. This is why, in *that render's* `onClick` handler, the value of `number` is still `0` even after `setNumber(number + 1)` was called: +**设置 state 只会在*下一个* 渲染中改变 state 的值。**在第一次渲染期间,`number` 为 `0`。这也就解释了,为什么在*这次渲染*中的 `onClick` 函数里,即便调用了 `setNumber(number + 1)` ,`number` 的值仍然是 `0`: ```js ``` -Here is what this button's click handler tells React to do: +以下是这个按钮的 click 事件处理函数通知 React 做的事情: -1. `setNumber(number + 1)`: `number` is `0` so `setNumber(0 + 1)`. - - React prepares to change `number` to `1` on the next render. -2. `setNumber(number + 1)`: `number` is `0` so `setNumber(0 + 1)`. - - React prepares to change `number` to `1` on the next render. -3. `setNumber(number + 1)`: `number` is `0` so `setNumber(0 + 1)`. - - React prepares to change `number` to `1` on the next render. +1. `setNumber(number + 1)`:`number` 是 `0` 所以 `setNumber(0 + 1)`。 + - React 准备在下一次渲染时将 `number` 更改为 `1`。 +2.`setNumber(number + 1)`:`number` 是`0` 所以`setNumber(0 + 1)`。 + - React 准备在下一次渲染时将 `number` 更改为 `1`。 +3.`setNumber(number + 1)`:`number` 是`0` 所以`setNumber(0 + 1)`。 + - React 准备在下一次渲染时将 `number` 更改为 `1`。 -Even though you called `setNumber(number + 1)` three times, in *this render's* event handler `number` is always `0`, so you set the state to `1` three times. This is why, after your event handler finishes, React re-renders the component with `number` equal to `1` rather than `3`. +尽管你调用了`setNumber(number + 1)` 三次,但在*这次渲染的* 事件处理函数中 `number` 总是`0`,所以你将 state 设置成了 `1` 三次。这就是为什么,在你的事件处理函数完成后,React 重新渲染组件时,其中的 `number` 等于 `1` 而不是 `3`。 -You can also visualize this by mentally substituting state variables with their values in your code. Since the `number` state variable is `0` for *this render*, its event handler looks like this: +你还可以在心中可视化这个过程,用 state 变量当前的值去替换掉它。由于*这次渲染* 的state 变量 `number` 的值为 `0`,其事件处理函数看起来将是这样: ```js ``` - -For the next render, `number` is `1`, so *that render's* click handler looks like this: +对于下一次渲染,`number` 的值变为 `1`,因此*下次渲染的* click 事件处理函数如下所示: ```js ``` +这就是为什么第二次单击按钮会将计数器设置为“2”,再一次单击后又变为“3”,依此类推。 -This is why clicking the button again will set the counter to `2`, then to `3` on the next click, and so on. - -## State over time +## 状态伴随时间 {#state-over-time} -Well, that was fun. Try to guess what clicking this button will alert: +嗯,这很有趣。试着猜猜单击此按钮会发出什么警告: @@ -211,14 +211,14 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -If you use the substitution method from before, you can guess that the alert shows "0": +如果你使用之前的替换模型,你可以猜到这个 alert 将会显示"0": ```js setNumber(0 + 5); alert(0); ``` -But what if you put a timer on the alert, so it only fires _after_ the component re-rendered? Would it say "0" or "5"? Have a guess! +但如果你在这个 alert 上加上一个定时器, 使得它在组件重新渲染后才会触发,又会怎样呢?会显示“0”还是“5”?猜一猜! @@ -249,7 +249,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Surprised? If you use the substitution method, you can see the "snapshot" of the state passed to the alert. +惊讶吗?如果使用替换方法,则可以看到传递给 alert 的 state 的“快照”。 ```js setNumber(0 + 5); @@ -257,17 +257,16 @@ setTimeout(() => { alert(0); }, 3000); ``` +React 中存储的 state 可能在执行 alert 时发生了更改,但它的值是由用户与其交互时的 state 快照决定的! -The state stored in React may have changed by the time the alert runs, but it was scheduled using a snapshot of the state at the time the user interacted with it! - -**A state variable's value never changes within a render,** even if its event handler's code is asynchronous. Inside *that render's* `onClick`, the value of `number` continues to be `0` even after `setNumber(number + 5)` was called. Its value was "fixed" when React "took the snapshot" of the UI by calling your component. +**在一次渲染中,State 变量的值永远不会改变,**即使其事件处理函数的代码是异步的。在*这次渲染的* `onClick` 中,即使在调用 `setNumber(number + 5)` 之后,`number` 的值仍然是`0`。当 React 通过调用你的组件“获取 UI 的快照”时,它的值是“固定的”。 -Here is an example of how that makes your event handlers less prone to timing mistakes. Below is a form that sends a message with a five-second delay. Imagine this scenario: +这是一个使你的事件处理函数更不易出现计时错误的示例。下面是一个延迟五秒发送消息的表单。想象以下场景: -1. You press the "Send" button, sending "Hello" to Alice. -2. Before the five-second delay ends, you change the value of the "To" field to "Bob". +1. 你按下“发送”按钮,向 Alice 发送“Hello”。 +2. 在五秒延迟结束之前,将“To”字段的值更改为“Bob”。 -What do you expect the `alert` to display? Would it display, "You said Hello to Alice"? Or would it display, "You said Hello to Bob"? Make a guess based on what you know, and then try it: +你预测 `alert` 会显示什么?它会显示“You said Hello to Alice”吗?或者它会显示“You said Hello to Bob”?根据你已经学到的知识猜一猜,然后动手试一试: @@ -313,19 +312,19 @@ label, textarea { margin-bottom: 10px; display: block; } -**React keeps the state values "fixed" within one render's event handlers.** You don't need to worry whether the state has changed while the code is running. +**React 在一次渲染的事件处理函数中始终固定 state 的值。**你无需担心代码运行时 state 是否已更改。 -But what if you wanted to read the latest state before a re-render? You'll want to use a [state updater function](/learn/queueing-a-series-of-state-updates), covered on the next page! +但是,如果你想在重新渲染之前读取最新的 state 怎么办?你将需要使用[状态更新函数](/learn/queueing-a-series-of-state-updates),下一页将会介绍! -* Setting state requests a new render. -* React stores state outside of your component, as if on a shelf. -* When you call `useState`, React gives you a snapshot of the state *for that render*. -* Variables and event handlers don't "survive" re-renders. Every render has its own event handlers. -* Every render (and functions inside it) will always "see" the snapshot of the state that React gave to *that* render. -* You can mentally substitute state in event handlers, similarly to how you think about the rendered JSX. -* Event handlers created in the past have the state values from the render in which they were created. +* 设置 state 请求一次新的渲染。 +* React 将 state 存储在组件之外,就像在架子上一样。 +* 当你调用 `useState` 时,React 会为你提供*该次渲染* 的一张 state 快照。 +* 变量和事件处理函数不会在重渲染中“存活”。每个渲染都有自己的事件处理函数。 +* 每个渲染(以及其中的函数)始终“看到”的是 React 提供给*这个* 渲染的 state 快照。 +* 你可以在心中替换事件处理函数中的 state,类似于替换渲染的 JSX。 +* 过去创建的事件处理函数拥有的是创建它们的那次渲染中的 state 值。 @@ -333,9 +332,9 @@ But what if you wanted to read the latest state before a re-render? You'll want -### Implement a traffic light +### 实现红绿灯组件 -Here is a crosswalk light component that toggles on when the button is pressed: +以下是一个人行道红绿灯组件,在按下按钮时会切换状态: @@ -370,13 +369,13 @@ h1 { margin-top: 20px; } -Add an `alert` to the click handler. When the light is green and says "Walk," clicking the button should say "Stop is next." When the light is red and says "Stop," clicking the button should say "Walk is next." +向 click 事件处理函数添加一个 `alert` 。当灯为绿色且显示“Walk”时,单击按钮应显示“Stop is next”。当灯为红色并显示“Stop”时,单击按钮应显示“Walk is next”。 -Does it make a difference whether you put the `alert` before or after the `setWalk` call? +把 `alert` 方法放在 `setWalk` 方法之前或之后有区别吗? -Your `alert` should look like this: +你的 `alert` 应该看起来像是这样: @@ -412,17 +411,17 @@ h1 { margin-top: 20px; } -Whether you put it before or after the `setWalk` call makes no difference. That render's value of `walk` is fixed. Calling `setWalk` will only change it for the *next* render, but will not affect the event handler from the previous render. +无论是将它放在 `setWalk` 调用之前还是之后都没有区别。这次渲染下 `walk` 的值是固定的。调用`setWalk` 只会在*下次* 渲染时改变它,不会影响之前渲染的事件处理函数。 -This line might seem counter-intuitive at first: +这一行代码乍看之下似乎有违直觉: ```js alert(walk ? 'Stop is next' : 'Walk is next'); ``` -But it makes sense if you read it as: "If the traffic light shows 'Walk now', the message should say 'Stop is next.'" The `walk` variable inside your event handler matches that render's value of `walk` and does not change. +为了便于理解,如果你将其读作:“如果交通灯显示‘Walk now’,则消息应显示‘Stop is next’。”事件处理函数中的 `walk` 变量与渲染出的 `walk` 值一致,并且不会改变。 -You can verify that this is correct by applying the substitution method. When `walk` is `true`, you get: +你可以应用替换方法来验证这是否正确。当 `walk` 为 `true` 时,你会得到: ```js ``` -以下是这个按钮的 click 事件处理函数通知 React 做的事情: +以下是这个按钮的点击事件处理函数通知 React 要做的事情: 1. `setNumber(number + 1)`:`number` 是 `0` 所以 `setNumber(0 + 1)`。 - React 准备在下一次渲染时将 `number` 更改为 `1`。 -2.`setNumber(number + 1)`:`number` 是`0` 所以`setNumber(0 + 1)`。 +2. `setNumber(number + 1)`:`number` 是`0` 所以 `setNumber(0 + 1)`。 - React 准备在下一次渲染时将 `number` 更改为 `1`。 -3.`setNumber(number + 1)`:`number` 是`0` 所以`setNumber(0 + 1)`。 +3. `setNumber(number + 1)`:`number` 是`0` 所以 `setNumber(0 + 1)`。 - React 准备在下一次渲染时将 `number` 更改为 `1`。 -尽管你调用了`setNumber(number + 1)` 三次,但在*这次渲染的* 事件处理函数中 `number` 总是`0`,所以你将 state 设置成了 `1` 三次。这就是为什么,在你的事件处理函数完成后,React 重新渲染组件时,其中的 `number` 等于 `1` 而不是 `3`。 +尽管你调用了三次 `setNumber(number + 1)`,但在 *这次渲染的* 事件处理函数中 `number` 会一直是 `0`,所以你会三次将 state 设置成 `1`。这就是为什么在你的事件处理函数执行完以后,React 重新渲染的组件中的 `number` 等于 `1` 而不是 `3`。 -你还可以在心中可视化这个过程,用 state 变量当前的值去替换掉它。由于*这次渲染* 的state 变量 `number` 的值为 `0`,其事件处理函数看起来将是这样: +你还可以通过在心里把 state 变量替换成它们在你代码中的值来想象这个过程。由于 *这次渲染* 中的 state 变量 `number` 是 `0`,其事件处理函数看起来会像这样: ```js ``` -对于下一次渲染,`number` 的值变为 `1`,因此*下次渲染的* click 事件处理函数如下所示: +对于下一次渲染来说,`number` 是 `1`,因此 *那次渲染中的* 点击事件处理函数看起来会像这样: ```js ``` -这就是为什么第二次单击按钮会将计数器设置为“2”,再一次单击后又变为“3”,依此类推。 +这就是为什么再次点击按钮会将计数器设置为 `2`,下次点击时会设为 `3`,依此类推。 -## 状态伴随时间 {/*state-over-time*/} +## 随时间变化的 state {/*state-over-time*/} -嗯,这很有趣。试着猜猜单击此按钮会发出什么警告: +好的,刚才那些很有意思。试着猜猜点击这个按钮会发出什么警告: @@ -203,14 +204,14 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -如果你使用之前的替换模型,你可以猜到这个 alert 将会显示"0": +如果你使用之前替换的方法,你就能猜到这个提示框将会显示 "0": ```js setNumber(0 + 5); alert(0); ``` -但如果你在这个 alert 上加上一个定时器, 使得它在组件重新渲染后才会触发,又会怎样呢?会显示“0”还是“5”?猜一猜! +但如果你在这个提示框上加上一个定时器, 使得它在组件重新渲染 _之后_ 才触发,又会怎样呢?是会显示 “0” 还是 “5” ?猜一猜! @@ -241,7 +242,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -惊讶吗?如果使用替换方法,则可以看到传递给 alert 的 state 的“快照”。 +惊讶吗?你如果使用替换的方法,就能看到被传入提示框的 state “快照”。 ```js setNumber(0 + 5); @@ -249,16 +250,17 @@ setTimeout(() => { alert(0); }, 3000); ``` -React 中存储的 state 可能在执行 alert 时发生了更改,但它的值是由用户与其交互时的 state 快照决定的! -**在一次渲染中,State 变量的值永远不会改变,**即使其事件处理函数的代码是异步的。在*这次渲染的* `onClick` 中,即使在调用 `setNumber(number + 5)` 之后,`number` 的值仍然是`0`。当 React 通过调用你的组件“获取 UI 的快照”时,它的值是“固定的”。 +到提示框运行时,React 中存储的 state 可能已经发生了更改,但是这次更改是根据用户与其交互时的 state 快照来安排的! -这是一个使你的事件处理函数更不易出现计时错误的示例。下面是一个延迟五秒发送消息的表单。想象以下场景: +**一个 state 变量的值永远不会在一次渲染的内部发生变化,**即使其事件处理函数的代码是异步的。在 *那次渲染的* `onClick` 内部,`number` 的值即使在调用 `setNumber(number + 5)` 之后也还是 `0`。它的值在 React 通过调用你的组件“获取 UI 的快照”时就被“固定”了。 -1. 你按下“发送”按钮,向 Alice 发送“Hello”。 +这里有个示例能够说明上述特性会使你的事件处理函数更不容易出现计时错误。下面是一个会在五秒延迟之后发送一条消息的表单。想象以下场景: + +1. 你按下“发送”按钮,向 Alice 发送“你好”。 2. 在五秒延迟结束之前,将“To”字段的值更改为“Bob”。 -你预测 `alert` 会显示什么?它会显示“You said Hello to Alice”吗?或者它会显示“You said Hello to Bob”?根据你已经学到的知识猜一猜,然后动手试一试: +你觉得 `alert` 会显示什么?它是会显示“你向 Alice 说了你好“还是会显示“你向 Tom 说了你好”?根据你已经学到的知识猜一猜,然后动手试一试: @@ -304,9 +306,9 @@ label, textarea { margin-bottom: 10px; display: block; } -**React 在一次渲染的事件处理函数中始终固定 state 的值。** 你无需担心代码运行时 state 是否已更改。 +**React 会使 state 的值始终”固定“在一次渲染的各个事件处理函数内部。** 你无需担心代码运行时 state 是否发生了变化。 -但是,如果你想在重新渲染之前读取最新的 state 怎么办?你将需要使用[状态更新函数](/learn/queueing-a-series-of-state-updates),下一页将会介绍! +但是,万一你想在重新渲染之前读取最新的 state 怎么办?你应该使用 [状态更新函数](/learn/queueing-a-series-of-state-updates),下一页将会介绍! @@ -367,7 +369,7 @@ h1 { margin-top: 20px; } -你的 `alert` 应该看起来像是这样: +你的 `alert` 看起来应该像是这样: @@ -403,7 +405,7 @@ h1 { margin-top: 20px; } -无论是将它放在 `setWalk` 调用之前还是之后都没有区别。这次渲染下 `walk` 的值是固定的。调用`setWalk` 只会在*下次* 渲染时改变它,不会影响之前渲染的事件处理函数。 +无论你是将它放在 `setWalk` 调用之前还是之后都不会有区别。那次渲染的 `walk` 的值是固定的。调用`setWalk` 只会为 *下次* 渲染对它进行变更,而不会影响来自上次渲染的事件处理函数。 这一行代码乍看之下似乎有违直觉: @@ -411,9 +413,9 @@ h1 { margin-top: 20px; } alert(walk ? 'Stop is next' : 'Walk is next'); ``` -为了便于理解,如果你将其读作:“如果交通灯显示‘Walk now’,则消息应显示‘Stop is next’。”事件处理函数中的 `walk` 变量与渲染出的 `walk` 值一致,并且不会改变。 +但如果你将其读作“如果交通灯显示‘现在就走吧’,则消息应显示‘接下来是停’。”就容易理解了。你事件处理函数中的 `walk` 变量与所渲染的 `walk` 值是一致的,并且不会发生改变。 -你可以应用替换方法来验证这是否正确。当 `walk` 为 `true` 时,你会得到: +你可以用替换的方法来验证这是否正确。当 `walk` 为 `true` 时,你会得到: ```js