From b9cfec19201c19660dadb27b9f13a68d78bc74ed Mon Sep 17 00:00:00 2001 From: qinhua <352484005@qq.com> Date: Sat, 11 Dec 2021 21:09:39 +0800 Subject: [PATCH 01/10] docs(cn):extracting-state-logic-into-a-reducer --- .../extracting-state-logic-into-a-reducer.md | 424 +++++++++--------- 1 file changed, 212 insertions(+), 212 deletions(-) diff --git a/beta/src/pages/learn/extracting-state-logic-into-a-reducer.md b/beta/src/pages/learn/extracting-state-logic-into-a-reducer.md index 7836fd4408..90e45a0a0e 100644 --- a/beta/src/pages/learn/extracting-state-logic-into-a-reducer.md +++ b/beta/src/pages/learn/extracting-state-logic-into-a-reducer.md @@ -1,25 +1,27 @@ --- -title: Extracting State Logic into a Reducer + title: 把状态逻辑移到 Reducer 中 +translators: + - qinhua --- -Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called a "reducer." +对于那些需要更新多个状态的组件来说,过于分散的事件处理程序可能会令人不知所措。对于这种情况,你可以将组件外部的所有状态更新逻辑整合到一个称为 `reducer` 的函数中。 -- What a reducer function is -- How to refactor `useState` to `useReducer` -- When to use a reducer -- How to write one well +- 什么是 reducer 函数 +- 如何使用 `useReducer` 重构 `useState` +- 什么时候用 reducer +- 如何编写一个好的 reducer -## Consolidate state logic with a reducer +## 使用 reducer 整合状态逻辑 {/*consolidate-state-logic-with-a-reducer*/} -As your components grow in complexity, it can get harder to see all the different ways that a component's state gets updated at a glance. For example, the `TaskBoard` component below holds an array of `tasks` in state and uses three different event handlers to add, remove, and edit tasks: +随着组件复杂度的增加,你将很难一眼看清组件进行状态更新的实现方式。例如,下面的 `TaskBoard` 组件有一个数组类型的状态 `task`,并通过三个不同的事件处理程序来实现添加、删除和修改: @@ -57,7 +59,7 @@ export default function TaskBoard() { return ( <> -

Prague itinerary

+

布拉格的行程

