From 48d2c778e2f4c3ee6dbb8cb68ab97afe8fdd7924 Mon Sep 17 00:00:00 2001 From: Jiacheng Dong <2334173653@qq.com> Date: Sun, 28 Nov 2021 15:41:28 +0800 Subject: [PATCH 1/3] doc(cn): queueing-a-series-of-state-updates --- .../queueing-a-series-of-state-updates.md | 204 +++++++++--------- 1 file changed, 103 insertions(+), 101 deletions(-) diff --git a/beta/src/pages/learn/queueing-a-series-of-state-updates.md b/beta/src/pages/learn/queueing-a-series-of-state-updates.md index 1c8ee18f1c..f8abc81cf4 100644 --- a/beta/src/pages/learn/queueing-a-series-of-state-updates.md +++ b/beta/src/pages/learn/queueing-a-series-of-state-updates.md @@ -1,23 +1,25 @@ --- -title: Queueing a Series of State Updates +title: 状态更新的队列 +translators: + - Jiacheng787 --- -Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render. To do this, it helps to understand how React batches state updates. +设置组件状态将会把一个渲染任务加入队列。但有时你可能会希望在下次渲染任务加入队列之前,执行多次状态更新。为此,这有助于了解 React 如何批量更新状态。 -* What "batching" is and how React uses it to process multiple state updates -* How to apply several updates to the same state variable in a row +* 什么是“批处理”以及 React 如何使用它来处理多个状态更新 +* 如何通过一行代码对同一状态变量进行多次更新 -## React batches state updates {/*react-batches-state-updates*/} +## React 批量状态更新 {/*react-batches-state-updates*/} -You might expect that clicking the "+3" button will increment the counter three times because it calls `setNumber(number + 1)` three times: +在下面的示例中,你可能会认为,点击“+3”按钮会使计数器增加三次,因为它调用了 `setNumber(number + 1)` 三次: @@ -47,7 +49,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -However, as you might recall from the previous section, [each render's state values are fixed](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), so the value of `number` inside the first render's event handler is always `0`, no matter how many times you call `setNumber(1)`: +但是,你可能还记得上一节中的内容,[每一次渲染的状态值都是固定的](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time),因此无论你调用多少次 `setNumber(1)`,在第一次渲染的事件处理函数内部的 `number` 值总是 `0` : ```js setNumber(0 + 1); @@ -55,21 +57,21 @@ setNumber(0 + 1); setNumber(0 + 1); ``` -But there is one other factor at work here to discuss. **React waits until *all* code in the the event handlers has run before processing your state updates.** This is why the re-render only happens *after* all these `setNumber()` calls. +但是这里还有另外一个因素需要讨论。**在处理状态更新之前,React 会等待事件处理函数中的 *所有* 代码都运行完毕。** 这就是为什么重新渲染只在所有这些 `setNumber()` 调用 *之后* 发生的原因。 -This might remind you of a waiter taking an order at the restaurant. A waiter doesn't run to the kitchen at the mention of your first dish! Instead, they let you finish your order, let you make changes to it, and even take orders from other people at the table. +这可能会让你想起在餐厅点菜的服务员。服务员不会在提到你的第一道菜的时候就跑到厨房!相反,他们会让你完成订单,让你对其进行更改,甚至可以接受桌上其他人的订单。 - + -This lets you update multiple state variables--even from multiple components--without triggering too many [re-renders](/learn/render-and-commit#re-renders-when-state-updates). But this also means that the UI won't be updated until _after_ your event handler, and any code in it, completes. This behavior, also known as **batching,** makes your React app run much faster. It also avoids dealing with confusing "half-finished" renders where only some of the variables have been updated. +这让你可以更新多个状态——甚至来自多个组件——而不会触发太多的 [重新渲染](/learn/render-and-commit#re-renders-when-state-updates)。但这也意味着只有在你的事件处理函数及其中任何代码完成 *之后*,UI 才会更新。这种行为,被称为 **批处理,** 使你的 React app 运行更快。它还避免了处理只​​更新了一些变量的令人困惑的“半成品”渲染。 -**React does not batch across *multiple* intentional events like clicks**--each click is handled separately. Rest assured that React only does batching when it's generally safe to do. This ensures that, for example, if the first button click disables a form, the second click would not submit it again. +**React 不会跨 *多个* 有意的事件(如点击)进行批处理**——每次点击都是单独处理的。请放心,React 仅在通常安全的情况下才进行批处理。这确保了,例如,如果第一次点击按钮禁用了表单,则第二次点击不会再次提交它。 -## Updating the same state variable multiple times before the next render {/*updating-the-same-state-variable-multiple-times-before-the-next-render*/} +## 在下次渲染前多次更新同一个状态 {/*updating-the-same-state-variable-multiple-times-before-the-next-render*/} -It is an uncommon use case, but if you would like to update the same state variable multiple times before the next render, instead of passing the *next state value* like `setNumber(number + 1)`, you can pass a *function* that calculates the next state based on the previous one in the queue, like `setNumber(n => n + 1)`. It is a way to tell React to "do something with the state value" instead of just replacing it. +这是一个不常见的用例,但是如果你想在下次渲染之前多次更新同一个状态,而不是传递 *下一个状态值* 例如 `setNumber(number + 1)`,你可以传递一个 *函数*,该函数根据队列中的前一个状态计算下一个状态,例如 `setNumber(n => n + 1)`。这是一种告诉 React “用状态值做某事”而不是仅仅替换它的方法。 -Try incrementing the counter now: +现在尝试增加计数器: @@ -99,10 +101,10 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Here, `n => n + 1` is called an **updater function**. When you pass it to a state setter: +在这里,`n => n + 1` 被称为 **更新函数**。当你将它传递给 state setter 时: -1. React queues this function to be processed after all the other code in the event handler has run. -2. During the next render, React goes through the queue and gives you the final updated state. +1. React 将此函数加入队列,在事件处理函数中的所有其他代码运行后进行处理。 +2. 在下一次渲染期间,React 遍历队列并提供最终更新状态。 ```js setNumber(n => n + 1); @@ -110,26 +112,26 @@ setNumber(n => n + 1); setNumber(n => n + 1); ``` -Here's how React works through these lines of code while executing the event handler: +下面是 React 在执行事件处理函数时的处理流程: -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. +1. `setNumber(n => n + 1)`:`n => n + 1` 是一个函数。React 将它加入队列。 +2. `setNumber(n => n + 1)`:`n => n + 1` 是一个函数。React 将它加入队列。 +3. `setNumber(n => n + 1)`:`n => n + 1` 是一个函数。React 将它加入队列。 -When you call `useState` during the next render, React goes through the queue. The previous `number` state was `0`, so that's what React passes to the first updater function as the `n` argument. Then React takes the return value of your previous updater function and passes it to the next updater as `n`, and so on: +当你在下次渲染期间调用 `useState`,React 会遍历队列。初始的 `number` 状态是 `0`,所以这就是 React 作为 `n` 参数传递给第一个更新函数的值。然后 React 获取上一个更新函数的返回值,并将其作为 `n` 传递给下一个更新函数,以此类推: -| queued update | `n` | returns | +| 更新队列 | `n` | 返回值 | |--------------|---------|-----| | `n => n + 1` | `0` | `0 + 1 = 1` | | `n => n + 1` | `1` | `1 + 1 = 2` | | `n => n + 1` | `2` | `2 + 1 = 3` | -React stores `3` as the final result and returns it from `useState`. +React 保存 `3` 为最终结果并从 `useState` 中返回。 -This is why clicking "+3" in the above example correctly increments the value by 3. -### What happens if you update state after replacing it {/*what-happens-if-you-update-state-after-replacing-it*/} +这就是为什么在上面的示例中点击“+3”正确地将值增加“+3”。 +### 如果在替换状态后更新状态会发生什么 {/*what-happens-if-you-update-state-after-replacing-it*/} -What about this event handler? What do you think `number` will be in the next render? +这个事件处理函数会怎么样?你认为 `number` 下一次渲染的值是什么? ```js + }}>增加数字 ) } @@ -165,25 +167,25 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Here's what this event handler tells React to do: +这是事件处理函数告诉 React 要做的事情: -1. `setNumber(number + 5)`: `number` is `0`, so `setNumber(0 + 5)`. React adds *"replace with `5`"* to its queue. -2. `setNumber(n => n + 1)`: `n => n + 1` is an updater function. React adds *that function* to its queue. +1. `setNumber(number + 5)`:`number` 为 `0`,所以 `setNumber(0 + 5)`。React 将 *“替换为 `5`”* 添加到其队列中。 +2. `setNumber(n => n + 1)`:`n => n + 1` 是一个更新函数。 React 将 *该函数* 添加到其队列中。 -During the next render, React goes through the state queue: +在下一次渲染期间,React 会遍历状态队列: -| queued update | `n` | returns | +| 更新队列 | `n` | 返回值 | |--------------|---------|-----| -| "replace with `5`" | `0` (unused) | `5` | +| “替换为 `5`” | `0`(未使用) | `5` | | `n => n + 1` | `5` | `5 + 1 = 6` | -React stores `6` as the final result and returns it from `useState`. +React 保存 `6` 为最终结果并从 `useState` 中返回。 -> You may have noticed that `setState(x)` actually works like `setState(n => x)`, but `n` is unused! +> 你可能已经注意到,`setState(x)` 实际上像 `setState(n => x)` 一样工作,只是 `n` 未使用! -### What happens if you replace state after updating it {/*what-happens-if-you-replace-state-after-updating-it*/} +### 如果在更新状态后替换状态会发生什么 {/*what-happens-if-you-replace-state-after-updating-it*/} -Let's try one more example. What do you think `number` will be in the next render? +让我们再看一个例子。你认为 `number` 下一次渲染的值是什么? ```js + }}>增加数字 ) } @@ -221,32 +223,32 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Here's how React works through these lines of code while executing this event handler: +以下是 React 在执行事件处理函数时的处理流程: -1. `setNumber(number + 5)`: `number` is `0`, so `setNumber(0 + 5)`. React adds *"replace with `5`"* to its queue. -2. `setNumber(n => n + 1)`: `n => n + 1` is an updater function. React adds *that function* to its queue. -3. `setNumber(42)`: React adds *"replace with `42`"* to its queue. +1. `setNumber(number + 5)`:`number` 为 `0`,所以 `setNumber(0 + 5)`。React 将 *“替换为 `5`”* 添加到其队列中。 +2. `setNumber(n => n + 1)`:`n => n + 1` 是一个更新函数。React 将该函数添加到其队列中。 +3. `setNumber(42)`:React 将 *“替换为 `42`”* 添加到其队列中。 -During the next render, React goes through the state queue: +在下一次渲染期间,React 会遍历状态队列: -| queued update | `n` | returns | +| 更新队列 | `n` | 返回值 | |--------------|---------|-----| -| "replace with `5`" | `0` (unused) | `5` | +| “替换为 `5`” | `0`(未使用) | `5` | | `n => n + 1` | `5` | `5 + 1 = 6` | -| "replace with `42`" | `6` (unused) | `42` | +| “替换为 `42`” | `6`(未使用) | `42` | -Then React stores `42` as the final result and returns it from `useState`. +然后 React 保存 `42` 为最终结果并从 `useState` 中返回。 -To summarize, here's how you can think of what you're passing to the `setNumber` state setter: +总而言之,以下是你可以考虑传递给 `setNumber` state setter 的内容: -* **An updater function** (e.g. `n => n + 1`) gets added to the queue. -* **Any other value** (e.g. number `5`) adds "replace with `5`" to the queue, ignoring what's already queued. +* **一个更新函数**(例如:`n => n + 1`)被添加到队列中。 +* **任何其他值**(例如:数字 `5`)将“替换为 `5`”添加到队列中,忽略已经在队列中的内容。 -After the event handler completes, React will trigger a re-render. During the re-render, React will process the queue. Updater functions run during rendering, so **updater functions must be [pure](/learn/keeping-components-pure)** and only *return* the result. Don't try to set state from inside of them or run other side effects. In Strict Mode, React will run each updater function twice (but discard the second result) to help you find mistakes. +事件处理函数执行完成后,React 将触发重新渲染。在重新渲染期间,React 将处理队列。更新函数在渲染期间执行,因此 **更新函数必须是 [纯函数](/learn/keeping-components-pure)** 并且只 *返回* 结果。不要尝试从它们内部设置状态或者执行其他副作用。在严格模式下,React 会执行每个更新函数两次(但是丢弃第二个结果)以帮助你发现错误。 -### Naming conventions {/*naming-conventions*/} +### 命名约定 {/*naming-conventions*/} -It's common to name the updater function argument by the first letters of the corresponding state variable: +通常通过相应状态变量的第一个字母来命名更新函数参数: ```js setEnabled(e => !e); @@ -254,13 +256,13 @@ setLastName(ln => ln.reverse()); setFriendCount(fc => fc * 2); ``` -If you prefer more verbose code, another common convention is to repeat the full state variable name, like `setEnabled(enabled => !enabled)`, or to use a prefix like `setEnabled(prevEnabled => !prevEnabled)`. +如果你喜欢更冗长的代码,另一个常见的约定是重复完整的状态变量名称,如 `setEnabled(enabled => !enabled)`,或使用前缀如 `setEnabled(prevEnabled => !prevEnabled)`。 -* Setting state does not change the variable in the existing render, but it requests a new render. -* React processes state updates after event handlers have finished running. This is called batching. -* To update some state multiple times in one event, you can use `setNumber(n => n + 1)` updater function. +* 设置状态不会更改现有渲染中的变量,但会请求新的渲染。 +* React 在事件处理函数执行完成之后处理状态更新。这称为批处理。 +* 要在一个事件中多次更新某些状态,你可以使用 `setNumber(n => n + 1)` 更新函数。 @@ -268,13 +270,13 @@ If you prefer more verbose code, another common convention is to repeat the full -### Fix a request counter {/*fix-a-request-counter*/} +### 修复请求计数器 {/*fix-a-request-counter*/} -You're working on an art marketplace app that lets the user submit multiple orders for an art item at the same time. Each time the user presses the "Buy" button, the "Pending" counter should increase by one. After three seconds, the "Pending" counter should decrease, and the "Completed" counter should increase. +你正在开发一个艺术市场应用,该应用允许用户同时提交一个艺术项目的多个订单。每次用户按下“购买”按钮,“等待”计数器应增加一。三秒后,“等待”计数器应该减少,“完成”计数器应该增加。 -However, the "Pending" counter does not behave as intended. When you press "Buy," it decreases to `-1` (which should not be possible!). And if you click fast twice, both counters seem to behave unpredictably. +但是,“等待”计数器的行为不符合预期。当你按下“购买”按钮时,它会减少到 `-1`(这应该是不可能的)。如果你快速点击两次,两个计数器的行为似乎都无法预测。 -Why does this happen? Fix both counters. +为什么会发生这种情况?修复两个计数器。 @@ -295,13 +297,13 @@ export default function RequestTracker() { return ( <>

