diff --git a/src/content/learn/extracting-state-logic-into-a-reducer.md b/src/content/learn/extracting-state-logic-into-a-reducer.md index 5c08c0123..7bd896b32 100644 --- a/src/content/learn/extracting-state-logic-into-a-reducer.md +++ b/src/content/learn/extracting-state-logic-into-a-reducer.md @@ -1,25 +1,25 @@ --- -title: Extracting State Logic into a Reducer +title: Trích xuất State Logic vào một Reducer --- -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._ +Những component có nhiều cập nhật state trải rộng khắp nhiều event handler có thể trở nên phức tạp quá mức. Trong những trường hợp này, bạn có thể hợp nhất tất cả logic cập nhật state bên ngoài component của bạn vào một function duy nhất, được gọi là _reducer._ -- What a reducer function is -- How to refactor `useState` to `useReducer` -- When to use a reducer -- How to write one well +- Function reducer là gì +- Cách refactor từ `useState` sang `useReducer` +- Khi nào nên sử dụng reducer +- Cách viết reducer tốt -## Consolidate state logic with a reducer {/*consolidate-state-logic-with-a-reducer*/} +## Hợp nhất logic state với reducer {/*consolidate-state-logic-with-a-reducer*/} -As your components grow in complexity, it can get harder to see at a glance all the different ways in which a component's state gets updated. For example, the `TaskApp` component below holds an array of `tasks` in state and uses three different event handlers to add, remove, and edit tasks: +Khi những component của bạn phát triển theo độ phức tạp, có thể trở nên khó khăn hơn để nhìn thoáng qua tất cả những cách khác nhau mà state của component được cập nhật. Ví dụ, component `TaskApp` bên dưới giữ một mảng `tasks` trong state và sử dụng ba event handler khác nhau để thêm, xóa và chỉnh sửa task: @@ -179,17 +179,17 @@ li { -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".** +Mỗi event handler của nó gọi `setTasks` để cập nhật state. Khi component này phát triển, cũng như số lượng logic state rải rác khắp nó. Để giảm độ phức tạp này và giữ tất cả logic của bạn ở một nơi dễ truy cập, bạn có thể di chuyển logic state đó thành một function duy nhất bên ngoài component của bạn, **được gọi là "reducer".** -Reducers are a different way to handle state. You can migrate from `useState` to `useReducer` in three steps: +Reducer là một cách khác để xử lý state. Bạn có thể di chuyển từ `useState` sang `useReducer` trong ba bước: -1. **Move** from setting state to dispatching actions. -2. **Write** a reducer function. -3. **Use** the reducer from your component. +1. **Di chuyển** từ việc setting state sang dispatching action. +2. **Viết** một function reducer. +3. **Sử dụng** reducer từ component của bạn. -### Step 1: Move from setting state to dispatching actions {/*step-1-move-from-setting-state-to-dispatching-actions*/} +### Bước 1: Di chuyển từ setting state sang dispatching action {/*step-1-move-from-setting-state-to-dispatching-actions*/} -Your event handlers currently specify _what to do_ by setting state: +Những event handler hiện tại của bạn chỉ định _phải làm gì_ bằng cách setting state: ```js function handleAddTask(text) { @@ -220,13 +220,13 @@ function handleDeleteTask(taskId) { } ``` -Remove all the state setting logic. What you are left with are three event handlers: +Loại bỏ tất cả logic setting state. Những gì bạn còn lại là ba event handler: -- `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)` được gọi khi người dùng nhấn "Add". +- `handleChangeTask(task)` được gọi khi người dùng toggle một task hoặc nhấn "Save". +- `handleDeleteTask(taskId)` được gọi khi người dùng nhấn "Delete". -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 an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent. +Quản lý state với reducer hơi khác so với việc setting state trực tiếp. Thay vì nói với React "phải làm gì" bằng cách setting state, bạn chỉ định "người dùng vừa làm gì" bằng cách dispatching "action" từ những event handler của bạn. (Logic cập nhật state sẽ tồn tại ở nơi khác!) Vì vậy thay vì "setting `tasks`" thông qua một event handler, bạn đang dispatching một action "added/changed/deleted a task". Điều này mô tả ý định của người dùng một cách rõ ràng hơn. ```js function handleAddTask(text) { @@ -252,7 +252,7 @@ function handleDeleteTask(taskId) { } ``` -The object you pass to `dispatch` is called an "action": +Object bạn truyền cho `dispatch` được gọi là "action": ```js {3-7} function handleDeleteTask(taskId) { @@ -266,13 +266,13 @@ 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.) +Đó là một object JavaScript thông thường. Bạn quyết định đặt gì vào đó, nhưng nói chung nó nên chứa thông tin tối thiểu về _điều gì đã xảy ra_. (Bạn sẽ thêm chính function `dispatch` ở bước sau.) -An action object can have any shape. +Một object action có thể có bất kỳ hình dạng nào. -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! +Theo quy ước, việc thường thấy là đặt cho nó một string `type` mô tả điều gì đã xảy ra, và truyền bất kỳ thông tin bổ sung nào trong những field khác. `type` cụ thể cho một component, vì vậy trong ví dụ này `'added'` hoặc `'added_task'` đều ổn. Chọn một tên nói lên điều gì đã xảy ra! ```js dispatch({ @@ -284,9 +284,9 @@ dispatch({ -### Step 2: Write a reducer function {/*step-2-write-a-reducer-function*/} +### Bước 2: Viết một function reducer {/*step-2-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: +Function reducer là nơi bạn sẽ đặt logic state của mình. Nó nhận hai tham số, state hiện tại và object action, và nó trả về state tiếp theo: ```js function yourReducer(state, action) { @@ -294,15 +294,15 @@ function yourReducer(state, action) { } ``` -React will set the state to what you return from the reducer. +React sẽ set state thành những gì bạn return từ reducer. -To move your state setting logic from your event handlers to a reducer function in this example, you will: +Để di chuyển logic setting state từ những event handler của bạn sang một function reducer trong ví dụ này, bạn sẽ: -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. Khai báo state hiện tại (`tasks`) như tham số đầu tiên. +2. Khai báo object `action` như tham số thứ hai. +3. Return state _tiếp theo_ từ reducer (mà React sẽ set state thành). -Here is all the state setting logic migrated to a reducer function: +Đây là tất cả logic setting state được di chuyển sang một function reducer: ```js function tasksReducer(tasks, action) { @@ -331,13 +331,13 @@ function tasksReducer(tasks, action) { } ``` -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. +Bởi vì function reducer nhận state (`tasks`) như một tham số, bạn có thể **khai báo nó bên ngoài component của bạn.** Điều này giảm mức độ thụt lề và có thể làm cho code của bạn dễ đọc hơn. -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. +Code trên sử dụng câu lệnh if/else, nhưng việc thường thấy là sử dụng [câu lệnh switch](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/switch) bên trong reducer. Kết quả giống nhau, nhưng có thể dễ đọc hơn khi nhìn thoáng qua câu lệnh switch. -We'll be using them throughout the rest of this documentation like so: +Chúng ta sẽ sử dụng chúng trong phần còn lại của tài liệu này như sau: ```js function tasksReducer(tasks, action) { @@ -371,19 +371,19 @@ function tasksReducer(tasks, action) { } ``` -We recommend wrapping 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! +Chúng tôi khuyến nghị bao bọc mỗi khối `case` vào dấu ngoặc nhọn `{` và `}` để các biến được khai báo bên trong những `case` khác nhau không xung đột với nhau. Ngoài ra, một `case` thường nên kết thúc bằng `return`. Nếu bạn quên `return`, code sẽ "rơi qua" đến `case` tiếp theo, có thể dẫn đến lỗi! -If you're not yet comfortable with switch statements, using if/else is completely fine. +Nếu bạn chưa quen với câu lệnh switch, sử dụng if/else hoàn toàn ổn. -#### Why are reducers called this way? {/*why-are-reducers-called-this-way*/} +#### Tại sao reducer được gọi như vậy? {/*why-are-reducers-called-this-way*/} -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. +Mặc dù reducer có thể "giảm" số lượng code bên trong component của bạn, thực tế chúng được đặt tên theo phép toán [`reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) mà bạn có thể thực hiện trên mảng. -The `reduce()` operation lets you take an array and "accumulate" a single value out of many: +Phép toán `reduce()` cho phép bạn lấy một mảng và "tích lũy" một giá trị duy nhất từ nhiều giá trị: ``` const arr = [1, 2, 3, 4, 5]; @@ -392,9 +392,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. +Function bạn truyền cho `reduce` được gọi là "reducer". Nó nhận _kết quả hiện tại_ và _phần tử hiện tại,_ sau đó nó trả về _kết quả tiếp theo._ React reducer là một ví dụ của cùng một ý tưởng: chúng nhận _state hiện tại_ và _action_, và trả về _state tiếp theo._ Theo cách này, chúng tích lũy những action theo thời gian thành state. -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: +Bạn thậm chí có thể sử dụng method `reduce()` với một `initialState` và một mảng `actions` để tính toán state cuối cùng bằng cách truyền function reducer của bạn vào nó: @@ -453,43 +453,43 @@ export default function tasksReducer(tasks, action) { -You probably won't need to do this yourself, but this is similar to what React does! +Có thể bạn sẽ không cần phải tự làm điều này, nhưng đây là tương tự với những gì React làm! -### Step 3: Use the reducer from your component {/*step-3-use-the-reducer-from-your-component*/} +### Bước 3: Sử dụng reducer từ component của bạn {/*step-3-use-the-reducer-from-your-component*/} -Finally, you need to hook up the `tasksReducer` to your component. Import the `useReducer` Hook from React: +Cuối cùng, bạn cần kết nối `tasksReducer` với component của bạn. Import Hook `useReducer` từ React: ```js import { useReducer } from 'react'; ``` -Then you can replace `useState`: +Sau đó bạn có thể thay thế `useState`: ```js const [tasks, setTasks] = useState(initialTasks); ``` -with `useReducer` like so: +bằng `useReducer` như thế này: ```js 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. +Hook `useReducer` tương tự như `useState`—bạn phải truyền cho nó một initial state và nó trả về một stateful value và một cách để set state (trong trường hợp này, function dispatch). Nhưng nó hơi khác một chút. -The `useReducer` Hook takes two arguments: +Hook `useReducer` nhận hai tham số: -1. A reducer function -2. An initial state +1. Một function reducer +2. Một initial state -And it returns: +Và nó trả về: -1. A stateful value -2. A dispatch function (to "dispatch" user actions to the reducer) +1. Một stateful value +2. Một function dispatch (để "dispatch" những action của người dùng tới reducer) -Now it's fully wired up! Here, the reducer is declared at the bottom of the component file: +Bây giờ nó đã được kết nối đầy đủ! Ở đây, reducer được khai báo ở cuối file component: @@ -674,7 +674,7 @@ li { -If you want, you can even move the reducer to a different file: +Nếu bạn muốn, bạn thậm chí có thể di chuyển reducer sang một file khác: @@ -862,30 +862,30 @@ li { -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. +Component logic có thể dễ đọc hơn khi bạn tách biệt các mối quan tâm như thế này. Bây giờ những event handler chỉ chỉ định _điều gì đã xảy ra_ bằng cách dispatching action, và function reducer xác định _cách state cập nhật_ để phản hồi chúng. -## Comparing `useState` and `useReducer` {/*comparing-usestate-and-usereducer*/} +## So sánh `useState` và `useReducer` {/*comparing-usestate-and-usereducer*/} -Reducers are not without downsides! Here's a few ways you can compare them: +Reducer không phải là không có nhược điểm! Đây là một vài cách bạn có thể so sánh chúng: -- **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! +- **Kích thước code:** Nói chung, với `useState` bạn phải viết ít code hơn từ đầu. Với `useReducer`, bạn phải viết cả function reducer _và_ dispatch action. Tuy nhiên, `useReducer` có thể giúp cắt giảm code nếu nhiều event handler chỉnh sửa state theo cách tương tự. +- **Khả năng đọc:** `useState` rất dễ đọc khi những cập nhật state đơn giản. Khi chúng trở nên phức tạp hơn, chúng có thể làm phình to code component của bạn và làm cho nó khó để quét qua. Trong trường hợp này, `useReducer` cho phép bạn tách biệt một cách sạch sẽ _cách thức_ của logic cập nhật khỏi _điều gì đã xảy ra_ của những event handler. +- **Debugging:** Khi bạn có bug với `useState`, có thể khó để nói _ở đâu_ state được set không chính xác, và _tại sao_. Với `useReducer`, bạn có thể thêm console log vào reducer của mình để thấy mọi cập nhật state, và _tại sao_ nó xảy ra (do `action` nào). Nếu mỗi `action` đúng, bạn sẽ biết rằng lỗi nằm trong chính logic reducer. Tuy nhiên, bạn phải bước qua nhiều code hơn so với `useState`. +- **Testing:** Reducer là một pure function không phụ thuộc vào component của bạn. Điều này có nghĩa là bạn có thể export và test nó riêng biệt trong cô lập. Mặc dù nói chung tốt nhất là test component trong môi trường thực tế hơn, đối với logic cập nhật state phức tạp, có thể hữu ích để khẳng định rằng reducer của bạn trả về một state cụ thể cho một initial state và action cụ thể. +- **Sở thích cá nhân:** Một số người thích reducer, những người khác thì không. Điều đó ổn. Đó là vấn đề sở thích. Bạn luôn có thể chuyển đổi giữa `useState` và `useReducer` qua lại: chúng tương đương! -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. +Chúng tôi khuyên sử dụng reducer nếu bạn thường gặp bug do cập nhật state không chính xác trong một số component, và muốn giới thiệu thêm cấu trúc vào code của nó. Bạn không phải sử dụng reducer cho mọi thứ: cứ thoải mái kết hợp và phối hợp! Bạn thậm chí có thể `useState` và `useReducer` trong cùng một component. -## Writing reducers well {/*writing-reducers-well*/} +## Viết reducer tốt {/*writing-reducers-well*/} -Keep these two tips in mind when writing reducers: +Hãy ghi nhớ hai mẹo này khi viết reducer: -- **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. -- **Each action describes a single user interaction, even if that leads to multiple changes in the data.** 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! +- **Reducer phải là pure.** Tương tự như [state updater function](/learn/queueing-a-series-of-state-updates), reducer chạy trong quá trình render! (Action được xếp hàng cho đến lần render tiếp theo.) Điều này có nghĩa là reducer [phải là pure](/learn/keeping-components-pure)—cùng input luôn dẫn đến cùng output. Chúng không nên gửi request, lên lịch timeout, hoặc thực hiện bất kỳ side effect nào (phép toán ảnh hưởng đến những thứ bên ngoài component). Chúng nên cập nhật [object](/learn/updating-objects-in-state) và [mảng](/learn/updating-arrays-in-state) mà không có mutation. +- **Mỗi action mô tả một tương tác người dùng duy nhất, ngay cả khi điều đó dẫn đến nhiều thay đổi trong dữ liệu.** Ví dụ, nếu người dùng nhấn "Reset" trên một form với năm field được quản lý bởi reducer, việc dispatch một action `reset_form` có ý nghĩa hơn so với năm action `set_field` riêng biệt. Nếu bạn log mọi action trong reducer, log đó nên đủ rõ ràng để bạn tái tạo những tương tác hoặc phản hồi nào đã xảy ra theo thứ tự nào. Điều này giúp với debugging! -## Writing concise reducers with Immer {/*writing-concise-reducers-with-immer*/} +## Viết reducer ngắn gọn với Immer {/*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: +Giống như với [cập nhật object](/learn/updating-objects-in-state#write-concise-update-logic-with-immer) và [mảng](/learn/updating-arrays-in-state#write-concise-update-logic-with-immer) trong state thông thường, bạn có thể sử dụng thư viện Immer để làm cho reducer ngắn gọn hơn. Ở đây, [`useImmerReducer`](https://github.com/immerjs/use-immer#useimmerreducer) cho phép bạn mutate state với `push` hoặc gán `arr[i] =`: @@ -1082,34 +1082,34 @@ li { -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. +Reducer phải là pure, vì vậy chúng không nên mutate state. Nhưng Immer cung cấp cho bạn một object `draft` đặc biệt mà an toàn để mutate. Bên dưới, Immer sẽ tạo một bản copy của state của bạn với những thay đổi bạn đã thực hiện trên `draft`. Đây là lý do tại sao những reducer được quản lý bởi `useImmerReducer` có thể mutate tham số đầu tiên của chúng và không cần return 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. -- Each action describes a single user interaction. -- Use Immer if you want to write reducers in a mutating style. +- Để chuyển đổi từ `useState` sang `useReducer`: + 1. Dispatch action từ những event handler. + 2. Viết một function reducer trả về state tiếp theo cho một state và action đã cho. + 3. Thay thế `useState` bằng `useReducer`. +- Reducer yêu cầu bạn viết nhiều code hơn một chút, nhưng chúng giúp với debugging và testing. +- Reducer phải là pure. +- Mỗi action mô tả một tương tác người dùng duy nhất. +- Sử dụng Immer nếu bạn muốn viết reducer theo kiểu mutating. -#### Dispatch actions from event handlers {/*dispatch-actions-from-event-handlers*/} +#### Dispatch action từ event handler {/*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. +Hiện tại, những event handler trong `ContactList.js` và `Chat.js` có comment `// TODO`. Đây là lý do tại sao việc gõ vào input không hoạt động, và nhấp vào những button không thay đổi người nhận được chọn. -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`. +Thay thế hai `// TODO` này bằng code để `dispatch` những action tương ứng. Để thấy hình dạng mong đợi và loại của những action, kiểm tra reducer trong `messengerReducer.js`. Reducer đã được viết sẵn nên bạn sẽ không cần thay đổi nó. Bạn chỉ cần dispatch những action trong `ContactList.js` và `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. +Function `dispatch` đã có sẵn trong cả hai component này vì nó được truyền như một prop. Vì vậy bạn cần gọi `dispatch` với object action tương ứng. -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: +Để kiểm tra hình dạng object action, bạn có thể nhìn vào reducer và thấy những field `action` nào nó mong đợi thấy. Ví dụ, case `changed_selection` trong reducer trông như thế này: ```js case 'changed_selection': { @@ -1120,7 +1120,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. +Điều này có nghĩa là object action của bạn nên có `type: 'changed_selection'`. Bạn cũng thấy `action.contactId` được sử dụng, nên bạn cần bao gồm một thuộc tính `contactId` vào action của bạn. @@ -1256,7 +1256,7 @@ textarea { -From the reducer code, you can infer that actions need to look like this: +Từ code reducer, bạn có thể suy ra rằng những action cần trông như thế này: ```js // When the user presses "Alice" @@ -1272,7 +1272,7 @@ dispatch({ }); ``` -Here is the example updated to dispatch the corresponding messages: +Đây là ví dụ được cập nhật để dispatch những message tương ứng: @@ -1411,12 +1411,12 @@ textarea { -#### Clear the input on sending a message {/*clear-the-input-on-sending-a-message*/} +#### Xóa input khi gửi message {/*clear-the-input-on-sending-a-message*/} -Currently, pressing "Send" doesn't do anything. Add an event handler to the "Send" button that will: +Hiện tại, nhấn "Send" không làm gì cả. Thêm một event handler vào button "Send" sẽ: -1. Show an `alert` with the recipient's email and the message. -2. Clear the message input. +1. Hiển thị một `alert` với email của người nhận và message. +2. Xóa message input. @@ -1555,7 +1555,7 @@ textarea { -There are a couple of ways you could do it in the "Send" button event handler. One approach is to show an alert and then dispatch an `edited_message` action with an empty `message`: +Có một vài cách bạn có thể làm điều đó trong event handler của button "Send". Một cách tiếp cận là hiển thị alert và sau đó dispatch một action `edited_message` với `message` rỗng: @@ -1701,9 +1701,9 @@ textarea { -This works and clears the input when you hit "Send". +Điều này hoạt động và xóa input khi bạn nhấn "Send". -However, _from the user's perspective_, sending a message is a different action than editing the field. To reflect that, you could instead create a _new_ action called `sent_message`, and handle it separately in the reducer: +Tuy nhiên, _từ quan điểm của người dùng_, gửi message là một action khác so với chỉnh sửa field. Để phản ánh điều đó, bạn có thể tạo một action _mới_ gọi là `sent_message`, và xử lý nó riêng biệt trong reducer: @@ -1854,15 +1854,15 @@ textarea { -The resulting behavior is the same. But keep in mind that action types should ideally describe "what the user did" rather than "how you want the state to change". This makes it easier to later add more features. +Hành vi kết quả là giống nhau. Nhưng hãy nhớ rằng những loại action lý tưởng nên mô tả "người dùng đã làm gì" thay vì "bạn muốn state thay đổi như thế nào". Điều này làm cho việc thêm nhiều tính năng sau này dễ dàng hơn. -With either solution, it's important that you **don't** place the `alert` inside a reducer. The reducer should be a pure function--it should only calculate the next state. It should not "do" anything, including displaying messages to the user. That should happen in the event handler. (To help catch mistakes like this, React will call your reducers multiple times in Strict Mode. This is why, if you put an alert in a reducer, it fires twice.) +Với cả hai giải pháp, điều quan trọng là bạn **không** đặt `alert` bên trong reducer. Reducer nên là một pure function--nó chỉ nên tính toán state tiếp theo. Nó không nên "làm" bất cứ điều gì, bao gồm hiển thị message cho người dùng. Điều đó nên xảy ra trong event handler. (Để giúp bắt những lỗi như thế này, React sẽ gọi những reducer của bạn nhiều lần trong Strict Mode. Đây là lý do tại sao, nếu bạn đặt alert trong reducer, nó kích hoạt hai lần.) -#### Restore input values when switching between tabs {/*restore-input-values-when-switching-between-tabs*/} +#### Khôi phục giá trị input khi chuyển đổi giữa các tab {/*restore-input-values-when-switching-between-tabs*/} -In this example, switching between different recipients always clears the text input: +Trong ví dụ này, chuyển đổi giữa những người nhận khác nhau luôn xóa text input: ```js case 'changed_selection': { @@ -1873,13 +1873,13 @@ case 'changed_selection': { }; ``` -This is because you don't want to share a single message draft between several recipients. But it would be better if your app "remembered" a draft for each contact separately, restoring them when you switch contacts. +Điều này là vì bạn không muốn chia sẻ một message draft duy nhất giữa nhiều người nhận. Nhưng sẽ tốt hơn nếu ứng dụng của bạn "nhớ" một draft cho mỗi contact riêng biệt, khôi phục chúng khi bạn chuyển đổi contact. -Your task is to change the way the state is structured so that you remember a separate message draft _per contact_. You would need to make a few changes to the reducer, the initial state, and the components. +Nhiệm vụ của bạn là thay đổi cách state được cấu trúc để bạn nhớ một message draft riêng biệt _cho mỗi contact_. Bạn sẽ cần thực hiện một vài thay đổi đối với reducer, initial state, và những component. -You can structure your state like this: +Bạn có thể cấu trúc state của mình như thế này: ```js export const initialState = { @@ -1891,7 +1891,7 @@ export const initialState = { }; ``` -The `[key]: value` [computed property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names) syntax can help you update the `messages` object: +Cú pháp `[key]: value` [computed property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names) có thể giúp bạn cập nhật object `messages`: ```js { @@ -2053,7 +2053,7 @@ textarea { -You'll need to update the reducer to store and update a separate message draft per contact: +Bạn sẽ cần cập nhật reducer để lưu trữ và cập nhật một message draft riêng biệt cho mỗi contact: ```js // When the input is edited @@ -2071,13 +2071,13 @@ case 'edited_message': { } ``` -You would also update the `Messenger` component to read the message for the currently selected contact: +Bạn cũng sẽ cập nhật component `Messenger` để đọc message cho contact hiện đang được chọn: ```js const message = state.messages[state.selectedId]; ``` -Here is the complete solution: +Đây là giải pháp hoàn chỉnh: @@ -2237,19 +2237,19 @@ textarea { -Notably, you didn't need to change any of the event handlers to implement this different behavior. Without a reducer, you would have to change every event handler that updates the state. +Đáng chú ý, bạn không cần thay đổi bất kỳ event handler nào để triển khai hành vi khác này. Không có reducer, bạn sẽ phải thay đổi mọi event handler cập nhật state. -#### Implement `useReducer` from scratch {/*implement-usereducer-from-scratch*/} +#### Triển khai `useReducer` từ đầu {/*implement-usereducer-from-scratch*/} -In the earlier examples, you imported the `useReducer` Hook from React. This time, you will implement _the `useReducer` Hook itself!_ Here is a stub to get you started. It shouldn't take more than 10 lines of code. +Trong những ví dụ trước đó, bạn đã import Hook `useReducer` từ React. Lần này, bạn sẽ triển khai _chính Hook `useReducer`!_ Đây là một stub để bạn bắt đầu. Nó không nên mất hơn 10 dòng code. -To test your changes, try typing into the input or select a contact. +Để test những thay đổi của bạn, thử gõ vào input hoặc chọn một contact. -Here is a more detailed sketch of the implementation: +Đây là một bản phác thảo chi tiết hơn về việc triển khai: ```js export function useReducer(reducer, initialState) { @@ -2263,7 +2263,7 @@ export function useReducer(reducer, initialState) { } ``` -Recall that a reducer function takes two arguments--the current state and the action object--and it returns the next state. What should your `dispatch` implementation do with it? +Nhớ rằng một function reducer nhận hai tham số--state hiện tại và object action--và nó trả về state tiếp theo. Việc triển khai `dispatch` của bạn nên làm gì với nó? @@ -2439,7 +2439,7 @@ textarea { -Dispatching an action calls a reducer with the current state and the action, and stores the result as the next state. This is what it looks like in code: +Dispatching một action gọi reducer với state hiện tại và action, và lưu trữ kết quả như state tiếp theo. Đây là cách nó trông trong code: @@ -2614,7 +2614,7 @@ textarea { -Though it doesn't matter in most cases, a slightly more accurate implementation looks like this: +Mặc dù nó không quan trọng trong hầu hết các trường hợp, một triển khai chính xác hơn một chút trông như thế này: ```js function dispatch(action) { @@ -2622,7 +2622,7 @@ function dispatch(action) { } ``` -This is because the dispatched actions are queued until the next render, [similar to the updater functions.](/learn/queueing-a-series-of-state-updates) +Điều này là vì những action được dispatch được xếp hàng cho đến lần render tiếp theo, [tương tự như những updater function.](/learn/queueing-a-series-of-state-updates)