diff --git a/src/content/reference/react/useReducer.md b/src/content/reference/react/useReducer.md index 4f8885e045..0d9f963f21 100644 --- a/src/content/reference/react/useReducer.md +++ b/src/content/reference/react/useReducer.md @@ -4,7 +4,7 @@ title: useReducer -`useReducer` is a React Hook that lets you add a [reducer](/learn/extracting-state-logic-into-a-reducer) to your component. +`useReducer` 是一个 React Hook,它允许你向组件里面添加一个 [reducer](/learn/extracting-state-logic-into-a-reducer)。 ```js const [state, dispatch] = useReducer(reducer, initialArg, init?) @@ -16,11 +16,11 @@ const [state, dispatch] = useReducer(reducer, initialArg, init?) --- -## Reference {/*reference*/} +## 参考 {/*reference*/} ### `useReducer(reducer, initialArg, init?)` {/*usereducer*/} -Call `useReducer` at the top level of your component to manage its state with a [reducer.](/learn/extracting-state-logic-into-a-reducer) +在组件的顶层作用域调用 `useReducer` 以创建一个用于管理状态的 [reducer](/learn/extracting-state-logic-into-a-reducer)。 ```js import { useReducer } from 'react'; @@ -34,31 +34,31 @@ function MyComponent() { // ... ``` -[See more examples below.](#usage) +[参见下方更多示例](#usage)。 -#### Parameters {/*parameters*/} +#### 参数 {/*parameters*/} -* `reducer`: The reducer function that specifies how the state gets updated. It must be pure, should take the state and action as arguments, and should return the next state. State and action can be of any types. -* `initialArg`: The value from which the initial state is calculated. It can be a value of any type. How the initial state is calculated from it depends on the next `init` argument. -* **optional** `init`: The initializer function that should return the initial state. If it's not specified, the initial state is set to `initialArg`. Otherwise, the initial state is set to the result of calling `init(initialArg)`. +* `reducer`:用于更新 state 的纯函数。参数为 state 和 action,返回值是更新后的 state。state 与 action 可以是任意合法值。 +* `initialArg`:用于初始化 state 的任意值。初始值的计算逻辑取决于接下来的 `init` 参数。 +* **可选参数** `init`:用于计算初始值的函数。如果存在,使用 `init(initialArg)` 的执行结果作为初始值,否则使用 `initialArg`。 -#### Returns {/*returns*/} +#### 返回值 {/*returns*/} -`useReducer` returns an array with exactly two values: +`useReducer` 返回一个由两个值组成的数组: -1. The current state. During the first render, it's set to `init(initialArg)` or `initialArg` (if there's no `init`). -2. The [`dispatch` function](#dispatch) that lets you update the state to a different value and trigger a re-render. +1. 当前的 state。初次渲染时,它是 `init(initialArg)` 或 `initialArg` (如果没有 `init` 函数)。 +2. [`dispatch` 函数](#dispatch)。用于更新 state 并触发组件的重新渲染。 -#### Caveats {/*caveats*/} +#### 注意事项 {/*caveats*/} -* `useReducer` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. -* In Strict Mode, React will **call your reducer and initializer twice** in order to [help you find accidental impurities.](#my-reducer-or-initializer-function-runs-twice) This is development-only behavior and does not affect production. If your reducer and initializer are pure (as they should be), this should not affect your logic. The result from one of the calls is ignored. +* `useReducer` 是一个 Hook,所以只能在 **组件的顶层作用域** 或自定义 Hook 中调用,而不能在循环或条件语句中调用。如果你有这种需求,可以创建一个新的组件,并把 state 移入其中。 +* 严格模式下 React 会 **调用两次 reducer 和初始化函数**,这可以 [帮助你发现意外的副作用](#my-reducer-or-initializer-function-runs-twice)。这只是开发模式下的行为,并不会影响生产环境。只要 reducer 和初始化函数是纯函数(理应如此)就不会改变你的逻辑。其中一个调用结果会被忽略。 --- -### `dispatch` function {/*dispatch*/} +### `dispatch` 函数 {/*dispatch*/} -The `dispatch` function returned by `useReducer` lets you update the state to a different value and trigger a re-render. You need to pass the action as the only argument to the `dispatch` function: +`useReducer` 返回的 `dispatch` 函数允许你更新 state 并触发组件的重新渲染。它需要传入一个 action 作为参数: ```js const [state, dispatch] = useReducer(reducer, { age: 42 }); @@ -68,31 +68,31 @@ function handleClick() { // ... ``` -React will set the next state to the result of calling the `reducer` function you've provided with the current `state` and the action you've passed to `dispatch`. +React 会调用 `reducer` 函数以更新 state,`reducer` 函数的参数为当前的 state 与传递的 action。 -#### Parameters {/*dispatch-parameters*/} +#### 参数 {/*dispatch-parameters*/} -* `action`: The action performed by the user. It can be a value of any type. By convention, an action is usually an object with a `type` property identifying it and, optionally, other properties with additional information. +* `action`:用户执行的操作。可以是任意类型的值。通常来说 action 是一个对象,其中 `type` 属性标识类型,其它属性携带额外信息。 -#### Returns {/*dispatch-returns*/} +#### 返回值 {/*dispatch-returns*/} -`dispatch` functions do not have a return value. +`dispatch` 函数没有返回值。 -#### Caveats {/*setstate-caveats*/} +#### 注意 {/*setstate-caveats*/} -* The `dispatch` function **only updates the state variable for the *next* render**. If you read the state variable after calling the `dispatch` function, [you will still get the old value](#ive-dispatched-an-action-but-logging-gives-me-the-old-state-value) that was on the screen before your call. +* `dispatch` 函数 **是为下一次渲染而更新 state**。因此在调用 `dispatch` 函数后读取 state [并不会拿到更新后的值](#ive-dispatched-an-action-but-logging-gives-me-the-old-state-value),也就是说只能获取到调用前的值。 -* If the new value you provide is identical to the current `state`, as determined by an [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison, React will **skip re-rendering the component and its children.** This is an optimization. React may still need to call your component before ignoring the result, but it shouldn't affect your code. +* 如果你提供的新值与当前的 `state` 相同(使用 [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) 比较),React 会 **跳过组件和子组件的重新渲染**,这是一种优化手段。虽然在跳过重新渲染前 React 可能会调用你的组件,但是这不应该影响你的代码。 -* React [batches state updates.](/learn/queueing-a-series-of-state-updates) It updates the screen **after all the event handlers have run** and have called their `set` functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use [`flushSync`.](/reference/react-dom/flushSync) +* React [会批量更新 state](/learn/queueing-a-series-of-state-updates)。state 会在 **所有事件函数执行完毕** 并且已经调用过它的 `set` 函数后进行更新,这可以防止在一个事件中多次进行重新渲染。如果在访问 DOM 等极少数情况下需要强制 React 提前更新,可以使用 [`flushSync`](/reference/react-dom/flushSync)。 --- -## Usage {/*usage*/} +## 用法 {/*usage*/} -### Adding a reducer to a component {/*adding-a-reducer-to-a-component*/} +### 向组件添加 reducer {/*adding-a-reducer-to-a-component*/} -Call `useReducer` at the top level of your component to manage state with a [reducer.](/learn/extracting-state-logic-into-a-reducer) +在组件的顶层作用域调用 `useReducer` 来创建一个用于管理状态(state)的 [reducer](/learn/extracting-state-logic-into-a-reducer)。 ```js [[1, 8, "state"], [2, 8, "dispatch"], [4, 8, "reducer"], [3, 8, "{ age: 42 }"]] import { useReducer } from 'react'; @@ -106,12 +106,12 @@ function MyComponent() { // ... ``` -`useReducer` returns an array with exactly two items: +`useReducer` 返回一个由两个值组成的数组: -1. The current state of this state variable, initially set to the initial state you provided. -2. The `dispatch` function that lets you change it in response to interaction. +1. 当前的 state,首次渲染时为你提供的 初始值。 +2. `dispatch` 函数,让你可以根据交互修改 state。 -To update what's on the screen, call `dispatch` with an object representing what the user did, called an *action*: +为了更新屏幕上的内容,使用一个表示用户操作的 action 来调用 `dispatch` 函数: ```js [[2, 2, "dispatch"]] function handleClick() { @@ -119,7 +119,7 @@ function handleClick() { } ``` -React will pass the current state and the action to your reducer function. Your reducer will calculate and return the next state. React will store that next state, render your component with it, and update the UI. +React 会把当前的 state 和这个 action 一起作为参数传给 reducer 函数,然后 reducer 计算并返回新的 state,最后 React 保存新的 state,并使用它渲染组件和更新 UI。 @@ -157,13 +157,13 @@ button { display: block; margin-top: 10px; } -`useReducer` is very similar to [`useState`](/reference/react/useState), but it lets you move the state update logic from event handlers into a single function outside of your component. Read more about [choosing between `useState` and `useReducer`.](/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer) +`useReducer` 和 [`useState`](/reference/react/useState) 非常相似,但是它可以让你把状态更新逻辑从事件处理函数中移动到组件外部。详情可以参阅 [对比 `useState` 和 `useReducer`](/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer)。 --- -### Writing the reducer function {/*writing-the-reducer-function*/} +### 实现 reducer 函数 {/*writing-the-reducer-function*/} -A reducer function is declared like this: +reducer 函数的定义如下: ```js function reducer(state, action) { @@ -171,7 +171,7 @@ function reducer(state, action) { } ``` -Then you need to fill in the code that will calculate and return the next state. By convention, it is common to write it as a [`switch` statement.](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/switch) For each `case` in the `switch`, calculate and return some next state. +你需要往函数体里面添加计算并返回新的 state 的逻辑。一般会使用 [`switch` 语句](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/switch) 来完成。在 `switch` 语句中通过匹配 `case` 条件来计算并返回相应的 state。 ```js {4-7,10-13} function reducer(state, action) { @@ -193,7 +193,7 @@ function reducer(state, action) { } ``` -Actions can have any shape. By convention, it's common to pass objects with a `type` property identifying the action. It should include the minimal necessary information that the reducer needs to compute the next state. +action 可以是任意类型,不过通常至少是一个存在 `type` 属性的对象。也就是说它需要携带计算新的 state 值所必须的数据。 ```js {5,9-12} function Form() { @@ -212,31 +212,31 @@ function Form() { // ... ``` -The action type names are local to your component. [Each action describes a single interaction, even if that leads to multiple changes in data.](/learn/extracting-state-logic-into-a-reducer#writing-reducers-well) The shape of the state is arbitrary, but usually it'll be an object or an array. +action 的 type 依赖于组件的实际情况。[即使它会导致数据的多次更新,每个 action 都只描述一次交互](/learn/extracting-state-logic-into-a-reducer#writing-reducers-well)。state 的类型也是任意的,不过一般会使用对象或数组。 -Read [extracting state logic into a reducer](/learn/extracting-state-logic-into-a-reducer) to learn more. +阅读 [迁移状态逻辑至 Reducer 中](/learn/extracting-state-logic-into-a-reducer) 来了解更多内容。 -State is read-only. Don't modify any objects or arrays in state: +state 是只读的。即使是对象或数组也不要尝试修改它: ```js {4,5} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // 🚩 Don't mutate an object in state like this: + // 🚩 不要像下面这样修改一个对象类型的 state: state.age = state.age + 1; return state; } ``` -Instead, always return new objects from your reducer: +正确的做法是返回新的对象: ```js {4-8} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // ✅ Instead, return a new object + // ✅ 正确的做法是返回新的对象 return { ...state, age: state.age + 1 @@ -244,15 +244,15 @@ function reducer(state, action) { } ``` -Read [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) to learn more. +阅读 [更新对象类型的 state](/learn/updating-objects-in-state) 和 [更新数组类型的 state](/learn/updating-arrays-in-state) 来了解更多内容。 - + -#### Form (object) {/*form-object*/} +#### 表单(对象类型) {/*form-object*/} -In this example, the reducer manages a state object with two fields: `name` and `age`. +在这个示例中,state 是一个有 `name` 和 `age` 属性的对象。 @@ -316,9 +316,9 @@ button { display: block; margin-top: 10px; } -#### Todo list (array) {/*todo-list-array*/} +#### 代办事项(数组类型) {/*todo-list-array*/} -In this example, the reducer manages an array of tasks. The array needs to be updated [without mutation.](/learn/updating-arrays-in-state) +在这个示例中,reducer 管理一个名为 tasks 的数组。数组 [不能使用修改方法](/learn/updating-arrays-in-state) 来更新。 @@ -509,9 +509,9 @@ ul, li { margin: 0; padding: 0; } -#### Writing concise update logic with Immer {/*writing-concise-update-logic-with-immer*/} +#### 使用 Immer 编写简洁的更新逻辑 {/*writing-concise-update-logic-with-immer*/} -If updating arrays and objects without mutation feels tedious, you can use a library like [Immer](https://github.com/immerjs/use-immer#useimmerreducer) to reduce repetitive code. Immer lets you write concise code as if you were mutating objects, but under the hood it performs immutable updates: +如果使用复制方法更新数组和对象让你不厌其烦,那么可以使用 [Immer](https://github.com/immerjs/use-immer#useimmerreducer) 这样的库来减少一些重复的样板代码。Immer 让你可以专注于逻辑,因为它在内部均使用复制方法来完成更新: @@ -723,9 +723,9 @@ ul, li { margin: 0; padding: 0; } --- -### Avoiding recreating the initial state {/*avoiding-recreating-the-initial-state*/} +### 避免重新创建初始值 {/*avoiding-recreating-the-initial-state*/} -React saves the initial state once and ignores it on the next renders. +React 会保存 state 的初始值并在下一次渲染时忽略它。 ```js function createInitialState(username) { @@ -737,9 +737,9 @@ function TodoList({ username }) { // ... ``` -Although the result of `createInitialState(username)` is only used for the initial render, you're still calling this function on every render. This can be wasteful if it's creating large arrays or performing expensive calculations. +虽然 `createInitialState(username)` 的返回值只用于初次渲染,但是在每一次渲染的时候都会被调用。如果它创建了比较大的数组或者执行了昂贵的计算就会浪费性能。 -To solve this, you may **pass it as an _initializer_ function** to `useReducer` as the third argument instead: +你可以通过给 `useReducer` 的第三个参数传入 **初始化函数** 来解决这个问题: ```js {6} function createInitialState(username) { @@ -751,15 +751,15 @@ function TodoList({ username }) { // ... ``` -Notice that you’re passing `createInitialState`, which is the *function itself*, and not `createInitialState()`, which is the result of calling it. This way, the initial state does not get re-created after initialization. +需要注意的是你传入的参数是 `createInitialState` 这个 **函数自身**,而不是执行 `createInitialState()` 后的返回值。这样传参就可以保证初始化函数不会再次运行。 -In the above example, `createInitialState` takes a `username` argument. If your initializer doesn't need any information to compute the initial state, you may pass `null` as the second argument to `useReducer`. +在上面这个例子中,`createInitialState` 有一个 `username` 参数。如果初始化函数不需要参数就可以计算出初始值,可以把 `useReducer` 的第二个参数改为 `null`。 - + -#### Passing the initializer function {/*passing-the-initializer-function*/} +#### 使用初始化函数 {/*passing-the-initializer-function*/} -This example passes the initializer function, so the `createInitialState` function only runs during initialization. It does not run when component re-renders, such as when you type into the input. +这个示例使用了一个初始化函数,所以 `createInitialState` 函数只会在初次渲染的时候进行调用。即使往输入框中输入内容导致组件重新渲染,初始化函数也不会被再次调用。 @@ -845,9 +845,9 @@ export default function TodoList({ username }) { -#### Passing the initial state directly {/*passing-the-initial-state-directly*/} +#### 直接传入初始值 {/*passing-the-initial-state-directly*/} -This example **does not** pass the initializer function, so the `createInitialState` function runs on every render, such as when you type into the input. There is no observable difference in behavior, but this code is less efficient. +这个示例 **没有使用** 初始化函数,所以当你往输入框输入内容导致组件重新渲染的时候,`createInitialState` 函数就会执行。虽然在渲染结果上看没有什么区别,但是多余的逻辑会导致性能变差。 @@ -936,28 +936,28 @@ export default function TodoList({ username }) { --- -## Troubleshooting {/*troubleshooting*/} +## 疑难解答 {/*troubleshooting*/} -### I've dispatched an action, but logging gives me the old state value {/*ive-dispatched-an-action-but-logging-gives-me-the-old-state-value*/} +### 我已经 dispatch 了一个 action,但是打印出来仍然还是旧的 state {/*ive-dispatched-an-action-but-logging-gives-me-the-old-state-value*/} -Calling the `dispatch` function **does not change state in the running code**: +调用 `dispatch` 函数 **不会改变当前渲染的 state**: ```js {4,5,8} function handleClick() { console.log(state.age); // 42 - dispatch({ type: 'incremented_age' }); // Request a re-render with 43 - console.log(state.age); // Still 42! + dispatch({ type: 'incremented_age' }); // 用 43 进行重新渲染 + console.log(state.age); // 还是 42! setTimeout(() => { - console.log(state.age); // Also 42! + console.log(state.age); // 一样是 42! }, 5000); } ``` -This is because [states behaves like a snapshot.](/learn/state-as-a-snapshot) Updating state requests another render with the new state value, but does not affect the `state` JavaScript variable in your already-running event handler. +这是因为 [state 的行为和快照一样](/learn/state-as-a-snapshot)。更新 state 会使用新的值来对组件进行重新渲染,但是不会改变当前执行的事件处理函数里面 `state` 的值。 -If you need to guess the next state value, you can calculate it manually by calling the reducer yourself: +如果你需要获取更新后的 state,可以手动调用 reducer 来得到结果: ```js const action = { type: 'incremented_age' }; @@ -970,20 +970,20 @@ console.log(nextState); // { age: 43 } --- -### I've dispatched an action, but the screen doesn't update {/*ive-dispatched-an-action-but-the-screen-doesnt-update*/} +### 我已经 dispatch 了一个 action,但是屏幕并没有更新 {/*ive-dispatched-an-action-but-the-screen-doesnt-update*/} -React will **ignore your update if the next state is equal to the previous state,** as determined by an [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. This usually happens when you change an object or an array in state directly: +React 使用 [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) 比较更新前后的 state,如果 **它们相等就会跳过这次更新**。这通常是因为你直接修改了对象或数组: ```js {4-5,9-10} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // 🚩 Wrong: mutating existing object + // 🚩 错误行为:直接修改对象 state.age++; return state; } case 'changed_name': { - // 🚩 Wrong: mutating existing object + // 🚩 错误行为:直接修改对象 state.name = action.nextName; return state; } @@ -992,20 +992,20 @@ function reducer(state, action) { } ``` -You mutated an existing `state` object and returned it, so React ignored the update. To fix this, you need to ensure that you're always [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) instead of mutating them: +你直接修改并返回了一个 `state` 对象,所以 React 会跳过这次更新。为了修复这个错误,你应该确保总是 [使用正确的方式更新对象](/learn/updating-objects-in-state) 和 [使用正确的方式更新数组](/learn/updating-arrays-in-state): ```js {4-8,11-15} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // ✅ Correct: creating a new object + // ✅ 修复:创建一个新的对象 return { ...state, age: state.age + 1 }; } case 'changed_name': { - // ✅ Correct: creating a new object + // ✅ 修复:创建一个新的对象 return { ...state, name: action.nextName @@ -1018,29 +1018,29 @@ function reducer(state, action) { --- -### A part of my reducer state becomes undefined after dispatching {/*a-part-of-my-reducer-state-becomes-undefined-after-dispatching*/} +### 在 dispatch 后 state 的某些属性变为了 `undefined` {/*a-part-of-my-reducer-state-becomes-undefined-after-dispatching*/} -Make sure that every `case` branch **copies all of the existing fields** when returning the new state: +请确保每个 `case` 语句中所返回的新的 state **都复制了当前的属性**: ```js {5} function reducer(state, action) { switch (action.type) { case 'incremented_age': { return { - ...state, // Don't forget this! + ...state, // 不要忘记复制之前的属性! age: state.age + 1 }; } // ... ``` -Without `...state` above, the returned next state would only contain the `age` field and nothing else. +如果上面的代码没有 `...state` ,返回的新的 state 就只有 `age` 属性。 --- -### My entire reducer state becomes undefined after dispatching {/*my-entire-reducer-state-becomes-undefined-after-dispatching*/} +### 在 dispatch 后整个 state 都变为了 `undefined` {/*my-entire-reducer-state-becomes-undefined-after-dispatching*/} -If your state unexpectedly becomes `undefined`, you're likely forgetting to `return` state in one of the cases, or your action type doesn't match any of the `case` statements. To find why, throw an error outside the `switch`: +如果你的 state 错误地变成了 `undefined`,可能是因为你忘记在某个分支返回 state,或者是你遗漏了某些 `case` 分支。可以通过在 `switch` 语句之后抛出一个错误来查找原因: ```js {10} function reducer(state, action) { @@ -1056,42 +1056,42 @@ function reducer(state, action) { } ``` -You can also use a static type checker like TypeScript to catch such mistakes. +也可以通过使用 TypeScript 等静态检查工具来发现这类错误。 --- -### I'm getting an error: "Too many re-renders" {/*im-getting-an-error-too-many-re-renders*/} +### 我收到了一个报错:“Too many re-renders” {/*im-getting-an-error-too-many-re-renders*/} -You might get an error that says: `Too many re-renders. React limits the number of renders to prevent an infinite loop.` Typically, this means that you're unconditionally dispatching an action *during render*, so your component enters a loop: render, dispatch (which causes a render), render, dispatch (which causes a render), and so on. Very often, this is caused by a mistake in specifying an event handler: +你可能会收到这样一条报错信息:`Too many re-renders. React limits the number of renders to prevent an infinite loop.`。这通常是在 **渲染期间** dispatch 了 action 而导致组件进入了无限循环:dispatch(会导致一次重新渲染)、渲染、dispatch(再次导致重新渲染),然后无限循环。大多数这样的错误是由于事件处理函数中存在错误的逻辑: ```js {1-2} -// 🚩 Wrong: calls the handler during render +// 🚩 错误:渲染期间调用了处理函数 return -// ✅ Correct: passes down the event handler +// ✅ 修复:传递一个处理函数,而不是调用 return -// ✅ Correct: passes down an inline function +// ✅ 修复:传递一个内联的箭头函数 return ``` -If you can't find the cause of this error, click on the arrow next to the error in the console and look through the JavaScript stack to find the specific `dispatch` function call responsible for the error. +如果你没有发现上述错误,在控制台点开报错旁边的箭头以查看错误堆栈,从中查找是哪个 `dispatch` 函数引发的错误。 --- -### My reducer or initializer function runs twice {/*my-reducer-or-initializer-function-runs-twice*/} +### 我的 reducer 和初始化函数运行了两次 {/*my-reducer-or-initializer-function-runs-twice*/} -In [Strict Mode](/reference/react/StrictMode), React will call your reducer and initializer functions twice. This shouldn't break your code. +[严格模式](/reference/react/StrictMode) 下 React 会调用两次 reducer 和初始化函数,但是这不应该会破坏你的代码逻辑。 -This **development-only** behavior helps you [keep components pure.](/learn/keeping-components-pure) React uses the result of one of the calls, and ignores the result of the other call. As long as your component, initializer, and reducer functions are pure, this shouldn't affect your logic. However, if they are accidentally impure, this helps you notice the mistakes. +这个 **仅限于开发模式** 的行为可以帮助你 [保持组件纯粹](/learn/keeping-components-pure):React 会使用其中一次调用结果并忽略另一个结果。如果你的组件、初始化函数以及 reducer 函数都是纯函数,这并不会影响你的逻辑。不过一旦它们存在副作用,这个额外的行为就可以帮助你发现它。 -For example, this impure reducer function mutates an array in state: +比如下面这个 reducer 函数直接修改了数组类型的 state: ```js {4-6} function reducer(state, action) { switch (action.type) { case 'added_todo': { - // 🚩 Mistake: mutating state + // 🚩 错误:直接修改 state state.todos.push({ id: nextId++, text: action.text }); return state; } @@ -1100,13 +1100,13 @@ function reducer(state, action) { } ``` -Because React calls your reducer function twice, you'll see the todo was added twice, so you'll know that there is a mistake. In this example, you can fix the mistake by [replacing the array instead of mutating it](/learn/updating-arrays-in-state#adding-to-an-array): +因为 React 会调用 reducer 函数两次,导致你看到添加了两条代办事项,于是你就发现了这个错误行为。在这个示例中,你可以通过 [返回新的数组而不是修改数组](/learn/updating-arrays-in-state#adding-to-an-array) 来修复它: ```js {4-11} function reducer(state, action) { switch (action.type) { case 'added_todo': { - // ✅ Correct: replacing with new state + // ✅ 修复:返回一个新的 state 数组 return { ...state, todos: [ @@ -1120,6 +1120,6 @@ function reducer(state, action) { } ``` -Now that this reducer function is pure, calling it an extra time doesn't make a difference in behavior. This is why React calling it twice helps you find mistakes. **Only component, initializer, and reducer functions need to be pure.** Event handlers don't need to be pure, so React will never call your event handlers twice. +现在这个 reducer 是纯函数了,调用两次也不会有不一致的行为。这就是 React 如何通过调用两次函数来帮助你发现错误。**只有组件、初始化函数和 reducer 函数需要是纯函数**。事件处理函数不需要实现为纯函数,并且 React 永远不会调用事件函数两次。 -Read [keeping components pure](/learn/keeping-components-pure) to learn more. +阅读 [保持组件纯粹](/learn/keeping-components-pure) 以了解更多相关信息。