- Pending: {pending} + 等待:{pending}

- Completed: {completed} + 完成:{completed}

); @@ -318,7 +320,7 @@ function delay(ms) { -Inside the `handleClick` event handler, the values of `pending` and `completed` correspond to what they were at the time of the click event. For the first render, `pending` was `0`, so `setPending(pending - 1)` becomes `setPending(-1)`, which is wrong. Since you want to *increment* or *decrement* the counters, rather than set them to a concrete value determined during the click, you can instead pass the updater functions: +在 `handleClick` 事件处理函数内部,`pending` 和 `completed` 的值对应于点击事件发生时的值。对于第一次渲染,`pending` 为 `0` ,因此 `setPending(pending - 1)` 变成了 `setPending(-1)`,这是错误的。由于你想要 *增加* 或 *减少* 计数器,而不是将它们设置为在点击期间确定的具体值,你可以改为传递更新函数: @@ -339,13 +341,13 @@ export default function RequestTracker() { return ( <>

- Pending: {pending} + 等待:{pending}

- Completed: {completed} + 完成:{completed}

); @@ -360,23 +362,23 @@ function delay(ms) {
-This ensures that when you increment or decrement a counter, you do it in relation to its *latest* state rather than what the state was at the time of the click. +这可以确保当你增加或减少计数器时,会根据其 *最新* 状态而不是点击时的状态来执行此操作。
-### Implement the state queue yourself {/*implement-the-state-queue-yourself*/} +### 自己实现状态队列 {/*implement-the-state-queue-yourself*/} -In this challenge, you will reimplement a tiny part of React from scratch! It's not as hard as it sounds. +在这个挑战中,你将从头开始重新实现 React 的一小部分!这并不像听起来那么难。 -Scroll through the sandbox preview. Notice that it shows **four test cases**. They correspond to the examples you've seen earlier on this page. Your task is to implement the `getFinalState` function so that it returns the correct result for each of those cases. If you implement it correctly, all four tests should pass. +滚动 sandbox 进行预览。请注意,它显示了 **四个测试用例** 。它们对应于你之前在本页上看到的示例。你的任务是实现 `getFinalState` 函数,以便它为每种情况返回正确的结果。如果你正确实现它,则所有四个测试用例都应该通过。 -You will receive two arguments: `baseState` is the initial state (like `0`), and the `queue` is an array which contains a mix of numbers (like `5`) and updater functions (like `n => n + 1`) in the order they were added. +你将收到两个参数:`baseState` 是初始状态(例如:`0`),`queue` 是包含数字(例如:`5`)和更新函数(例如:`n => n + 1`)的混合数组,按添加顺序排列。 -Your task is to return the final state, just like the tables on this page show! +你的任务是返回最终状态,就像这个页面上的表格一样! -If you're feeling stuck, start with this code structure: +如果你没有思路,可以从下面的代码结构开始: ```js export function getFinalState(baseState, queue) { @@ -384,9 +386,9 @@ export function getFinalState(baseState, queue) { for (let update of queue) { if (typeof update === 'function') { - // TODO: apply the updater function + // TODO: 调用更新函数 } else { - // TODO: replace the state + // TODO: 替换状态 } } @@ -394,7 +396,7 @@ export function getFinalState(baseState, queue) { } ``` -Fill out the missing lines! +完成剩余的代码! @@ -404,7 +406,7 @@ Fill out the missing lines! export function getFinalState(baseState, queue) { let finalState = baseState; - // TODO: do something with the queue... + // TODO: 对队列做些什么... return finalState; } @@ -467,19 +469,19 @@ function TestCase({ const actual = getFinalState(baseState, queue); return ( <> -

Base state: {baseState}

-

Queue: [{queue.join(', ')}]

-

Expected result: {expected}

+

初始状态:{baseState}

+

队列:[{queue.join(', ')}]

+

期望结果:{expected}

- Your result: {actual} + 你的结果:{actual} {' '} ({actual === expected ? - 'correct' : - 'wrong' + '正确' : + '错误' })

@@ -491,7 +493,7 @@ function TestCase({ -This is the exact algorithm described on this page that React uses to calculate the final state: +本页描述的正是 React 用来计算最终状态的算法: @@ -501,10 +503,10 @@ export function getFinalState(baseState, queue) { for (let update of queue) { if (typeof update === 'function') { - // Apply the updater function. + // 调用更新函数 finalState = update(finalState); } else { - // Replace the next state. + // 替换状态 finalState = update; } } @@ -570,19 +572,19 @@ function TestCase({ const actual = getFinalState(baseState, queue); return ( <> -

Base state: {baseState}

-

Queue: [{queue.join(', ')}]

-

Expected result: {expected}

+

初始状态:{baseState}

+

队列:[{queue.join(', ')}]

+

期望结果:{expected}

- Your result: {actual} + 你的结果:{actual} {' '} ({actual === expected ? - 'correct' : - 'wrong' + '正确' : + '错误' })

@@ -592,7 +594,7 @@ function TestCase({
-Now you know how this part of React works! +现在你知道 React 这部分是如何工作的了!
From 448c33922c4d6cfa213e6ca04b49f890ae2dfe1c Mon Sep 17 00:00:00 2001 From: Jiacheng Dong <43509711+Jiacheng787@users.noreply.github.com> Date: Mon, 27 Dec 2021 09:42:55 +0800 Subject: [PATCH 2/3] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hao Jiang ᛋ <22409868+Neo42@users.noreply.github.com> --- .../queueing-a-series-of-state-updates.md | 113 +++++++++--------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/beta/src/pages/learn/queueing-a-series-of-state-updates.md b/beta/src/pages/learn/queueing-a-series-of-state-updates.md index f8abc81cf4..dbace90d1d 100644 --- a/beta/src/pages/learn/queueing-a-series-of-state-updates.md +++ b/beta/src/pages/learn/queueing-a-series-of-state-updates.md @@ -1,7 +1,8 @@ --- -title: 状态更新的队列 +title: 把一系列 state 更新加入队列 translators: - Jiacheng787 + - Neo42 --- @@ -12,14 +13,14 @@ translators: -* 什么是“批处理”以及 React 如何使用它来处理多个状态更新 -* 如何通过一行代码对同一状态变量进行多次更新 +* 什么是“批处理”以及 React 如何使用它来处理多个 state 更新 +* 如何连续多次对同一 state 变量进行更新 -## React 批量状态更新 {/*react-batches-state-updates*/} +## React 会对 state 更新进行批处理 {/*react-batches-state-updates*/} -在下面的示例中,你可能会认为,点击“+3”按钮会使计数器增加三次,因为它调用了 `setNumber(number + 1)` 三次: +在下面的示例中,你可能会认为点击 “+3” 按钮会使计数器递增三次,因为它调用了 `setNumber(number + 1)` 三次: @@ -49,7 +50,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -但是,你可能还记得上一节中的内容,[每一次渲染的状态值都是固定的](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time),因此无论你调用多少次 `setNumber(1)`,在第一次渲染的事件处理函数内部的 `number` 值总是 `0` : +但是,你可能还记得上一节中的内容,[每一次渲染的 state 值都是固定的](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time),因此无论你调用多少次 `setNumber(1)`,在第一次渲染的事件处理函数内部的 `number` 值总是 `0` : ```js setNumber(0 + 1); @@ -57,21 +58,21 @@ setNumber(0 + 1); setNumber(0 + 1); ``` -但是这里还有另外一个因素需要讨论。**在处理状态更新之前,React 会等待事件处理函数中的 *所有* 代码都运行完毕。** 这就是为什么重新渲染只在所有这些 `setNumber()` 调用 *之后* 发生的原因。 +但是这里还有另外一个影响因素需要讨论。**React 会等到事件处理函数中的 *所有* 代码都运行完毕再处理你的 state 更新。** 这就是为什么重新渲染只会发生在所有这些 `setNumber()` 调用 *之后* 的原因。 -这可能会让你想起在餐厅点菜的服务员。服务员不会在提到你的第一道菜的时候就跑到厨房!相反,他们会让你完成订单,让你对其进行更改,甚至可以接受桌上其他人的订单。 +这可能会让你想起餐厅里帮你点菜的服务员。服务员不会在你说第一道菜的时候就跑到厨房!相反,他们会让你把菜点完,让你修改菜品,甚至会帮桌上的其他人点菜。 - + -这让你可以更新多个状态——甚至来自多个组件——而不会触发太多的 [重新渲染](/learn/render-and-commit#re-renders-when-state-updates)。但这也意味着只有在你的事件处理函数及其中任何代码完成 *之后*,UI 才会更新。这种行为,被称为 **批处理,** 使你的 React app 运行更快。它还避免了处理只​​更新了一些变量的令人困惑的“半成品”渲染。 +这让你可以更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的 [重新渲染](/learn/render-and-commit#re-renders-when-state-updates)。但这也意味着只有在你的事件处理函数及其中任何代码执行完成 *之后*,UI 才会更新。这种特性也就是 **批处理**,它会使你的 React 应用运行得更快。它还会帮你避免处理只​​更新了一部分 state 变量的令人困惑的“半成品”渲染。 -**React 不会跨 *多个* 有意的事件(如点击)进行批处理**——每次点击都是单独处理的。请放心,React 仅在通常安全的情况下才进行批处理。这确保了,例如,如果第一次点击按钮禁用了表单,则第二次点击不会再次提交它。 +**React 不会跨 *多个* 需要刻意触发的事件(如点击)进行批处理**——每次点击都是单独处理的。请放心,React 只会在一般来说安全的情况下才进行批处理。这可以确保,例如,如果第一次点击按钮会禁用表单,那么第二次点击就不会再次提交它。 -## 在下次渲染前多次更新同一个状态 {/*updating-the-same-state-variable-multiple-times-before-the-next-render*/} +## 在下次渲染前多次更新同一个 state 变量 {/*updating-the-same-state-variable-multiple-times-before-the-next-render*/} -这是一个不常见的用例,但是如果你想在下次渲染之前多次更新同一个状态,而不是传递 *下一个状态值* 例如 `setNumber(number + 1)`,你可以传递一个 *函数*,该函数根据队列中的前一个状态计算下一个状态,例如 `setNumber(n => n + 1)`。这是一种告诉 React “用状态值做某事”而不是仅仅替换它的方法。 +这是一个不常见的用例,但是如果你想在下次渲染之前多次更新同一个 state,你可以像 `setNumber(n => n + 1)` 这样传入一个根据队列中的前一个 state 计算下一个 state 的 *函数*,而不是像 `setNumber(number + 1)` 这样传入 *下一个 state 值*。这是一种告诉 React “用 state 值做某事”而不是仅仅替换它的方法。 -现在尝试增加计数器: +现在尝试递增计数器: @@ -101,10 +102,10 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -在这里,`n => n + 1` 被称为 **更新函数**。当你将它传递给 state setter 时: +在这里,`n => n + 1` 被称为 **更新函数**。当你将它传递给一个 state 设置函数时: -1. React 将此函数加入队列,在事件处理函数中的所有其他代码运行后进行处理。 -2. 在下一次渲染期间,React 遍历队列并提供最终更新状态。 +1. React 会将此函数加入队列,以便在事件处理函数中的所有其他代码运行后进行处理。 +2. 在下一次渲染期间,React 会遍历队列并给你更新之后的最终 state。 ```js setNumber(n => n + 1); @@ -112,13 +113,13 @@ setNumber(n => n + 1); setNumber(n => n + 1); ``` -下面是 React 在执行事件处理函数时的处理流程: +下面是 React 在执行事件处理函数时处理这几行代码的过程: 1. `setNumber(n => n + 1)`:`n => n + 1` 是一个函数。React 将它加入队列。 2. `setNumber(n => n + 1)`:`n => n + 1` 是一个函数。React 将它加入队列。 3. `setNumber(n => n + 1)`:`n => n + 1` 是一个函数。React 将它加入队列。 -当你在下次渲染期间调用 `useState`,React 会遍历队列。初始的 `number` 状态是 `0`,所以这就是 React 作为 `n` 参数传递给第一个更新函数的值。然后 React 获取上一个更新函数的返回值,并将其作为 `n` 传递给下一个更新函数,以此类推: +当你在下次渲染期间调用 `useState` 时,React 会遍历队列。之前的 `number` state 的值是 `0`,所以这就是 React 作为参数 `n` 传递给第一个更新函数的值。然后 React 会获取你上一个更新函数的返回值,并将其作为 `n` 传递给下一个更新函数,以此类推: | 更新队列 | `n` | 返回值 | |--------------|---------|-----| @@ -126,12 +127,12 @@ setNumber(n => n + 1); | `n => n + 1` | `1` | `1 + 1 = 2` | | `n => n + 1` | `2` | `2 + 1 = 3` | -React 保存 `3` 为最终结果并从 `useState` 中返回。 +React 会保存 `3` 为最终结果并从 `useState` 中返回。 这就是为什么在上面的示例中点击“+3”正确地将值增加“+3”。 -### 如果在替换状态后更新状态会发生什么 {/*what-happens-if-you-update-state-after-replacing-it*/} +### 如果你在替换 state 后更新 state 会发生什么 {/*what-happens-if-you-update-state-after-replacing-it*/} -这个事件处理函数会怎么样?你认为 `number` 下一次渲染的值是什么? +这个事件处理函数会怎么样?你认为 `number` 在下一次渲染中的值是什么? ```js