diff --git a/src/content/learn/choosing-the-state-structure.md b/src/content/learn/choosing-the-state-structure.md
index 5be2b4d34..c34189da1 100644
--- a/src/content/learn/choosing-the-state-structure.md
+++ b/src/content/learn/choosing-the-state-structure.md
@@ -1,53 +1,54 @@
---
-title: Choosing the State Structure
+title: Lựa chọn cấu trúc cho state
---
-Structuring state well can make a difference between a component that is pleasant to modify and debug, and one that is a constant source of bugs. Here are some tips you should consider when structuring state.
+Cấu trúc state tốt có thể tạo ra sự khác biệt giữa một component dễ chỉnh sửa và debug và một component bị lỗi liên tục. Sau đây là một số mẹo bạn nên cân nhắc khi cấu trúc state.
-* When to use a single vs multiple state variables
-* What to avoid when organizing state
-* How to fix common issues with the state structure
+* Khi nào nên sử dụng nhiều state hay một state duy nhất cho nhiều giá trị
+* Những điều cần tránh khi tổ chức state
+* Cách để fix những lỗi phổ biến khi cấu trúc state
-## Principles for structuring state {/*principles-for-structuring-state*/}
+## Nguyên tắc khi cấu trúc state {/*principles-for-structuring-state*/}
-When you write a component that holds some state, you'll have to make choices about how many state variables to use and what the shape of their data should be. While it's possible to write correct programs even with a suboptimal state structure, there are a few principles that can guide you to make better choices:
+Khi bạn viết một component có chứa một vài state, bạn sẽ phải đưa ra quyết định về việc có bao nhiêu state cần sử dụng và cấu trúccủa chúng. Mặc dù có thể viết chương trình đúng ngay cả khi cấu trúc state không tối ưu, nhưng có một vài nguyên tắc có thể giúp bạn đưa ra những lựa chọn tốt hơn:
-1. **Group related state.** If you always update two or more state variables at the same time, consider merging them into a single state variable.
-2. **Avoid contradictions in state.** When the state is structured in a way that several pieces of state may contradict and "disagree" with each other, you leave room for mistakes. Try to avoid this.
-3. **Avoid redundant state.** If you can calculate some information from the component's props or its existing state variables during rendering, you should not put that information into that component's state.
-4. **Avoid duplication in state.** When the same data is duplicated between multiple state variables, or within nested objects, it is difficult to keep them in sync. Reduce duplication when you can.
-5. **Avoid deeply nested state.** Deeply hierarchical state is not very convenient to update. When possible, prefer to structure state in a flat way.
+1. **Nhóm các state có liên quan.** Nếu bạn luôn phải cập nhật hai hoặc nhiều hơn state cùng một lúc, hãy nghĩ đến việc gộp chúng vào một state duy nhất.
+2. **Tránh sự mâu thuẫn trong state.** Khi state được cấu trúc sao cho một số phần của state có thể mâu thuẫn và "không đồng ý" với nhau, bạn để lại cơ hội cho lỗi. Hãy cố gắng tránh điều này.
+3. **Tránh dư thừa state.** Nếu bạn có thể tính toán một số thông tin từ props của component hoặc các state hiện tại của nó trong quá trình render, bạn không nên đặt thông tin đó vào state của component đó.
+4. **Tránh trùng lặp trong state.** Khi cùng một data được lặp lại giữa nhiều state hoặc trong các object lồng nhau, rất khó để giữ cho chúng đồng bộ với nhau. Hạn chế sự trùng lặp này khi bạn có thể.
+5. **Tránh lồng state quá sâu.** State có cấu trúc phân cấp sâu rất không thuận tiện để cập nhật. Khi có thể, hãy ưu tiên cấu trúc state theo cách phẳng.
-The goal behind these principles is to *make state easy to update without introducing mistakes*. Removing redundant and duplicate data from state helps ensure that all its pieces stay in sync. This is similar to how a database engineer might want to ["normalize" the database structure](https://docs.microsoft.com/en-us/office/troubleshoot/access/database-normalization-description) to reduce the chance of bugs. To paraphrase Albert Einstein, **"Make your state as simple as it can be--but no simpler."**
+Mục tiêu đằng sau các quy tắc này là *làm cho state dễ dàng cập nhật mà không gây ra lỗi*. Xoá data dư thừa và trùng lặp khỏi state giúp đảm bảo rằng tất cả các phần của nó luông đồng bộ. Điều này gần giống với cách một database engineer muốn ["chuẩn hoá" cấu trúc database](https://docs.microsoft.com/en-us/office/troubleshoot/access/database-normalization-description) để giảm khả năng xảy ra lỗi. Để dùng lời của Albert Einstein, **"Hãy làm cho state của bạn đơn giản nhất có thể--nhưng không đơn giản hơn."**
-Now let's see how these principles apply in action.
-## Group related state {/*group-related-state*/}
+Giờ hãy xem cách các nguyên tắc này được áp dụng trong thực tế.
-You might sometimes be unsure between using a single or multiple state variables.
+## Nhóm các state liên quan {/*group-related-state*/}
-Should you do this?
+Đôi khi bạn có thể không chắc chắn giữa việc sử dụng nhiều state hay một state duy nhất cho nhiều giá trị.
+
+Bạn nên làm như thế này?
```js
const [x, setX] = useState(0);
const [y, setY] = useState(0);
```
-Or this?
+Hay như thế này?
```js
const [position, setPosition] = useState({ x: 0, y: 0 });
```
-Technically, you can use either of these approaches. But **if some two state variables always change together, it might be a good idea to unify them into a single state variable.** Then you won't forget to always keep them in sync, like in this example where moving the cursor updates both coordinates of the red dot:
+Về mặt kỹ thuật, bạn có thể sử dụng một trong hai cách trên. Nhưng **nếu hai state luôn thay đổi cùng nhau, việc gộp chúng lại với nhau có thể là một ý tưởng tốt.** Khi đó, bạn không cần phải lo lắng về việc giữ cho chúng đồng bộ, giống như trong ví dụ dưới đây khi di chuyển con trỏ sẽ cập nhật cả hai tọa độ của chấm đỏ:
@@ -93,17 +94,17 @@ body { margin: 0; padding: 0; height: 250px; }
-Another case where you'll group data into an object or an array is when you don't know how many pieces of state you'll need. For example, it's helpful when you have a form where the user can add custom fields.
+Một trường hợp khác là bạn sẽ nhóm data vào một object hoặc một mảng khi bạn không biết bạn sẽ cần bao nhiêu state. Ví dụ, nó rất hữu ích khi bạn có một form mà người dùng có thể thêm các trường tùy chỉnh.
-If your state variable is an object, remember that [you can't update only one field in it](/learn/updating-objects-in-state) without explicitly copying the other fields. For example, you can't do `setPosition({ x: 100 })` in the above example because it would not have the `y` property at all! Instead, if you wanted to set `x` alone, you would either do `setPosition({ ...position, x: 100 })`, or split them into two state variables and do `setX(100)`.
+Nếu state của bạn là một object, hãy nhớ rằng [bạn không thể chỉ cập nhật một trường của nó](/learn/updating-objects-in-state) mà không phải sao chép các trường khác. Ví dụ, bạn không thể gọi `setPosition({ x: 100 })` trong ví dụ trên vì nó sẽ không có trường `y` nào cả! Thay vào đó, nếu bạn muốn chỉ cập nhật `x`, bạn sẽ phải gọi `setPosition({ ...position, x: 100 })`, hoặc chia chúng thành hai state và gọi `setX(100)`.
-## Avoid contradictions in state {/*avoid-contradictions-in-state*/}
+## Tránh mâu thuẫn trong state {/*avoid-contradictions-in-state*/}
-Here is a hotel feedback form with `isSending` and `isSent` state variables:
+Đây là một form phản hồi của khách sạn với state `isSending` và `isSent`:
@@ -124,12 +125,13 @@ export default function FeedbackForm() {
}
if (isSent) {
- return
Thanks for feedback!
+ return
Cảm ơn bạn đã phản hồi!
}
return (
);
}
@@ -157,9 +159,9 @@ function sendMessage(text) {
-While this code works, it leaves the door open for "impossible" states. For example, if you forget to call `setIsSent` and `setIsSending` together, you may end up in a situation where both `isSending` and `isSent` are `true` at the same time. The more complex your component is, the harder it is to understand what happened.
+Trong khi đoạn code này hoạt động, nó để lại cơ hội cho các trạng thái "không thể xảy ra" xảy ra. Ví dụ, nếu bạn quên gọi `setIsSent` và `setIsSending` cùng một lúc, bạn có thể kết thúc trong tình huống mà cả `isSending` và `isSent` đều là `true` cùng một lúc. Component của bạn càng phức tạp, việc hiểu xem đã xảy ra điều gì càng khó khăn.
-**Since `isSending` and `isSent` should never be `true` at the same time, it is better to replace them with one `status` state variable that may take one of *three* valid states:** `'typing'` (initial), `'sending'`, and `'sent'`:
+**Vì `isSending` và `isSent` không nên cùng `true` trong bất kỳ trường hợp nào, tốt hơn là nó nên được thay thế bởi một state được gọi là `status` và nó có thể mang một trong các giá trị sau:** `'typing'` (ban đầu), `'sending'`, và `'sent'`:
@@ -181,12 +183,12 @@ export default function FeedbackForm() {
const isSent = status === 'sent';
if (isSent) {
- return
Thanks for feedback!
+ return
Cảm ơn bạn đã phản hồi!
}
return (
);
}
@@ -214,20 +216,20 @@ function sendMessage(text) {
-You can still declare some constants for readability:
+Bạn cũng có thể khai báo thêm một số hằng số để dễ đọc:
```js
const isSending = status === 'sending';
const isSent = status === 'sent';
```
-But they're not state variables, so you don't need to worry about them getting out of sync with each other.
+Vì chúng không phải là state, nên bạn không cần phải lo lắng về việc chúng không đồng bộ với nhau.
-## Avoid redundant state {/*avoid-redundant-state*/}
+## Tránh dư thừa state {/*avoid-redundant-state*/}
-If you can calculate some information from the component's props or its existing state variables during rendering, you **should not** put that information into that component's state.
+Nếu bạn có thể tính toán một số thông tin từ props của component hoặc các state hiện tại của nó trong quá trình render, bạn **không nên** đặt thông tin đó vào state của component đó.
-For example, take this form. It works, but can you find any redundant state in it?
+Ví dụ, hãy xem form này. Nó hoạt động, nhưng bạn có thể tìm thấy bất kỳ state nào dư thừa không?
@@ -241,33 +243,33 @@ export default function Form() {
function handleFirstNameChange(e) {
setFirstName(e.target.value);
- setFullName(e.target.value + ' ' + lastName);
+ setFullName(lastName + ' ' + e.target.value);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
- setFullName(firstName + ' ' + e.target.value);
+ setFullName(e.target.value + ' ' + firstName);
}
return (
<>
-
Let’s check you in
+
Đăng ký thông tin
- Your ticket will be issued to: {fullName}
+ Vé của bạn sẽ được cấp cho: {fullName}
>
);
@@ -280,9 +282,9 @@ label { display: block; margin-bottom: 5px; }
-This form has three state variables: `firstName`, `lastName`, and `fullName`. However, `fullName` is redundant. **You can always calculate `fullName` from `firstName` and `lastName` during render, so remove it from state.**
+Form này có chứa ba state: `firstName`, `lastName` và `fullName`. Tuy nhiên, `fullName` là dư thừa. **Bạn luôn có thể tính được `fullName` từ `firstName` và `lastName` khi render, do đó hãy xoá nó khỏi state.**
-This is how you can do it:
+Đây là cách bạn có thể làm điều đó:
@@ -293,7 +295,7 @@ export default function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
- const fullName = firstName + ' ' + lastName;
+ const fullName = lastName + ' ' + firstName;
function handleFirstNameChange(e) {
setFirstName(e.target.value);
@@ -305,23 +307,23 @@ export default function Form() {
return (
<>
-
Let’s check you in
+
Đăng ký thông tin
- Your ticket will be issued to: {fullName}
+ Vé của bạn sẽ được cấp cho: {fullName}
>
);
@@ -334,50 +336,52 @@ label { display: block; margin-bottom: 5px; }
-Here, `fullName` is *not* a state variable. Instead, it's calculated during render:
+Giờ đây, `fullName` *không* là state. Thay vào đó, nó được tính toán trong quá trình render:
```js
-const fullName = firstName + ' ' + lastName;
+const fullName = lastName + ' ' + firstName;
```
As a result, the change handlers don't need to do anything special to update it. When you call `setFirstName` or `setLastName`, you trigger a re-render, and then the next `fullName` will be calculated from the fresh data.
+Do đó, các handler không cần phải làm bất cứ điều gì đặc biệt để cập nhật nó. Khi bạn gọi `setFirstName` hoặc `setLastName`, bạn kích hoạt một lần re-render, và sau đó `fullName` sẽ được tính toán từ dữ liệu mới.
+
-#### Don't mirror props in state {/*don-t-mirror-props-in-state*/}
+#### Đừng sao chép props vào state {/*don-t-mirror-props-in-state*/}
-A common example of redundant state is code like this:
+Một ví dụ cho sự dư thừa state phổ biến là đoạn code như sau:
```js
function Message({ messageColor }) {
const [color, setColor] = useState(messageColor);
```
-Here, a `color` state variable is initialized to the `messageColor` prop. The problem is that **if the parent component passes a different value of `messageColor` later (for example, `'red'` instead of `'blue'`), the `color` *state variable* would not be updated!** The state is only initialized during the first render.
+Ở đây, `color` mang giá trị khỏi tạo là prop `messageColor`. Vấn đề là **nếu component cha truyền một giá trị khác của `messageColor` sau này (ví dụ, `'red'` thay vì `'blue'`), biến `color` *state variable* sẽ không được cập nhật!** State chỉ được khởi tạo trong lần render đầu tiên.
-This is why "mirroring" some prop in a state variable can lead to confusion. Instead, use the `messageColor` prop directly in your code. If you want to give it a shorter name, use a constant:
+Đây là lý do tại sao sao chép một số prop vào một state có thể dẫn đến sự nhầm lẫn. Thay vào đó, hãy sử dụng prop `messageColor` trực tiếp trong code của bạn. Nếu bạn muốn đặt tên ngắn gọn hơn, hãy gán cho nó một hằng số:
```js
function Message({ messageColor }) {
const color = messageColor;
```
-This way it won't get out of sync with the prop passed from the parent component.
+Bằng cách này, nó sẽ đồng bộ với prop được truyền từ component cha.
-"Mirroring" props into state only makes sense when you *want* to ignore all updates for a specific prop. By convention, start the prop name with `initial` or `default` to clarify that its new values are ignored:
+Sao chép props vào state chỉ hợp lý khi bạn *muốn* bỏ qua tất cả các cập nhật cho một prop cụ thể. Theo quy ước, bắt đầu tên prop với `initial` hoặc `default` để làm rõ rằng các giá trị mới của nó bị bỏ qua:
```js
function Message({ initialColor }) {
- // The `color` state variable holds the *first* value of `initialColor`.
- // Further changes to the `initialColor` prop are ignored.
+ // State `color` mang giá trị *đầu tiên* của `initialColor`.
+ // Các thay đổi sau này của prop `initialColor` sẽ bị bỏ qua.
const [color, setColor] = useState(initialColor);
```
-## Avoid duplication in state {/*avoid-duplication-in-state*/}
+## Tránh trùng lặp trong state {/*avoid-duplication-in-state*/}
-This menu list component lets you choose a single travel snack out of several:
+Component Menu này cho phép bạn chọn một món ăn từ danh sách và hiển thị món ăn đã chọn:
@@ -385,9 +389,9 @@ This menu list component lets you choose a single travel snack out of several:
import { useState } from 'react';
const initialItems = [
- { title: 'pretzels', id: 0 },
- { title: 'crispy seaweed', id: 1 },
- { title: 'granola bar', id: 2 },
+ { title: 'phở', id: 0 },
+ { title: 'bún chả', id: 1 },
+ { title: 'bánh mì', id: 2 },
];
export default function Menu() {
@@ -398,7 +402,7 @@ export default function Menu() {
return (
<>
-
>
);
}
@@ -422,9 +426,9 @@ button { margin-top: 10px; }
-Currently, it stores the selected item as an object in the `selectedItem` state variable. However, this is not great: **the contents of the `selectedItem` is the same object as one of the items inside the `items` list.** This means that the information about the item itself is duplicated in two places.
+Hiện tại, nó lưu món ăn được chọn dưới dạng một object trong state `selectedItem`. Tuy nhiên, điều này là không tốt: **nội dung của `selectedItem` giống một object trong danh sách `items`.** Điều này có nghĩa là thông tin về món ăn đó được lặp lại ở hai nơi.
-Why is this a problem? Let's make each item editable:
+Tại sao điều này là một vấn đề? Hãy thử cho phép người dùng chỉnh sửa món ăn trong danh sách:
@@ -432,9 +436,9 @@ Why is this a problem? Let's make each item editable:
import { useState } from 'react';
const initialItems = [
- { title: 'pretzels', id: 0 },
- { title: 'crispy seaweed', id: 1 },
- { title: 'granola bar', id: 2 },
+ { title: 'phở', id: 0 },
+ { title: 'bún chả', id: 1 },
+ { title: 'bánh mì', id: 2 },
];
export default function Menu() {
@@ -458,7 +462,7 @@ export default function Menu() {
return (
<>
-
>
);
}
@@ -487,9 +491,9 @@ button { margin-top: 10px; }
-Notice how if you first click "Choose" on an item and *then* edit it, **the input updates but the label at the bottom does not reflect the edits.** This is because you have duplicated state, and you forgot to update `selectedItem`.
+Hãy để ý là khi bạn nhấn "Chọn" một món ăn *sau đó* chỉnh sửa món đó, **ô input được cập nhật nhưng nhãn ở dưới không phản ánh những chỉnh sửa.** Điều này xảy ra vì bạn đã trùng lặp state, và bạn đã quên cập nhật `selectedItem`.
-Although you could update `selectedItem` too, an easier fix is to remove duplication. In this example, instead of a `selectedItem` object (which creates a duplication with objects inside `items`), you hold the `selectedId` in state, and *then* get the `selectedItem` by searching the `items` array for an item with that ID:
+Mặc dù bạn cũng có thể cập nhật `selectedItem`, một cách fix dễ hơn là xoá bỏ sự trùng lặp. Trong ví dụ này, thay vì một object `selectedItem` (tạo ra sự trùng lặp với các object trong `items`), bạn giữ `selectedId` trong state, và *sau đó* lấy `selectedItem` bằng cách tìm kiếm mảng `items` để tìm một item với ID đó:
@@ -497,9 +501,9 @@ Although you could update `selectedItem` too, an easier fix is to remove duplica
import { useState } from 'react';
const initialItems = [
- { title: 'pretzels', id: 0 },
- { title: 'crispy seaweed', id: 1 },
- { title: 'granola bar', id: 2 },
+ { title: 'phở', id: 0 },
+ { title: 'bún chả', id: 1 },
+ { title: 'bánh mì', id: 2 },
];
export default function Menu() {
@@ -525,7 +529,7 @@ export default function Menu() {
return (
<>
-
>
);
}
@@ -554,23 +558,23 @@ button { margin-top: 10px; }
-The state used to be duplicated like this:
+State được sử dụng trước đây trông như thế này:
-* `items = [{ id: 0, title: 'pretzels'}, ...]`
-* `selectedItem = {id: 0, title: 'pretzels'}`
+* `items = [{ id: 0, title: 'phở' }, ...]`
+* `selectedItem = { id: 0, title: 'phở' }`
-But after the change it's like this:
+Nhưng sau khi thay đổi, nó trông như thế này:
-* `items = [{ id: 0, title: 'pretzels'}, ...]`
+* `items = [{ id: 0, title: 'phở'}, ...]`
* `selectedId = 0`
-The duplication is gone, and you only keep the essential state!
+Sự trùng lặp đã biến mất, và bạn chỉ giữ lại state cần thiết!
-Now if you edit the *selected* item, the message below will update immediately. This is because `setItems` triggers a re-render, and `items.find(...)` would find the item with the updated title. You didn't need to hold *the selected item* in state, because only the *selected ID* is essential. The rest could be calculated during render.
+Giờ nếu bạn chỉnh sửa món ăn *đã chọn*, nội dung tin nhắn bên dưới sẽ cập nhật ngay lập tức. Điều này xảy ra vì `setItems` kích hoạt một lần re-render, và `items.find(...)` sẽ tìm thấy món ăn với tiêu đề đã cập nhật. Bạn không cần giữ lại *món đã chọn* trong state, vì chỉ *ID đã chọn* mới là cần thiết. Phần còn lại có thể được tính toán trong quá trình render.
-## Avoid deeply nested state {/*avoid-deeply-nested-state*/}
+## Tránh sử dụng state lồng nhau quá sâu {/*avoid-deeply-nested-state*/}
-Imagine a travel plan consisting of planets, continents, and countries. You might be tempted to structure its state using nested objects and arrays, like in this example:
+Hãy tưởng tượng một kế hoạch du lịch bao gồm các hành tinh, châu lục và quốc gia. Bạn có thể muốn cấu trúc state của nó bằng cách sử dụng các object và mảng lồng nhau, như trong ví dụ này:
@@ -599,7 +603,7 @@ export default function TravelPlan() {
const planets = plan.childPlaces;
return (
<>
-
Places to visit
+
Địa điểm tham quan
{planets.map(place => (
@@ -616,10 +620,10 @@ export const initialTravelPlan = {
title: '(Root)',
childPlaces: [{
id: 1,
- title: 'Earth',
+ title: 'Trái Đất',
childPlaces: [{
id: 2,
- title: 'Africa',
+ title: 'Châu Phi',
childPlaces: [{
id: 3,
title: 'Botswana',
@@ -646,12 +650,12 @@ export const initialTravelPlan = {
childPlaces: []
}, {
id: 9,
- title: 'South Africa',
+ title: 'Nam Phi',
childPlaces: []
}]
}, {
id: 10,
- title: 'Americas',
+ title: 'Châu Mỹ',
childPlaces: [{
id: 11,
title: 'Argentina',
@@ -687,14 +691,14 @@ export const initialTravelPlan = {
}]
}, {
id: 19,
- title: 'Asia',
+ title: 'Châu Á',
childPlaces: [{
id: 20,
- title: 'China',
+ title: 'Trung Quốc',
childPlaces: []
}, {
id: 21,
- title: 'India',
+ title: 'Ấn Độ',
childPlaces: []
}, {
id: 22,
@@ -702,31 +706,31 @@ export const initialTravelPlan = {
childPlaces: []
}, {
id: 23,
- title: 'South Korea',
+ title: 'Hàn Quốc',
childPlaces: []
}, {
id: 24,
- title: 'Thailand',
+ title: 'Thái Lan',
childPlaces: []
}, {
id: 25,
- title: 'Vietnam',
+ title: 'Việt Nam',
childPlaces: []
}]
}, {
id: 26,
- title: 'Europe',
+ title: 'Châu Âu',
childPlaces: [{
id: 27,
title: 'Croatia',
childPlaces: [],
}, {
id: 28,
- title: 'France',
+ title: 'Pháp',
childPlaces: [],
}, {
id: 29,
- title: 'Germany',
+ title: 'Đức',
childPlaces: [],
}, {
id: 30,
@@ -742,15 +746,15 @@ export const initialTravelPlan = {
childPlaces: [],
}, {
id: 33,
- title: 'Turkey',
+ title: 'Thổ Nhĩ Kỳ',
childPlaces: [],
}]
}, {
id: 34,
- title: 'Oceania',
+ title: 'Châu Đại Dương',
childPlaces: [{
id: 35,
- title: 'Australia',
+ title: 'Úc',
childPlaces: [],
}, {
id: 36,
@@ -780,7 +784,7 @@ export const initialTravelPlan = {
}]
}, {
id: 42,
- title: 'Moon',
+ title: 'Mặt Trăng',
childPlaces: [{
id: 43,
title: 'Rheita',
@@ -796,7 +800,7 @@ export const initialTravelPlan = {
}]
}, {
id: 46,
- title: 'Mars',
+ title: 'Sao Hoả',
childPlaces: [{
id: 47,
title: 'Corn Town',
@@ -812,11 +816,11 @@ export const initialTravelPlan = {
-Now let's say you want to add a button to delete a place you've already visited. How would you go about it? [Updating nested state](/learn/updating-objects-in-state#updating-a-nested-object) involves making copies of objects all the way up from the part that changed. Deleting a deeply nested place would involve copying its entire parent place chain. Such code can be very verbose.
+Giờ giả sử bạn muốn thêm một cái nút để xoá một địa điểm mà bạn đã ghé thăm. Bạn sẽ làm như thế nào? [Cập nhật state lồng nhau](/learn/updating-objects-in-state#updating-a-nested-object) liên quan đến việc sao chép các object từ phần đã thay đổi. Xoá một địa điểm sâu sẽ liên quan đến việc sao chép toàn bộ chuỗi cha của nó. Đoạn code như vậy có thể rất dài dòng.
-**If the state is too nested to update easily, consider making it "flat".** Here is one way you can restructure this data. Instead of a tree-like structure where each `place` has an array of *its child places*, you can have each place hold an array of *its child place IDs*. Then store a mapping from each place ID to the corresponding place.
+**Nếu state quá lồng nhau để cập nhật dễ dàng, hãy xem xét làm cho nó "phẳng".** Dưới đây là một cách bạn có thể cấu trúc lại dữ liệu này. Thay vì một cấu trúc giống cây với mỗi `place` có một mảng *các địa điểm con của nó*, bạn có thể làm cho mỗi địa điểm giữ một mảng *các ID địa điểm con của nó*. Sau đó map từng ID đến địa điểm tương ứng.
-This data restructuring might remind you of seeing a database table:
+Cấu trúc mới này có thể khiến bạn nhớ đến việc xem một bảng cơ sở dữ liệu:
@@ -851,7 +855,7 @@ export default function TravelPlan() {
const planetIds = root.childIds;
return (
<>
-
Places to visit
+
Địa điểm tham quan
{planetIds.map(id => (
-**Now that the state is "flat" (also known as "normalized"), updating nested items becomes easier.**
+**Giờ khi state đã "phẳng" (còn được gọi là "chuẩn hoá"), việc cập nhật các mục lồng nhau trở nên dễ dàng hơn.**
-In order to remove a place now, you only need to update two levels of state:
+Để xoá một địa điểm bây giờ, bạn chỉ cần thực hiện hai cập nhật state:
-- The updated version of its *parent* place should exclude the removed ID from its `childIds` array.
-- The updated version of the root "table" object should include the updated version of the parent place.
+- Xoá ID của nó khỏi mảng `childIds` của địa điểm cha.
+- Cập nhật object state gốc để nó không chứa địa điểm đó nữa.
-Here is an example of how you could go about it:
+Đây là một ví dụ về cách bạn có thể thực hiện điều đó:
@@ -1138,17 +1142,17 @@ export default function TravelPlan() {
function handleComplete(parentId, childId) {
const parent = plan[parentId];
- // Create a new version of the parent place
- // that doesn't include this child ID.
+ // Tạo một phiên bản mới của địa điểm cha
+ // mà không bao gồm ID con này.
const nextParent = {
...parent,
childIds: parent.childIds
.filter(id => id !== childId)
};
- // Update the root state object...
+ // Cập nhật object state gốc...
setPlan({
...plan,
- // ...so that it has the updated parent.
+ // ...để nó có cha đã cập nhật.
[parentId]: nextParent
});
}
@@ -1157,7 +1161,7 @@ export default function TravelPlan() {
const planetIds = root.childIds;
return (
<>
-
Places to visit
+
Địa điểm tham quan
{planetIds.map(id => (
{
onComplete(parentId, id);
}}>
- Complete
+ Hoàn thành
{childIds.length > 0 &&
@@ -1211,12 +1215,12 @@ export const initialTravelPlan = {
},
1: {
id: 1,
- title: 'Earth',
+ title: 'Trái Đất',
childIds: [2, 10, 19, 26, 34]
},
2: {
id: 2,
- title: 'Africa',
+ title: 'Châu Phi',
childIds: [3, 4, 5, 6 , 7, 8, 9]
},
3: {
@@ -1251,12 +1255,12 @@ export const initialTravelPlan = {
},
9: {
id: 9,
- title: 'South Africa',
+ title: 'Nam Phi',
childIds: []
},
10: {
id: 10,
- title: 'Americas',
+ title: 'Châu Mỹ',
childIds: [11, 12, 13, 14, 15, 16, 17, 18],
},
11: {
@@ -1301,17 +1305,17 @@ export const initialTravelPlan = {
},
19: {
id: 19,
- title: 'Asia',
+ title: 'Châu Á',
childIds: [20, 21, 22, 23, 24, 25],
},
20: {
id: 20,
- title: 'China',
+ title: 'Trung Quốc',
childIds: []
},
21: {
id: 21,
- title: 'India',
+ title: 'Ấn Độ',
childIds: []
},
22: {
@@ -1321,22 +1325,22 @@ export const initialTravelPlan = {
},
23: {
id: 23,
- title: 'South Korea',
+ title: 'Hàn Quốc',
childIds: []
},
24: {
id: 24,
- title: 'Thailand',
+ title: 'Thái Lan',
childIds: []
},
25: {
id: 25,
- title: 'Vietnam',
+ title: 'Việt Nam',
childIds: []
},
26: {
id: 26,
- title: 'Europe',
+ title: 'Châu Âu',
childIds: [27, 28, 29, 30, 31, 32, 33],
},
27: {
@@ -1346,12 +1350,12 @@ export const initialTravelPlan = {
},
28: {
id: 28,
- title: 'France',
+ title: 'Pháp',
childIds: []
},
29: {
id: 29,
- title: 'Germany',
+ title: 'Đức',
childIds: []
},
30: {
@@ -1371,17 +1375,17 @@ export const initialTravelPlan = {
},
33: {
id: 33,
- title: 'Turkey',
+ title: 'Thổ Nhĩ Kỳ',
childIds: []
},
34: {
id: 34,
- title: 'Oceania',
+ title: 'Châu Đại Dương',
childIds: [35, 36, 37, 38, 39, 40, 41],
},
35: {
id: 35,
- title: 'Australia',
+ title: 'Úc',
childIds: []
},
36: {
@@ -1400,7 +1404,7 @@ export const initialTravelPlan = {
childIds: []
},
39: {
- id: 39,
+ id: 40,
title: 'Hawaii (the USA)',
childIds: []
},
@@ -1416,7 +1420,7 @@ export const initialTravelPlan = {
},
42: {
id: 42,
- title: 'Moon',
+ title: 'Mặt Trăng',
childIds: [43, 44, 45]
},
43: {
@@ -1436,7 +1440,7 @@ export const initialTravelPlan = {
},
46: {
id: 46,
- title: 'Mars',
+ title: 'Sao Hoả',
childIds: [47, 48]
},
47: {
@@ -1458,13 +1462,13 @@ button { margin: 10px; }
-You can nest state as much as you like, but making it "flat" can solve numerous problems. It makes state easier to update, and it helps ensure you don't have duplication in different parts of a nested object.
+Bạn có thể lồng state bao nhiêu cũng được, nhưng làm cho nó "phẳng" có thể giải quyết nhiều vấn đề. Nó giúp cập nhật state dễ dàng hơn, và đảm bảo bạn không có sự trùng lặp ở các phần khác nhau của một object lồng nhau.
-#### Improving memory usage {/*improving-memory-usage*/}
+#### Cải thiện việc sử dụng bộ nhớ {/*improving-memory-usage*/}
-Ideally, you would also remove the deleted items (and their children!) from the "table" object to improve memory usage. This version does that. It also [uses Immer](/learn/updating-objects-in-state#write-concise-update-logic-with-immer) to make the update logic more concise.
+Một cách lý tưởng, bạn cũng nên xoá các mục đã xoá (và các mục con của chúng!) khỏi object "bảng" để cải thiện việc sử dụng bộ nhớ. Phiên bản này thực hiện điều đó. Nó cũng [sử dụng Immer](/learn/updating-objects-in-state#write-concise-update-logic-with-immer) để làm cho logic cập nhật ngắn gọn hơn.
@@ -1477,12 +1481,12 @@ export default function TravelPlan() {
function handleComplete(parentId, childId) {
updatePlan(draft => {
- // Remove from the parent place's child IDs.
+ // Xoá khỏi mảng ID con của địa điểm cha.
const parent = draft[parentId];
parent.childIds = parent.childIds
.filter(id => id !== childId);
- // Forget this place and all its subtree.
+ // Đệ quy để xoá tất cả các địa điểm con.
deleteAllChildren(childId);
function deleteAllChildren(id) {
const place = draft[id];
@@ -1496,7 +1500,7 @@ export default function TravelPlan() {
const planetIds = root.childIds;
return (
<>
-
Places to visit
+
Địa điểm tham quan
{planetIds.map(id => (
{
onComplete(parentId, id);
}}>
- Complete
+ Hoàn thành
{childIds.length > 0 &&
@@ -1550,12 +1554,12 @@ export const initialTravelPlan = {
},
1: {
id: 1,
- title: 'Earth',
+ title: 'Trái Đất',
childIds: [2, 10, 19, 26, 34]
},
2: {
id: 2,
- title: 'Africa',
+ title: 'Châu Phi',
childIds: [3, 4, 5, 6 , 7, 8, 9]
},
3: {
@@ -1590,12 +1594,12 @@ export const initialTravelPlan = {
},
9: {
id: 9,
- title: 'South Africa',
+ title: 'Nam Phi',
childIds: []
},
10: {
id: 10,
- title: 'Americas',
+ title: 'Châu Mỹ',
childIds: [11, 12, 13, 14, 15, 16, 17, 18],
},
11: {
@@ -1640,17 +1644,17 @@ export const initialTravelPlan = {
},
19: {
id: 19,
- title: 'Asia',
- childIds: [20, 21, 22, 23, 24, 25,],
+ title: 'Châu Á',
+ childIds: [20, 21, 22, 23, 24, 25],
},
20: {
id: 20,
- title: 'China',
+ title: 'Trung Quốc',
childIds: []
},
21: {
id: 21,
- title: 'India',
+ title: 'Ấn Độ',
childIds: []
},
22: {
@@ -1660,22 +1664,22 @@ export const initialTravelPlan = {
},
23: {
id: 23,
- title: 'South Korea',
+ title: 'Hàn Quốc',
childIds: []
},
24: {
id: 24,
- title: 'Thailand',
+ title: 'Thái Lan',
childIds: []
},
25: {
id: 25,
- title: 'Vietnam',
+ title: 'Việt Nam',
childIds: []
},
26: {
id: 26,
- title: 'Europe',
+ title: 'Châu Âu',
childIds: [27, 28, 29, 30, 31, 32, 33],
},
27: {
@@ -1685,12 +1689,12 @@ export const initialTravelPlan = {
},
28: {
id: 28,
- title: 'France',
+ title: 'Pháp',
childIds: []
},
29: {
id: 29,
- title: 'Germany',
+ title: 'Đức',
childIds: []
},
30: {
@@ -1710,17 +1714,17 @@ export const initialTravelPlan = {
},
33: {
id: 33,
- title: 'Turkey',
+ title: 'Thổ Nhĩ Kỳ',
childIds: []
},
34: {
id: 34,
- title: 'Oceania',
- childIds: [35, 36, 37, 38, 39, 40,, 41],
+ title: 'Châu Đại Dương',
+ childIds: [35, 36, 37, 38, 39, 40, 41],
},
35: {
id: 35,
- title: 'Australia',
+ title: 'Úc',
childIds: []
},
36: {
@@ -1739,7 +1743,7 @@ export const initialTravelPlan = {
childIds: []
},
39: {
- id: 39,
+ id: 40,
title: 'Hawaii (the USA)',
childIds: []
},
@@ -1755,7 +1759,7 @@ export const initialTravelPlan = {
},
42: {
id: 42,
- title: 'Moon',
+ title: 'Mặt Trăng',
childIds: [43, 44, 45]
},
43: {
@@ -1775,7 +1779,7 @@ export const initialTravelPlan = {
},
46: {
id: 46,
- title: 'Mars',
+ title: 'Sao Hoả',
childIds: [47, 48]
},
47: {
@@ -1817,25 +1821,25 @@ button { margin: 10px; }
-Sometimes, you can also reduce state nesting by moving some of the nested state into the child components. This works well for ephemeral UI state that doesn't need to be stored, like whether an item is hovered.
+Đôi khi, bạn cũng có thể giảm thiểu việc lồng state bằng cách di chuyển một số state lồng vào các component con. Điều này hoạt động tốt cho state UI tạm thời mà không cần lưu trữ, như việc kiểm tra một item có được hover hay không.
-* If two state variables always update together, consider merging them into one.
-* Choose your state variables carefully to avoid creating "impossible" states.
-* Structure your state in a way that reduces the chances that you'll make a mistake updating it.
-* Avoid redundant and duplicate state so that you don't need to keep it in sync.
-* Don't put props *into* state unless you specifically want to prevent updates.
-* For UI patterns like selection, keep ID or index in state instead of the object itself.
-* If updating deeply nested state is complicated, try flattening it.
+* Nếu hai state luôn luôn cập nhật cùng nhau, hãy xem xét việc gộp chúng thành một.
+* Chọn cẩn thận các biến state của bạn để tránh tạo ra các trạng thái "không thể xảy ra".
+* Cấu trúc state của bạn sao cho giảm khả năng bạn sẽ mắc lỗi khi cập nhật nó.
+* Tránh state trùng lặp và dư thừa để bạn không cần phải đồng bộ chúng.
+* Không đặt props *vào* state trừ khi bạn muốn ngăn cập nhật.
+* Đối với UI như chọn lựa, giữ ID hoặc index trong state thay vì chính object đó.
+* Nếu việc cập nhật state lồng nhau quá phức tạp, hãy thử làm phẳng nó.
-#### Fix a component that's not updating {/*fix-a-component-thats-not-updating*/}
+#### Fix một component không cập nhật {/*fix-a-component-thats-not-updating*/}
-This `Clock` component receives two props: `color` and `time`. When you select a different color in the select box, the `Clock` component receives a different `color` prop from its parent component. However, for some reason, the displayed color doesn't update. Why? Fix the problem.
+Component `Clock` này nhận vào hai props: `color` và `time`. Khi bạn chọn một màu khác trong hộp chọn, component `Clock` nhận một `color` khác từ component cha của nó. Tuy nhiên, vì một lý do nào đó, màu hiển thị không cập nhật. Tại sao? Hãy fix lỗi này.
@@ -1873,7 +1877,7 @@ export default function App() {
return (