@@ -72,9 +74,9 @@ export default function TaskBoard() { let nextId = 3; const initialTasks = [ - { id: 0, text: 'Visit Kafka Museum', done: true }, - { id: 1, text: 'Watch a puppet show', done: false }, - { id: 2, text: 'Lennon Wall pic', done: false }, + { id: 0, text: '参观卡夫卡博物馆', done: true }, + { id: 1, text: '看木偶戏', done: false }, + { id: 2, text: '列侬墙图片', done: false }, ]; ``` @@ -86,14 +88,14 @@ export default function AddTask({ onAddTask }) { return ( <> setText(e.target.value)} /> + }}>添加 ) } @@ -137,7 +139,7 @@ function Task({ task, onChange, onDelete }) { }); }} /> ); @@ -146,7 +148,7 @@ function Task({ task, onChange, onDelete }) { <> {task.text} ); @@ -165,7 +167,7 @@ function Task({ task, onChange, onDelete }) { /> {taskContent} ); @@ -180,17 +182,17 @@ ul, li { margin: 0; padding: 0; }
-Each of its event handlers calls `setTasks` in order to update the state. As this component grows, so does the amount of state logic sprinkled throughout it. To reduce this complexity and keep all your logic in one easy-to-access place, you can move that state logic into a single function outside your component, **called a "reducer."** +它的每个事件处理程序都通过 `setTasks` 来更新状态。随着这个组件的不断迭代,其中的状态逻辑数量也在增加。为了降低这种复杂性并将所有逻辑保持在一个容易访问的地方,你可以将该状态逻辑移到组件之外的一个称为 **reducer** 的函数中。 -Reducers are a different way to handle state. You can migrate from `useState` to `useReducer` in three steps: +Reducer 是处理状态的另一种方式。你可以通过三个步骤从 `useState` 迁移到 `useReducer`: -1. **Move** from setting state to dispatching actions. -2. **Write** a reducer function. -3. **Use** the reducer from your component. +1. 将设置状态的相关逻辑 **移动** 到派发操作中; +2. **编写** 一个 reducer 函数; +3. 在你的组件中 **使用** reducer。 -### Step 1: Move from setting state to dispatching actions +### 第 1 步: 将设置状态的相关逻辑移动到调度操作中 {/*move-from-setting-state-to-dispatching-actions*/} -Your event handlers currently specify *what to do* by setting state: +你的事件处理程序目前是通过设置状态来实现逻辑的: ```js function handleAddTask(text) { @@ -218,13 +220,14 @@ function handleDeleteTask(taskId) { } ``` -Remove all the state setting logic. What you are left with are three event handlers: +移除所有的状态设置逻辑。只留下三个事件处理函数: -* `handleAddTask(text)` is called when the user presses "Add". -* `handleChangeTask(task)` is called when the user toggles a task or presses "Save". -* `handleDeleteTask(taskId)` is called when the user presses "Delete". +* `handleAddTask(text)` 在用户点击 “添加” 时被调用。 +* `handleChangeTask(task)` 在用户切换任务或点击 “保存” 时被调用。 +* `handleDeleteTask(taskId)` 在用户点击 “删除” 时被调用。 -Managing state with reducers is slightly different from directly setting state. Instead of telling React "what to do" by setting state, you specify "what the user just did" by dispatching "actions" from your event handlers. (The state update logic will live elsewhere!) So instead of "setting `tasks`" via event handler, you're dispatching an "added/removed/deleted a task" action. This is more descriptive of the user's intent. +使用 reducers 管理状态与直接设置状态略有不同。它不是通过设置状态告诉 React “要做什么”,而是通过从事件处理程序派发 “动作” 来指明 “用户刚刚做了什么”。(状态更新逻辑在其他地方!) +因此,不是通过事件处理器“设置任务”,而是调度一个 “添加/修改/删除任务” 的动作。这更加符合用户的思维。 ```js function handleAddTask(text) { @@ -250,12 +253,12 @@ function handleDeleteTask(taskId) { } ``` -The object you pass to `dispatch` is called an "action:" +你传递给 `dispatch` 的对象叫做 "action:" ```js {3-7} function handleDeleteTask(taskId) { dispatch( - // "action" object: + // "action" 对象: { type: 'deleted', id: taskId @@ -264,41 +267,40 @@ function handleDeleteTask(taskId) { } ``` -It is a regular JavaScript object. You decide what to put in it, but generally it should contain the minimal information about *what happened*. (You will add the `dispatch` function itself in a later step.) - +它是一个普通的 JavaScript 对象。里面放什么由你决定,但通常它应该至少包含 *事件发生* 的信息。(你将在后面的步骤中把它添加到 `dispatch` 函数中。) -An action object can have any shape. By convention, it is common to give it a string `type` that describes what happened, and pass any additional information in other fields. The `type` is specific to a component, so in this example either `'added'` or `'added_task'` would be fine. Choose a name that says what happened! +action 对象可以有多种形式。按照约定,通常给 `type` 字段一个字符串来描述发生了什么,并通过其它字段传递附加信息。`type` 是特定于组件的,所以,在这个例子中 `added` 和 `addded_task` 都可以。选一个能描述清楚事情的名字! ```js dispatch({ - // specific to component + // 针对特定的组件 type: 'what_happened', - // other fields go here + // 其它字段放这里 }); ``` -### Step 2: Write a reducer function +### 第 2 步: 编写一个 reducer 函数 {/*write-a-reducer-function*/} -A reducer function is where you will put your state logic. It takes two arguments, the current state and the action object, and it returns the next state: +reducer 函数就是你放置状态逻辑的地方。它接受两个参数,当前 state 和 action 对象,并返回下一个 state: ```js function yourReducer(state, action) { - // return next state for React to set + // 给 React 返回下一个状态 } ``` -React will set the state to what you return from the reducer. +React 会将状态设置为你从 reducer 返回的状态。 -To move your state setting logic from your event handlers to a reducer function in this example, you will: +在这个例子中,要将状态设置逻辑从事件处理程序移到 reducer 函数中,你需要: -1. Declare the current state (`tasks`) as the first argument. -2. Declare the `action` object as the second argument. -3. Return the *next* state from the reducer (which React will set the state to). +1. 声明当前状态(`tasks`)作为第一个参数; +2. 声明 `action` 对象作为第二个参数; +3. 从 `reducer` 返回 *下一个* 状态(React 会将旧的状态设置为这个最新的状态)。 -Here is all the state setting logic migrated to a reducer function: +下面是所有迁移到 `reducer` 函数的状态设置逻辑: ```js function tasksReducer(tasks, action) { @@ -319,16 +321,15 @@ function tasksReducer(tasks, action) { } else if (action.type === 'deleted') { return tasks.filter(t => t.id !== action.id); } else { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action: ' + action.type); } } ``` -> Because the reducer function takes state (`tasks`) as an argument, you can **declare it outside of your component.** This decreases the indentation level and can make your code easier to read. - +> 由于 `reducer` 函数接受 `state`(tasks)作为参数,因此你可以 **在组件之外声明它**。**这减少了代码的缩进级别,提升了代码的可读性。 -The code above uses if/else statements, but it's a convention to use [switch statements](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/switch) inside reducers. The result is the same, but it can be easier to read switch statements at a glance. We'll be using them throughout the rest of this documentation like so: +上面的代码使用了 `if/else` 语句,但是在 reducers 中使用 [switch 语句](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/switch) 是一种约定。结果是相同的,但 `switch` 语句读起来一目了然。我们将在本文档的后面部分像这样使用它们: ```js function tasksReducer(tasks, action) { @@ -353,24 +354,25 @@ function tasksReducer(tasks, action) { return tasks.filter(t => t.id !== action.id); } default: { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action: ' + action.type); } } } ``` -We recommend to wrap each `case` block into the `{` and `}` curly braces so that variables declared inside of different `case`s don't clash with each other. Also, a `case` should usually end with a `return`. If you forget to `return`, the code will "fall through" to the next `case`, which can lead to mistakes! +我们建议将每个 `case` 块包装到 `{` 和 `}` 花括号中,这样在不同 `case` 中声明的变量就不会互相冲突。此外,`case` 通常应该以 `return` 结尾。如果你忘了 `return`,代码就会 `进入` 到下一个 `case`,这就会导致错误! -If you're not yet comfortable with switch statements, using if/else is completely fine. +如果你还不熟悉 `switch` 语句,可以使用 `if/else`。 - + -Although reducers can "reduce" the amount of code inside your component, they are actually named after the [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) operation that you can perform on arrays. -The `reduce()` operation lets you take an array and "accumulate" a single value out of many: +尽管 `reducer` 可以 “减少” 组件内的代码量,但它实际上是以你可以在数组上执行的 [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) 操作命名的。 + +`reduce()` 操作允许你从数组中 “累加” 一个值: ``` const arr = [1, 2, 3, 4, 5]; @@ -379,9 +381,9 @@ const sum = arr.reduce( ); // 1 + 2 + 3 + 4 + 5 ``` -The function you pass to `reduce` is known as a "reducer". It takes the _result so far_ and the _current item,_ then it returns the _next result._ React reducers are an example of the same idea: they take the _state so far_ and the _action_, and return the _next state._ In this way, they accumulate actions over time into state. +你传递给 `reduce` 的函数被称为 “reducer”。它接受 `目前的结果` 和 `当前的值`,然后返回 `下一个结果`。React 中的 `reducer` 和这个是一样的:它们都接受 `目前的状态` 和 `action` ,然后返回 `下一个状态`。通过这种方式,随着时间的推移,将 `actions` 积累到状态中。 -You could even use the `reduce()` method with an `initialState` and an array of `actions` to calculate the final state by passing your reducer function to it: +你甚至可以使用 `reduce()` 方法以及 `initialState` 和 `actions` 数组,通过传递你的 `reducer` 函数来计算最终的状态: @@ -390,10 +392,10 @@ import tasksReducer from './tasksReducer.js'; let initialState = []; let actions = [ - { type: 'added', id: 1, text: 'Visit Kafka Museum' }, - { type: 'added', id: 2, text: 'Watch a puppet show' }, + { type: 'added', id: 1, text: '参观卡夫卡博物馆' }, + { type: 'added', id: 2, text: '看木偶戏' }, { type: 'deleted', id: 1 }, - { type: 'added', id: 3, text: 'Lennon Wall pic' }, + { type: 'added', id: 3, text: '列侬墙图片' }, ]; let finalState = actions.reduce( @@ -435,7 +437,7 @@ export default function tasksReducer( return tasks.filter(t => t.id !== action.id); } default: { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action: ' + action.type); } } } @@ -447,13 +449,13 @@ export default function tasksReducer( -You probably won't need to do this yourself, but this is similar to what React does! +你可能不需要自己做这些,但这与 React 所做的很相似! -### Step 3: Use the reducer from your component +### 第 3 步: 在组件中使用 reducer {/*use-the-reducer-from-your-component*/} -Finally, you need to hook up the `tasksReducer` to your component. Make sure to import the `useReducer` Hook from React: +最后,你需要将 `tasksReducer` 导入到组件中。记得先从 React 中导入 `useReducer` 钩子: ```js import { useReducer } from 'react'; @@ -471,19 +473,19 @@ with `useReducer` like so: const [tasks, dispatch] = useReducer(tasksReducer, initialTasks); ``` -The `useReducer` Hook is similar to `useState`—you must pass it an initial state and it returns a stateful value and a way to set state (in this case, the dispatch function). But it's a little different. +`useReducer` 和 `useState` 是相似的。你必须给它传递一个初始状态,它会返回一个有状态的值和一个设置该状态的函数(在这个例子中就是 dispatch 函数)。但是,它们两个之间还是有点差异的。 -The `useReducer` Hook takes two arguments: +`useReducer` 钩子接受 2 个参数: -1. A reducer function -2. An initial state +1. 一个 reducer 函数 +2. 一个初始的 state -And it returns: +它返回如下内容: -1. A stateful value -2. A dispatch function (to "dispatch" user actions to the reducer) +1. 一个有状态的值 +2. 一个 dispatch 函数(用来 “派发” 用户操作给 reducer) -Now it's fully wired up! Here, the reducer is declared at the bottom of the component file: +现在一切都准备就绪了!我们在这里把 reducer 定义在了组件的末尾: @@ -522,7 +524,7 @@ export default function TaskBoard() { return ( <> -

Prague itinerary

+

布拉格的行程

@@ -557,16 +559,16 @@ function tasksReducer(tasks, action) { return tasks.filter(t => t.id !== action.id); } default: { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action: ' + action.type); } } } let nextId = 3; const initialTasks = [ - { id: 0, text: 'Visit Kafka Museum', done: true }, - { id: 1, text: 'Watch a puppet show', done: false }, - { id: 2, text: 'Lennon Wall pic', done: false } + { id: 0, text: '参观卡夫卡博物馆', done: true }, + { id: 1, text: '看木偶戏', done: false }, + { id: 2, text: '列侬墙图片', done: false } ]; ``` @@ -578,14 +580,14 @@ export default function AddTask({ onAddTask }) { return ( <> setText(e.target.value)} /> + }}>添加 ) } @@ -629,7 +631,7 @@ function Task({ task, onChange, onDelete }) { }); }} /> ); @@ -638,7 +640,7 @@ function Task({ task, onChange, onDelete }) { <> {task.text} ); @@ -657,7 +659,7 @@ function Task({ task, onChange, onDelete }) { /> {taskContent} ); @@ -672,7 +674,7 @@ ul, li { margin: 0; padding: 0; }
-If you want, you can even move the reducer to a different file: +如果有需要,你甚至可以把 reducer 移到一个单独的文件中: @@ -711,8 +713,8 @@ export default function TaskBoard() { } return ( - <> -

Prague itinerary

+ <> +

布拉格的行程

@@ -727,9 +729,9 @@ export default function TaskBoard() { let nextId = 3; const initialTasks = [ - { id: 0, text: 'Visit Kafka Museum', done: true }, - { id: 1, text: 'Watch a puppet show', done: false }, - { id: 2, text: 'Lennon Wall pic', done: false }, + { id: 0, text: '参观卡夫卡博物馆', done: true }, + { id: 1, text: '看木偶戏', done: false }, + { id: 2, text: '列侬墙图片', done: false }, ]; ``` @@ -759,7 +761,7 @@ export default function tasksReducer( return tasks.filter(t => t.id !== action.id); } default: { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action:' + action.type); } } } @@ -773,14 +775,14 @@ export default function AddTask({ onAddTask }) { return ( <> setText(e.target.value)} /> + }}>添加 ) } @@ -824,7 +826,7 @@ function Task({ task, onChange, onDelete }) { }); }} /> ); @@ -833,7 +835,7 @@ function Task({ task, onChange, onDelete }) { <> {task.text} ); @@ -852,7 +854,7 @@ function Task({ task, onChange, onDelete }) { /> {taskContent} ); @@ -867,30 +869,30 @@ ul, li { margin: 0; padding: 0; }
-Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify *what happened* by dispatching actions, and the reducer function determines *how the state updates* in response to them. +分离关注点可以让我们更容易地理解组件逻辑。现在事件处理程序只通过派发 `actions` 来指定 *发生了什么*,而 `reducer` 函数决定了 *状态如何更新* 来响应它们。 -## Comparing `useState` and `useReducer` +## `useState` 和 `useReducer` 的对比 {/*Comparing-useState-and-useReducer*/} -Reducers are not without downsides! Here's a few ways you can compare them: +Reducers 并非没有缺点!你可以从这些角度去对比它们: -* **Code size:** Generally, with `useState` you have to write less code upfront. With `useReducer`, you have to write both a reducer function _and_ dispatch actions. However, `useReducer` can help cut down on the code if many event handlers modify state in a similar way. -* **Readability:** `useState` is very easy to read when the state updates are simple. When they get more complex, they can bloat your component's code and make it difficult to scan. In this case, `useReducer` lets you cleanly separate the *how* of update logic from the *what happened* of event handlers. -* **Debugging:** When you have a bug with `useState`, it can be difficult to tell _where_ the state was set incorrectly, and _why_. With `useReducer`, you can add a console log into your reducer to see every state update, and _why_ it happened (due to which `action`). If each `action` is correct, you'll know that the mistake is in the reducer logic itself. However, you have to step through more code than with `useState`. -* **Testing:** A reducer is a pure function that doesn't depend on your component. This means that you can export and test it separately in isolation. While generally it's best to test components in a more realistic environment, for complex state update logic it can be useful to assert that your reducer returns a particular state for a particular initial state and action. -* **Personal preference:** Some people like reducers, others don't. That's okay. It's a matter of preference. You can always convert between `useState` and `useReducer` back and forth: they are equivalent! +- **代码体积:** 通常,在使用 `useState` 时,一开始只需要编写少量代码。而 `useReducer` 必须提前编写 reducer 函数和需要调度的 actions。但是,当多个事件处理程序以相似的方式修改 state 时,`useReducer` 可以减少代码量。 +- **可读性:** 当状态更新逻辑足够简单时,`useState` 的可读性还行。但是,一旦逻辑变得复杂起来,它们会使组件变得臃肿且难以扫描。在这种情况下,`useReducer` 允许你将状态更新逻辑与事件处理程序分离开来。 +- **可调试性:** 当使用 `useState` 出现问题时, 你很难发现具体原因以及为什么。 而使用 `useReducer` 时, 你可以在 reducer 函数中通过打印日志的方式来观察每个状态的更新,以及为什么要更新 (来自哪个 `action`)。 如果所有 `action` 都没问题,你就知道问题出在了 reducer 本身的逻辑中。 然而,与使用 `useState` 相比,你必须遍历更多的代码。 +- **可测试性:** reducer 是一个不依赖于组件的纯函数。这就意味着你可以单独对它进行测试。一般来说,我们最好是在真实环境中测试组件,但对于复杂的状态更新逻辑,把 reducer 断言为特定的初始状态和 `action` 可能会很有帮助。 +- **个人偏好:** 并不是所有人都喜欢用 reducer,没关系,这是个人偏好问题。你可以随时在 `useState` 和 `useReducer` 之间切换,他们是等价的! -We recommend using a reducer if you often encounter bugs due to incorrect state updates in some component, and want to introduce more structure to its code. You don't have to use reducers for everything: feel free to mix and match! You can even `useState` and `useReducer` in the same component. +如果你在修改某些组件状态时经常出现问题或者想给组件添加更多逻辑时,我们建议你还是使用 reducer。当然,你也不必整个项目都用 reducer,这是可以自由搭配的。你甚至可以在一个组件中同时使用 `useState` 和 `useReducer`。 -## Writing reducers well +## 编写更好的 reducers {/*writing-reducers-well*/} -Keep these two tips in mind when writing reducers: +编写 `reducers` 时最好牢记以下两点: -* **Reducers must be pure.** Similar to [state updater functions](/learn/queueing-a-series-of-state-updates), reducers run during rendering! (Actions are queued until the next render.) This means that reducers [must be pure](/learn/keeping-components-pure)—same inputs always result in the same output. They should not send requests, schedule timeouts, or perform any side effects (operations that impact things outside the component). They should update [objects](/learn/updating-objects-in-state) and [arrays](/learn/updating-arrays-in-state) without mutations. -* **Actions describe "what happened," not "what to do."** For example, if a user presses "Reset" on a form with five fields managed by a reducer, it makes more sense to dispatch one `reset_form` action rather than five separate `set_field` actions. If you log every action in a reducer, that log should be clear enough for you to reconstruct what interactions or responses happened in what order. This helps with debugging! +- **reducers 必须是纯净的。** 这一点和 [状态更新函数](/learn/queueing-a-series-of-state-updates) 是相似的,`reducers` 在是在渲染时运行的!(actions 会排队直到下一次渲染)。 这就意味着 `reducers` [必须纯净](/learn/keeping-components-pure),即当输入一定时,输出也是一定的。 (operations that impact things outside the component).它们不应该包含异步请求、定时器或者任何副作用(对组件外部有影响的操作)。它们应该以不可变值的方式去修改 [对象](/learn/updating-objects-in-state) 和 [数组](/learn/updating-arrays-in-state)。 +- **actions 用来述 “发生了什么” ,而不是 “做什么”** 举个例子,如果用户在一个由 `reducer` 管理的表单(包含五个表单项)中点击了 `重置按钮`,那么派发一个 `reset_form` 操作比派发五个单独的 `set_field` 操作将更加合理。如果你在一个 `reducer` 中打印了所有的 `action` 日志,那么这个日志应该是很清晰的,它能让你以某种步骤复现已发生的交互或响应。这对代码调试很有帮助! -## Writing concise reducers with Immer +## 使用 Immer 简化 reducers {/*writing-concise-reducers-with-immer*/} -Just like with [updating objects](/learn/updating-objects-in-state#write-concise-update-logic-with-immer) and [arrays](/learn/updating-arrays-in-state#write-concise-update-logic-with-immer) in regular state, you can use the Immer library to make reducers more concise. Here, [`useImmerReducer`](https://github.com/immerjs/use-immer#useimmerreducer) lets you mutate the state with `push` or `arr[i] =` assignment: +与在平常的 state 中 [修改对象](/learn/updating-objects-in-state#write-concise-update-logic-with-immer) 和 [数组](/learn/updating-arrays-in-state#write-concise-update-logic-with-immer) 一样,你可以使用 `Immer` 这个库来简化 `reducer`。在这里,[`useImmerReducer`](https://github.com/immerjs/use-immer#useimmerreducer) 让你可以通过 `push` 或 `arr[i] =` 来修改 state : @@ -920,7 +922,7 @@ function tasksReducer(draft, action) { return draft.filter(t => t.id !== action.id); } default: { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action:' + action.type); } } } @@ -955,7 +957,7 @@ export default function TaskBoard() { return ( <> -

Prague itinerary

+

布拉格的行程

@@ -970,9 +972,9 @@ export default function TaskBoard() { let nextId = 3; const initialTasks = [ - { id: 0, text: 'Visit Kafka Museum', done: true }, - { id: 1, text: 'Watch a puppet show', done: false }, - { id: 2, text: 'Lennon Wall pic', done: false }, + {id: 0, text: '参观卡夫卡博物馆', done: true}, + {id: 1, text: '看木偶戏', done: false}, + {id: 2, text: '列侬墙图片', done: false}, ]; ``` @@ -984,14 +986,14 @@ export default function AddTask({ onAddTask }) { return ( <> setText(e.target.value)} /> + }}>添加 ) } @@ -1035,7 +1037,7 @@ function Task({ task, onChange, onDelete }) { }); }} /> ); @@ -1044,7 +1046,7 @@ function Task({ task, onChange, onDelete }) { <> {task.text} ); @@ -1063,7 +1065,7 @@ function Task({ task, onChange, onDelete }) { /> {taskContent} ); @@ -1096,36 +1098,34 @@ ul, li { margin: 0; padding: 0; }
-Reducers must be pure, so they shouldn't mutate state. But Immer provides you with a special `draft` object which is safe to mutate. Under the hood, Immer will create a copy of your state with the changes you made to the `draft`. This is why reducers managed by `useImmerReducer` can mutate their first argument and don't need to return state. +Reducers 应该是纯净的,所以它们不应该去修改 state。而 Immer 为你提供了一种特殊的 `draft` 对象,你可以用它安全的修改 state。在底层,Immer 会基于当前 state 创建一个副本。这就是为什么通过 `useImmerReducer` 来管理 reducers 时可以修改第一个参数,但不需要返回一个新的 state 的原因。 -* To convert from `useState` to `useReducer`: - 1. Dispatch actions from event handlers. - 2. Write a reducer function that returns the next state for a given state and action. - 3. Replace `useState` with `useReducer`. -* Reducers require you to write a bit more code, but they help with debugging and testing. -* Reducers must be pure. -* Actions describe "what happened," not "what to do." -* Use Immer if you want to write reducers in a mutating style. +* 把 `useState` 转化为 `useReducer`: + 1. 通过事件处理函数派发 actions; + 2. 编写一个 reducer 函数,它接受传入的 state 和一个 action,并返回一个新的 state; + 3. 使用 `useReducer` 替换 `useState`; +* Reducers 可能需要你写更多的代码,但是这有利于代码的调试和测试。 +* Reducers 必须是纯净的。 +* Actions 描述的是 “发生了什么” 而不是 “要做什么”。 +* 使用 Immer 来帮你编写变更风格的 reducers。 - - -### Dispatch actions from event handlers +### 通过事件处理函数派发 actions {/*dispatch-actions-from-event-handlers*/} -Currently, the event handlers in `ContactList.js` and `Chat.js` have `// TODO` comments. This is why typing into the input doesn't work, and clicking on the buttons doesn't change the selected recipient. +目前,`ContactList.js` 和 `Chat.js` 中的事件处理程序包含 `// TODO` 注释。这就是为什么输入不起作用,点击按钮也不会改变收件人的原因。 -Replace these two `// TODO`s with the code to `dispatch` the corresponding actions. To see the expected shape and the type of the actions, check the reducer in `messengerReducer.js`. The reducer is already written so you won't need to change it. You only need to dispatch the actions in `ContactList.js` and `Chat.js`. +将这两个 `// TODO` 替换为 `dispatch` 相应的操作。如果要查看 actions 对于的参数和类型,请阅读 `messerreducer.js` 中的 reducer。reducer 已经写好了,不需要修改它。你只需要在 `ContactList.js` 和 `Chat.js` 中派发动作即可。 -The `dispatch` function is already available in both of these components because it was passed as a prop. So you need to call `dispatch` with the corresponding action object. +`dispatch` 函数在这两个组件中都是可用的,因为它已经以 prop 的形式传递进来了。所以你需要用相应的 action 对象来调用 `dispatch` 函数。 -To check the action object shape, you can look at the reducer and see which `action` fields it expects to see. For example, the `changed_selection` case in the reducer looks like this: +要检查 action 的对象结构,你可以查看 reducer,看看它需要哪些字段。例如,reducer 中的 `changed_selection` 是这样的: ```js case 'changed_selection': { @@ -1136,7 +1136,7 @@ case 'changed_selection': { } ``` -This means that your action object should have a `type: 'changed_selection'`. You also see the `action.contactId` being used, so you need to include a `contactId` property into your action. +这表示你的 `action` 对象应该有一个 `type: 'changed_selection'`。同时你也可以看到 `action.contactId` 被使用到了,所以你需要传入一个 `contactId` 属性到你的动作中。 @@ -1187,7 +1187,7 @@ const contacts = [ ```js messengerReducer.js export const initialState = { selectedId: 0, - message: 'Hello' + message: '你好' }; export function messengerReducer( @@ -1209,7 +1209,7 @@ export function messengerReducer( }; } default: { - throw Error('Unknown action: ' + action.type); + throw Error('未知 action:' + action.type); } } } @@ -1227,7 +1227,7 @@ export default function ContactList({ {contacts.map(contact =>