From 058a1e41afa342180a57876ceb2c9f1b93bd727b Mon Sep 17 00:00:00 2001
From: KotonoSora <26807417+KotonoSora@users.noreply.github.com>
Date: Tue, 17 Jun 2025 07:39:43 +0700
Subject: [PATCH] feat(i18n): src/content/learn/you-might-not-need-an-effect.md
from English to Vietnamese
---
.../learn/you-might-not-need-an-effect.md | 268 +++++++++---------
1 file changed, 134 insertions(+), 134 deletions(-)
diff --git a/src/content/learn/you-might-not-need-an-effect.md b/src/content/learn/you-might-not-need-an-effect.md
index a009793ab..ae24edf55 100644
--- a/src/content/learn/you-might-not-need-an-effect.md
+++ b/src/content/learn/you-might-not-need-an-effect.md
@@ -1,38 +1,38 @@
---
-title: 'You Might Not Need an Effect'
+title: 'Bạn có thể không cần Effect'
---
-Effects are an escape hatch from the React paradigm. They let you "step outside" of React and synchronize your components with some external system like a non-React widget, network, or the browser DOM. If there is no external system involved (for example, if you want to update a component's state when some props or state change), you shouldn't need an Effect. Removing unnecessary Effects will make your code easier to follow, faster to run, and less error-prone.
+Effect là một lối thoát khỏi tư duy React. Chúng cho phép bạn "bước ra ngoài" React và đồng bộ hóa các component của bạn với một hệ thống bên ngoài như widget không phải React, mạng, hoặc DOM trình duyệt. Nếu không có hệ thống bên ngoài nào liên quan (ví dụ, nếu bạn muốn cập nhật state của component khi một số props hoặc state thay đổi), bạn không nên cần Effect. Loại bỏ những Effect không cần thiết sẽ làm cho code của bạn dễ theo dõi hơn, chạy nhanh hơn, và ít lỗi hơn.
-* Why and how to remove unnecessary Effects from your components
-* How to cache expensive computations without Effects
-* How to reset and adjust component state without Effects
-* How to share logic between event handlers
-* Which logic should be moved to event handlers
-* How to notify parent components about changes
+* Tại sao và cách loại bỏ những Effect không cần thiết khỏi các component của bạn
+* Cách cache những phép tính đắt đỏ mà không cần Effect
+* Cách reset và điều chỉnh state component mà không cần Effect
+* Cách chia sẻ logic giữa các event handler
+* Logic nào nên được chuyển vào event handler
+* Cách thông báo cho component cha về những thay đổi
-## How to remove unnecessary Effects {/*how-to-remove-unnecessary-effects*/}
+## Cách loại bỏ những Effect không cần thiết {/*how-to-remove-unnecessary-effects*/}
-There are two common cases in which you don't need Effects:
+Có hai trường hợp phổ biến mà bạn không cần Effect:
-* **You don't need Effects to transform data for rendering.** For example, let's say you want to filter a list before displaying it. You might feel tempted to write an Effect that updates a state variable when the list changes. However, this is inefficient. When you update the state, React will first call your component functions to calculate what should be on the screen. Then React will ["commit"](/learn/render-and-commit) these changes to the DOM, updating the screen. Then React will run your Effects. If your Effect *also* immediately updates the state, this restarts the whole process from scratch! To avoid the unnecessary render passes, transform all the data at the top level of your components. That code will automatically re-run whenever your props or state change.
-* **You don't need Effects to handle user events.** For example, let's say you want to send an `/api/buy` POST request and show a notification when the user buys a product. In the Buy button click event handler, you know exactly what happened. By the time an Effect runs, you don't know *what* the user did (for example, which button was clicked). This is why you'll usually handle user events in the corresponding event handlers.
+* **Bạn không cần Effect để biến đổi dữ liệu cho việc render.** Ví dụ, giả sử bạn muốn lọc một danh sách trước khi hiển thị nó. Bạn có thể cảm thấy muốn viết một Effect để cập nhật một biến state khi danh sách thay đổi. Tuy nhiên, điều này không hiệu quả. Khi bạn cập nhật state, React sẽ trước tiên gọi các function component của bạn để tính toán những gì nên hiển thị trên màn hình. Sau đó React sẽ ["commit"](/learn/render-and-commit) những thay đổi này vào DOM, cập nhật màn hình. Sau đó React sẽ chạy các Effect của bạn. Nếu Effect của bạn *cũng* ngay lập tức cập nhật state, điều này khởi động lại toàn bộ quá trình từ đầu! Để tránh những lần render không cần thiết, hãy biến đổi tất cả dữ liệu ở cấp cao nhất của các component của bạn. Code đó sẽ tự động chạy lại bất cứ khi nào props hoặc state của bạn thay đổi.
+* **Bạn không cần Effect để xử lý sự kiện người dùng.** Ví dụ, giả sử bạn muốn gửi một POST request `/api/buy` và hiển thị một thông báo khi người dùng mua một sản phẩm. Trong event handler click của nút Buy, bạn biết chính xác điều gì đã xảy ra. Vào thời điểm một Effect chạy, bạn không biết *gì* mà người dùng đã làm (ví dụ, nút nào đã được click). Đây là lý do tại sao bạn thường sẽ xử lý sự kiện người dùng trong các event handler tương ứng.
-You *do* need Effects to [synchronize](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events) with external systems. For example, you can write an Effect that keeps a jQuery widget synchronized with the React state. You can also fetch data with Effects: for example, you can synchronize the search results with the current search query. Keep in mind that modern [frameworks](/learn/start-a-new-react-project#production-grade-react-frameworks) provide more efficient built-in data fetching mechanisms than writing Effects directly in your components.
+Bạn *có* cần Effect để [đồng bộ hóa](/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events) với các hệ thống bên ngoài. Ví dụ, bạn có thể viết một Effect giữ cho widget jQuery đồng bộ với state React. Bạn cũng có thể fetch dữ liệu với Effect: ví dụ, bạn có thể đồng bộ hóa kết quả tìm kiếm với truy vấn tìm kiếm hiện tại. Hãy nhớ rằng các [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) hiện đại cung cấp các cơ chế fetch dữ liệu tích hợp hiệu quả hơn so với việc viết Effect trực tiếp trong các component của bạn.
-To help you gain the right intuition, let's look at some common concrete examples!
+Để giúp bạn có được trực giác đúng, hãy xem một số ví dụ cụ thể phổ biến!
-### Updating state based on props or state {/*updating-state-based-on-props-or-state*/}
+### Cập nhật state dựa trên props hoặc state {/*updating-state-based-on-props-or-state*/}
-Suppose you have a component with two state variables: `firstName` and `lastName`. You want to calculate a `fullName` from them by concatenating them. Moreover, you'd like `fullName` to update whenever `firstName` or `lastName` change. Your first instinct might be to add a `fullName` state variable and update it in an Effect:
+Giả sử bạn có một component với hai biến state: `firstName` và `lastName`. Bạn muốn tính toán một `fullName` từ chúng bằng cách nối chúng lại. Hơn nữa, bạn muốn `fullName` cập nhật bất cứ khi nào `firstName` hoặc `lastName` thay đổi. Bản năng đầu tiên của bạn có thể là thêm một biến state `fullName` và cập nhật nó trong một Effect:
```js {5-9}
function Form() {
@@ -48,7 +48,7 @@ function Form() {
}
```
-This is more complicated than necessary. It is inefficient too: it does an entire render pass with a stale value for `fullName`, then immediately re-renders with the updated value. Remove the state variable and the Effect:
+Điều này phức tạp hơn mức cần thiết. Nó cũng không hiệu quả: nó thực hiện một lần render pass hoàn chỉnh với giá trị cũ cho `fullName`, sau đó ngay lập tức render lại với giá trị đã cập nhật. Hãy loại bỏ biến state và Effect:
```js {4-5}
function Form() {
@@ -60,11 +60,11 @@ function Form() {
}
```
-**When something can be calculated from the existing props or state, [don't put it in state.](/learn/choosing-the-state-structure#avoid-redundant-state) Instead, calculate it during rendering.** This makes your code faster (you avoid the extra "cascading" updates), simpler (you remove some code), and less error-prone (you avoid bugs caused by different state variables getting out of sync with each other). If this approach feels new to you, [Thinking in React](/learn/thinking-in-react#step-3-find-the-minimal-but-complete-representation-of-ui-state) explains what should go into state.
+**Khi một cái gì đó có thể được tính toán từ props hoặc state hiện có, [đừng đặt nó vào state.](/learn/choosing-the-state-structure#avoid-redundant-state) Thay vào đó, hãy tính toán nó trong quá trình render.** Điều này làm cho code của bạn nhanh hơn (bạn tránh được các cập nhật "liên tục" bổ sung), đơn giản hơn (bạn loại bỏ một số code), và ít lỗi hơn (bạn tránh được những bug gây ra bởi các biến state khác nhau không đồng bộ với nhau). Nếu cách tiếp cận này cảm thấy mới lạ với bạn, [Thinking in React](/learn/thinking-in-react#step-3-find-the-minimal-but-complete-representation-of-ui-state) giải thích những gì nên đưa vào state.
-### Caching expensive calculations {/*caching-expensive-calculations*/}
+### Cache những phép tính đắt đỏ {/*caching-expensive-calculations*/}
-This component computes `visibleTodos` by taking the `todos` it receives by props and filtering them according to the `filter` prop. You might feel tempted to store the result in state and update it from an Effect:
+Component này tính toán `visibleTodos` bằng cách lấy `todos` mà nó nhận từ props và lọc chúng theo `filter` prop. Bạn có thể cảm thấy muốn lưu trữ kết quả trong state và cập nhật nó từ một Effect:
```js {4-8}
function TodoList({ todos, filter }) {
@@ -80,7 +80,7 @@ function TodoList({ todos, filter }) {
}
```
-Like in the earlier example, this is both unnecessary and inefficient. First, remove the state and the Effect:
+Giống như trong ví dụ trước đó, điều này vừa không cần thiết vừa không hiệu quả. Trước tiên, hãy loại bỏ state và Effect:
```js {3-4}
function TodoList({ todos, filter }) {
@@ -91,9 +91,9 @@ function TodoList({ todos, filter }) {
}
```
-Usually, this code is fine! But maybe `getFilteredTodos()` is slow or you have a lot of `todos`. In that case you don't want to recalculate `getFilteredTodos()` if some unrelated state variable like `newTodo` has changed.
+Thường thì code này ổn! Nhưng có thể `getFilteredTodos()` chậm hoặc bạn có rất nhiều `todos`. Trong trường hợp đó bạn không muốn tính toán lại `getFilteredTodos()` nếu một biến state không liên quan như `newTodo` đã thay đổi.
-You can cache (or ["memoize"](https://en.wikipedia.org/wiki/Memoization)) an expensive calculation by wrapping it in a [`useMemo`](/reference/react/useMemo) Hook:
+Bạn có thể cache (hoặc ["memoize"](https://en.wikipedia.org/wiki/Memoization)) một phép tính đắt đỏ bằng cách bọc nó trong một [`useMemo`](/reference/react/useMemo) Hook:
```js {5-8}
import { useMemo, useState } from 'react';
@@ -121,15 +121,15 @@ function TodoList({ todos, filter }) {
}
```
-**This tells React that you don't want the inner function to re-run unless either `todos` or `filter` have changed.** React will remember the return value of `getFilteredTodos()` during the initial render. During the next renders, it will check if `todos` or `filter` are different. If they're the same as last time, `useMemo` will return the last result it has stored. But if they are different, React will call the inner function again (and store its result).
+**Điều này nói với React rằng bạn không muốn function bên trong chạy lại trừ khi `todos` hoặc `filter` đã thay đổi.** React sẽ nhớ giá trị trả về của `getFilteredTodos()` trong lần render đầu tiên. Trong những lần render tiếp theo, nó sẽ kiểm tra xem `todos` hoặc `filter` có khác không. Nếu chúng giống như lần trước, `useMemo` sẽ trả về kết quả cuối cùng mà nó đã lưu trữ. Nhưng nếu chúng khác, React sẽ gọi function bên trong một lần nữa (và lưu trữ kết quả của nó).
-The function you wrap in [`useMemo`](/reference/react/useMemo) runs during rendering, so this only works for [pure calculations.](/learn/keeping-components-pure)
+Function mà bạn bọc trong [`useMemo`](/reference/react/useMemo) chạy trong quá trình render, vì vậy điều này chỉ hoạt động cho [các phép tính thuần túy.](/learn/keeping-components-pure)
-#### How to tell if a calculation is expensive? {/*how-to-tell-if-a-calculation-is-expensive*/}
+#### Làm sao để biết một phép tính có đắt đỏ không? {/*how-to-tell-if-a-calculation-is-expensive*/}
-In general, unless you're creating or looping over thousands of objects, it's probably not expensive. If you want to get more confidence, you can add a console log to measure the time spent in a piece of code:
+Nói chung, trừ khi bạn đang tạo hoặc lặp qua hàng nghìn đối tượng, nó có thể không đắt đỏ. Nếu bạn muốn có thêm sự tự tin, bạn có thể thêm một console log để đo thời gian dành cho một đoạn code:
```js {1,3}
console.time('filter array');
@@ -137,27 +137,27 @@ const visibleTodos = getFilteredTodos(todos, filter);
console.timeEnd('filter array');
```
-Perform the interaction you're measuring (for example, typing into the input). You will then see logs like `filter array: 0.15ms` in your console. If the overall logged time adds up to a significant amount (say, `1ms` or more), it might make sense to memoize that calculation. As an experiment, you can then wrap the calculation in `useMemo` to verify whether the total logged time has decreased for that interaction or not:
+Thực hiện tương tác mà bạn đang đo (ví dụ, gõ vào input). Sau đó bạn sẽ thấy các log như `filter array: 0.15ms` trong console của bạn. Nếu tổng thời gian log cộng lại đạt đến một số lượng đáng kể (chẳng hạn, `1ms` hoặc hơn), có thể có ý nghĩa để memoize phép tính đó. Như một thử nghiệm, sau đó bạn có thể bọc phép tính trong `useMemo` để xác minh xem tổng thời gian log có giảm cho tương tác đó hay không:
```js
console.time('filter array');
const visibleTodos = useMemo(() => {
- return getFilteredTodos(todos, filter); // Skipped if todos and filter haven't changed
+ return getFilteredTodos(todos, filter); // Bỏ qua nếu todos và filter không thay đổi
}, [todos, filter]);
console.timeEnd('filter array');
```
-`useMemo` won't make the *first* render faster. It only helps you skip unnecessary work on updates.
+`useMemo` sẽ không làm cho lần render *đầu tiên* nhanh hơn. Nó chỉ giúp bạn bỏ qua công việc không cần thiết trong các lần cập nhật.
-Keep in mind that your machine is probably faster than your users' so it's a good idea to test the performance with an artificial slowdown. For example, Chrome offers a [CPU Throttling](https://developer.chrome.com/blog/new-in-devtools-61/#throttling) option for this.
+Hãy nhớ rằng máy của bạn có thể nhanh hơn máy của người dùng vì vậy nên test hiệu suất với việc làm chậm nhân tạo. Ví dụ, Chrome cung cấp một tùy chọn [CPU Throttling](https://developer.chrome.com/blog/new-in-devtools-61/#throttling) cho việc này.
-Also note that measuring performance in development will not give you the most accurate results. (For example, when [Strict Mode](/reference/react/StrictMode) is on, you will see each component render twice rather than once.) To get the most accurate timings, build your app for production and test it on a device like your users have.
+Cũng lưu ý rằng đo hiệu suất trong quá trình phát triển sẽ không cho bạn kết quả chính xác nhất. (Ví dụ, khi [Strict Mode](/reference/react/StrictMode) được bật, bạn sẽ thấy mỗi component render hai lần thay vì một lần.) Để có được thời gian chính xác nhất, hãy build ứng dụng của bạn cho production và test nó trên một thiết bị giống như người dùng của bạn có.
-### Resetting all state when a prop changes {/*resetting-all-state-when-a-prop-changes*/}
+### Reset tất cả state khi một prop thay đổi {/*resetting-all-state-when-a-prop-changes*/}
-This `ProfilePage` component receives a `userId` prop. The page contains a comment input, and you use a `comment` state variable to hold its value. One day, you notice a problem: when you navigate from one profile to another, the `comment` state does not get reset. As a result, it's easy to accidentally post a comment on a wrong user's profile. To fix the issue, you want to clear out the `comment` state variable whenever the `userId` changes:
+Component `ProfilePage` này nhận một `userId` prop. Trang chứa một input comment, và bạn sử dụng một biến state `comment` để giữ giá trị của nó. Một ngày, bạn nhận thấy một vấn đề: khi bạn điều hướng từ một profile này sang profile khác, state `comment` không được reset. Kết quả là, dễ dàng vô tình đăng một comment trên profile của người dùng sai. Để khắc phục sự cố, bạn muốn xóa biến state `comment` bất cứ khi nào `userId` thay đổi:
```js {4-7}
export default function ProfilePage({ userId }) {
@@ -171,9 +171,9 @@ export default function ProfilePage({ userId }) {
}
```
-This is inefficient because `ProfilePage` and its children will first render with the stale value, and then render again. It is also complicated because you'd need to do this in *every* component that has some state inside `ProfilePage`. For example, if the comment UI is nested, you'd want to clear out nested comment state too.
+Điều này không hiệu quả vì `ProfilePage` và các children của nó sẽ trước tiên render với giá trị cũ, và sau đó render lại. Nó cũng phức tạp vì bạn sẽ cần phải làm điều này trong *mọi* component có một số state bên trong `ProfilePage`. Ví dụ, nếu UI comment được lồng nhau, bạn cũng sẽ muốn xóa state comment lồng nhau.
-Instead, you can tell React that each user's profile is conceptually a _different_ profile by giving it an explicit key. Split your component in two and pass a `key` attribute from the outer component to the inner one:
+Thay vào đó, bạn có thể nói với React rằng profile của mỗi người dùng về mặt khái niệm là một profile *khác nhau* bằng cách cho nó một key rõ ràng. Chia component của bạn thành hai và truyền một thuộc tính `key` từ component bên ngoài vào component bên trong:
```js {5,11-12}
export default function ProfilePage({ userId }) {
@@ -192,15 +192,15 @@ function Profile({ userId }) {
}
```
-Normally, React preserves the state when the same component is rendered in the same spot. **By passing `userId` as a `key` to the `Profile` component, you're asking React to treat two `Profile` components with different `userId` as two different components that should not share any state.** Whenever the key (which you've set to `userId`) changes, React will recreate the DOM and [reset the state](/learn/preserving-and-resetting-state#option-2-resetting-state-with-a-key) of the `Profile` component and all of its children. Now the `comment` field will clear out automatically when navigating between profiles.
+Thông thường, React bảo toàn state khi cùng một component được render ở cùng vị trí. **Bằng cách truyền `userId` như một `key` cho component `Profile`, bạn đang yêu cầu React coi hai component `Profile` với `userId` khác nhau như hai component khác nhau không nên chia sẻ bất kỳ state nào.** Bất cứ khi nào key (mà bạn đã đặt thành `userId`) thay đổi, React sẽ tạo lại DOM và [reset state](/learn/preserving-and-resetting-state#option-2-resetting-state-with-a-key) của component `Profile` và tất cả các children của nó. Bây giờ trường `comment` sẽ tự động xóa khi điều hướng giữa các profile.
-Note that in this example, only the outer `ProfilePage` component is exported and visible to other files in the project. Components rendering `ProfilePage` don't need to pass the key to it: they pass `userId` as a regular prop. The fact `ProfilePage` passes it as a `key` to the inner `Profile` component is an implementation detail.
+Lưu ý rằng trong ví dụ này, chỉ component `ProfilePage` bên ngoài được export và hiển thị với các file khác trong dự án. Các component render `ProfilePage` không cần truyền key cho nó: chúng truyền `userId` như một prop thông thường. Việc `ProfilePage` truyền nó như một `key` cho component `Profile` bên trong là một chi tiết triển khai.
-### Adjusting some state when a prop changes {/*adjusting-some-state-when-a-prop-changes*/}
+### Điều chỉnh một số state khi một prop thay đổi {/*adjusting-some-state-when-a-prop-changes*/}
-Sometimes, you might want to reset or adjust a part of the state on a prop change, but not all of it.
+Đôi khi, bạn có thể muốn reset hoặc điều chỉnh một phần của state khi prop thay đổi, nhưng không phải tất cả.
-This `List` component receives a list of `items` as a prop, and maintains the selected item in the `selection` state variable. You want to reset the `selection` to `null` whenever the `items` prop receives a different array:
+Component `List` này nhận một list `items` như một prop, và duy trì item được chọn trong biến state `selection`. Bạn muốn reset `selection` thành `null` bất cứ khi nào `items` prop nhận một array khác:
```js {5-8}
function List({ items }) {
@@ -215,9 +215,9 @@ function List({ items }) {
}
```
-This, too, is not ideal. Every time the `items` change, the `List` and its child components will render with a stale `selection` value at first. Then React will update the DOM and run the Effects. Finally, the `setSelection(null)` call will cause another re-render of the `List` and its child components, restarting this whole process again.
+Điều này cũng không lý tưởng. Mỗi khi `items` thay đổi, `List` và các component con của nó sẽ render trước với giá trị `selection` cũ. Sau đó React sẽ cập nhật DOM và chạy các Effect. Cuối cùng, lời gọi `setSelection(null)` sẽ gây ra một lần re-render khác của `List` và các component con của nó, khởi động lại toàn bộ quá trình này một lần nữa.
-Start by deleting the Effect. Instead, adjust the state directly during rendering:
+Bắt đầu bằng cách xóa Effect. Thay vào đó, điều chỉnh state trực tiếp trong quá trình render:
```js {5-11}
function List({ items }) {
@@ -250,11 +250,11 @@ function List({ items }) {
}
```
-Now there is no need to "adjust" the state at all. If the item with the selected ID is in the list, it remains selected. If it's not, the `selection` calculated during rendering will be `null` because no matching item was found. This behavior is different, but arguably better because most changes to `items` preserve the selection.
+Bây giờ không cần "điều chỉnh" state nữa. Nếu item với ID được chọn có trong danh sách, nó vẫn được chọn. Nếu không, `selection` được tính toán trong quá trình render sẽ là `null` vì không tìm thấy item nào khớp. Hành vi này khác, nhưng có thể tốt hơn vì hầu hết các thay đổi đối với `items` bảo toàn selection.
-### Sharing logic between event handlers {/*sharing-logic-between-event-handlers*/}
+### Chia sẻ logic giữa các event handler {/*sharing-logic-between-event-handlers*/}
-Let's say you have a product page with two buttons (Buy and Checkout) that both let you buy that product. You want to show a notification whenever the user puts the product in the cart. Calling `showNotification()` in both buttons' click handlers feels repetitive so you might be tempted to place this logic in an Effect:
+Giả sử bạn có một trang sản phẩm với hai nút (Buy và Checkout) đều cho phép bạn mua sản phẩm đó. Bạn muốn hiển thị một thông báo bất cứ khi nào người dùng đặt sản phẩm vào giỏ hàng. Gọi `showNotification()` trong click handler của cả hai nút cảm thấy lặp lại vì vậy bạn có thể bị cám dỗ đặt logic này trong một Effect:
```js {2-7}
function ProductPage({ product, addToCart }) {
@@ -277,9 +277,9 @@ function ProductPage({ product, addToCart }) {
}
```
-This Effect is unnecessary. It will also most likely cause bugs. For example, let's say that your app "remembers" the shopping cart between the page reloads. If you add a product to the cart once and refresh the page, the notification will appear again. It will keep appearing every time you refresh that product's page. This is because `product.isInCart` will already be `true` on the page load, so the Effect above will call `showNotification()`.
+Effect này không cần thiết. Nó cũng rất có thể gây ra bug. Ví dụ, giả sử ứng dụng của bạn "nhớ" giỏ hàng giữa các lần reload trang. Nếu bạn thêm một sản phẩm vào giỏ hàng một lần và làm mới trang, thông báo sẽ xuất hiện lại. Nó sẽ tiếp tục xuất hiện mỗi khi bạn làm mới trang của sản phẩm đó. Điều này là do `product.isInCart` sẽ đã là `true` khi trang load, vì vậy Effect ở trên sẽ gọi `showNotification()`.
-**When you're not sure whether some code should be in an Effect or in an event handler, ask yourself *why* this code needs to run. Use Effects only for code that should run *because* the component was displayed to the user.** In this example, the notification should appear because the user *pressed the button*, not because the page was displayed! Delete the Effect and put the shared logic into a function called from both event handlers:
+**Khi bạn không chắc liệu một code nào đó nên ở trong Effect hay trong event handler, hãy tự hỏi *tại sao* code này cần chạy. Chỉ sử dụng Effect cho code nên chạy *vì* component được hiển thị cho người dùng.** Trong ví dụ này, thông báo nên xuất hiện vì người dùng *nhấn nút*, không phải vì trang được hiển thị! Xóa Effect và đặt logic được chia sẻ vào một function được gọi từ cả hai event handler:
```js {2-6,9,13}
function ProductPage({ product, addToCart }) {
@@ -301,11 +301,11 @@ function ProductPage({ product, addToCart }) {
}
```
-This both removes the unnecessary Effect and fixes the bug.
+Điều này vừa loại bỏ Effect không cần thiết và sửa bug.
-### Sending a POST request {/*sending-a-post-request*/}
+### Gửi POST request {/*sending-a-post-request*/}
-This `Form` component sends two kinds of POST requests. It sends an analytics event when it mounts. When you fill in the form and click the Submit button, it will send a POST request to the `/api/register` endpoint:
+Component `Form` này gửi hai loại POST request. Nó gửi một sự kiện analytics khi nó mount. Khi bạn điền vào form và click nút Submit, nó sẽ gửi một POST request đến endpoint `/api/register`:
```js {5-8,10-16}
function Form() {
@@ -333,11 +333,11 @@ function Form() {
}
```
-Let's apply the same criteria as in the example before.
+Hãy áp dụng cùng tiêu chí như trong ví dụ trước.
-The analytics POST request should remain in an Effect. This is because the _reason_ to send the analytics event is that the form was displayed. (It would fire twice in development, but [see here](/learn/synchronizing-with-effects#sending-analytics) for how to deal with that.)
+POST request analytics nên vẫn ở trong một Effect. Điều này là do *lý do* để gửi sự kiện analytics là form đã được hiển thị. (Nó sẽ chạy hai lần trong quá trình phát triển, nhưng [xem ở đây](/learn/synchronizing-with-effects#sending-analytics) để biết cách xử lý điều đó.)
-However, the `/api/register` POST request is not caused by the form being _displayed_. You only want to send the request at one specific moment in time: when the user presses the button. It should only ever happen _on that particular interaction_. Delete the second Effect and move that POST request into the event handler:
+Tuy nhiên, POST request `/api/register` không được gây ra bởi form được *hiển thị*. Bạn chỉ muốn gửi request tại một thời điểm cụ thể: khi người dùng nhấn nút. Nó chỉ nên xảy ra *trong tương tác cụ thể đó*. Xóa Effect thứ hai và chuyển POST request đó vào event handler:
```js {12-13}
function Form() {
@@ -358,11 +358,11 @@ function Form() {
}
```
-When you choose whether to put some logic into an event handler or an Effect, the main question you need to answer is _what kind of logic_ it is from the user's perspective. If this logic is caused by a particular interaction, keep it in the event handler. If it's caused by the user _seeing_ the component on the screen, keep it in the Effect.
+Khi bạn chọn có nên đặt một logic nào đó vào event handler hay Effect, câu hỏi chính bạn cần trả lời là *loại logic* nào từ góc độ người dùng. Nếu logic này được gây ra bởi một tương tác cụ thể, hãy giữ nó trong event handler. Nếu nó được gây ra bởi việc người dùng *nhìn thấy* component trên màn hình, hãy giữ nó trong Effect.
-### Chains of computations {/*chains-of-computations*/}
+### Chuỗi các phép tính {/*chains-of-computations*/}
-Sometimes you might feel tempted to chain Effects that each adjust a piece of state based on other state:
+Đôi khi bạn có thể cảm thấy muốn chain các Effect mà mỗi Effect điều chỉnh một phần state dựa trên state khác:
```js {7-29}
function Game() {
@@ -406,13 +406,13 @@ function Game() {
// ...
```
-There are two problems with this code.
+Có hai vấn đề với code này.
-The first problem is that it is very inefficient: the component (and its children) have to re-render between each `set` call in the chain. In the example above, in the worst case (`setCard` → render → `setGoldCardCount` → render → `setRound` → render → `setIsGameOver` → render) there are three unnecessary re-renders of the tree below.
+Vấn đề đầu tiên là nó rất không hiệu quả: component (và các children của nó) phải re-render giữa mỗi lời gọi `set` trong chuỗi. Trong ví dụ trên, trong trường hợp tệ nhất (`setCard` → render → `setGoldCardCount` → render → `setRound` → render → `setIsGameOver` → render) có ba lần re-render không cần thiết của cây bên dưới.
-The second problem is that even if it weren't slow, as your code evolves, you will run into cases where the "chain" you wrote doesn't fit the new requirements. Imagine you are adding a way to step through the history of the game moves. You'd do it by updating each state variable to a value from the past. However, setting the `card` state to a value from the past would trigger the Effect chain again and change the data you're showing. Such code is often rigid and fragile.
+Vấn đề thứ hai là ngay cả khi nó không chậm, khi code của bạn phát triển, bạn sẽ gặp phải những trường hợp mà "chuỗi" bạn viết không phù hợp với yêu cầu mới. Hãy tưởng tượng bạn đang thêm cách để bước qua lịch sử các nước đi của game. Bạn sẽ làm điều đó bằng cách cập nhật mỗi biến state thành một giá trị từ quá khứ. Tuy nhiên, việc đặt state `card` thành một giá trị từ quá khứ sẽ kích hoạt chuỗi Effect một lần nữa và thay đổi dữ liệu bạn đang hiển thị. Code như vậy thường cứng nhắc và dễ vỡ.
-In this case, it's better to calculate what you can during rendering, and adjust the state in the event handler:
+Trong trường hợp này, tốt hơn là tính toán những gì bạn có thể trong quá trình render, và điều chỉnh state trong event handler:
```js {6-7,14-26}
function Game() {
@@ -446,17 +446,17 @@ function Game() {
// ...
```
-This is a lot more efficient. Also, if you implement a way to view game history, now you will be able to set each state variable to a move from the past without triggering the Effect chain that adjusts every other value. If you need to reuse logic between several event handlers, you can [extract a function](#sharing-logic-between-event-handlers) and call it from those handlers.
+Điều này hiệu quả hơn rất nhiều. Ngoài ra, nếu bạn triển khai cách để xem lịch sử game, bây giờ bạn sẽ có thể đặt mỗi biến state thành một nước đi từ quá khứ mà không kích hoạt chuỗi Effect điều chỉnh mọi giá trị khác. Nếu bạn cần tái sử dụng logic giữa nhiều event handler, bạn có thể [trích xuất một function](#sharing-logic-between-event-handlers) và gọi nó từ những handler đó.
-Remember that inside event handlers, [state behaves like a snapshot.](/learn/state-as-a-snapshot) For example, even after you call `setRound(round + 1)`, the `round` variable will reflect the value at the time the user clicked the button. If you need to use the next value for calculations, define it manually like `const nextRound = round + 1`.
+Hãy nhớ rằng bên trong event handler, [state hoạt động như một snapshot.](/learn/state-as-a-snapshot) Ví dụ, ngay cả sau khi bạn gọi `setRound(round + 1)`, biến `round` sẽ phản ánh giá trị tại thời điểm người dùng click nút. Nếu bạn cần sử dụng giá trị tiếp theo cho các phép tính, hãy định nghĩa nó thủ công như `const nextRound = round + 1`.
-In some cases, you *can't* calculate the next state directly in the event handler. For example, imagine a form with multiple dropdowns where the options of the next dropdown depend on the selected value of the previous dropdown. Then, a chain of Effects is appropriate because you are synchronizing with network.
+Trong một số trường hợp, bạn *không thể* tính toán state tiếp theo trực tiếp trong event handler. Ví dụ, hãy tưởng tượng một form với nhiều dropdown mà các tùy chọn của dropdown tiếp theo phụ thuộc vào giá trị được chọn của dropdown trước đó. Khi đó, một chuỗi Effect là phù hợp vì bạn đang đồng bộ hóa với mạng.
-### Initializing the application {/*initializing-the-application*/}
+### Khởi tạo ứng dụng {/*initializing-the-application*/}
-Some logic should only run once when the app loads.
+Một số logic chỉ nên chạy một lần khi ứng dụng load.
-You might be tempted to place it in an Effect in the top-level component:
+Bạn có thể bị cám dỗ đặt nó trong một Effect trong component cấp cao nhất:
```js {2-6}
function App() {
@@ -469,9 +469,9 @@ function App() {
}
```
-However, you'll quickly discover that it [runs twice in development.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) This can cause issues--for example, maybe it invalidates the authentication token because the function wasn't designed to be called twice. In general, your components should be resilient to being remounted. This includes your top-level `App` component.
+Tuy nhiên, bạn sẽ nhanh chóng phát hiện ra rằng nó [chạy hai lần trong quá trình phát triển.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) Điều này có thể gây ra vấn đề--ví dụ, có thể nó làm vô hiệu hóa authentication token vì function không được thiết kế để được gọi hai lần. Nói chung, các component của bạn nên có khả năng chịu đựng việc được remount. Điều này bao gồm cả component `App` cấp cao nhất của bạn.
-Although it may not ever get remounted in practice in production, following the same constraints in all components makes it easier to move and reuse code. If some logic must run *once per app load* rather than *once per component mount*, add a top-level variable to track whether it has already executed:
+Mặc dù nó có thể không bao giờ được remount trong thực tế trong production, việc tuân theo các ràng buộc tương tự trong tất cả các component giúp việc di chuyển và tái sử dụng code dễ dàng hơn. Nếu một số logic phải chạy *một lần mỗi lần load ứng dụng* thay vì *một lần mỗi lần mount component*, hãy thêm một biến cấp cao để theo dõi xem nó đã được thực thi chưa:
```js {1,5-6,10}
let didInit = false;
@@ -489,7 +489,7 @@ function App() {
}
```
-You can also run it during module initialization and before the app renders:
+Bạn cũng có thể chạy nó trong quá trình khởi tạo module và trước khi ứng dụng render:
```js {1,5}
if (typeof window !== 'undefined') { // Check if we're running in the browser.
@@ -503,11 +503,11 @@ function App() {
}
```
-Code at the top level runs once when your component is imported--even if it doesn't end up being rendered. To avoid slowdown or surprising behavior when importing arbitrary components, don't overuse this pattern. Keep app-wide initialization logic to root component modules like `App.js` or in your application's entry point.
+Code ở cấp cao nhất chạy một lần khi component của bạn được import--ngay cả khi nó không được render cuối cùng. Để tránh làm chậm hoặc hành vi bất ngờ khi import các component tùy ý, đừng lạm dụng pattern này. Giữ logic khởi tạo toàn ứng dụng cho các module component gốc như `App.js` hoặc trong entry point của ứng dụng của bạn.
-### Notifying parent components about state changes {/*notifying-parent-components-about-state-changes*/}
+### Thông báo cho component cha về những thay đổi state {/*notifying-parent-components-about-state-changes*/}
-Let's say you're writing a `Toggle` component with an internal `isOn` state which can be either `true` or `false`. There are a few different ways to toggle it (by clicking or dragging). You want to notify the parent component whenever the `Toggle` internal state changes, so you expose an `onChange` event and call it from an Effect:
+Giả sử bạn đang viết một component `Toggle` với state nội bộ `isOn` có thể là `true` hoặc `false`. Có một vài cách khác nhau để toggle nó (bằng cách click hoặc kéo). Bạn muốn thông báo cho component cha bất cứ khi nào state nội bộ `Toggle` thay đổi, vì vậy bạn expose một event `onChange` và gọi nó từ một Effect:
```js {4-7}
function Toggle({ onChange }) {
@@ -534,9 +534,9 @@ function Toggle({ onChange }) {
}
```
-Like earlier, this is not ideal. The `Toggle` updates its state first, and React updates the screen. Then React runs the Effect, which calls the `onChange` function passed from a parent component. Now the parent component will update its own state, starting another render pass. It would be better to do everything in a single pass.
+Giống như trước đó, điều này không lý tưởng. `Toggle` cập nhật state của nó trước, và React cập nhật màn hình. Sau đó React chạy Effect, gọi function `onChange` được truyền từ component cha. Bây giờ component cha sẽ cập nhật state của chính nó, bắt đầu một render pass khác. Sẽ tốt hơn nếu làm mọi thứ trong một lần pass.
-Delete the Effect and instead update the state of *both* components within the same event handler:
+Xóa Effect và thay vào đó cập nhật state của *cả hai* component trong cùng một event handler:
```js {5-7,11,16,18}
function Toggle({ onChange }) {
@@ -564,9 +564,9 @@ function Toggle({ onChange }) {
}
```
-With this approach, both the `Toggle` component and its parent component update their state during the event. React [batches updates](/learn/queueing-a-series-of-state-updates) from different components together, so there will only be one render pass.
+Với cách tiếp cận này, cả component `Toggle` và component cha của nó đều cập nhật state trong sự kiện. React [gom nhóm các cập nhật](/learn/queueing-a-series-of-state-updates) từ các component khác nhau lại với nhau, vì vậy sẽ chỉ có một render pass.
-You might also be able to remove the state altogether, and instead receive `isOn` from the parent component:
+Bạn cũng có thể loại bỏ hoàn toàn state, và thay vào đó nhận `isOn` từ component cha:
```js {1,2}
// ✅ Also good: the component is fully controlled by its parent
@@ -587,11 +587,11 @@ function Toggle({ isOn, onChange }) {
}
```
-["Lifting state up"](/learn/sharing-state-between-components) lets the parent component fully control the `Toggle` by toggling the parent's own state. This means the parent component will have to contain more logic, but there will be less state overall to worry about. Whenever you try to keep two different state variables synchronized, try lifting state up instead!
+["Nâng state lên"](/learn/sharing-state-between-components) cho phép component cha kiểm soát hoàn toàn `Toggle` bằng cách toggle state của chính cha. Điều này có nghĩa là component cha sẽ phải chứa nhiều logic hơn, nhưng sẽ có ít state tổng thể hơn để lo lắng. Bất cứ khi nào bạn cố gắng giữ hai biến state khác nhau đồng bộ, hãy thử nâng state lên thay thế!
-### Passing data to the parent {/*passing-data-to-the-parent*/}
+### Truyền dữ liệu cho cha {/*passing-data-to-the-parent*/}
-This `Child` component fetches some data and then passes it to the `Parent` component in an Effect:
+Component `Child` này fetch một số dữ liệu và sau đó truyền nó cho component `Parent` trong một Effect:
```js {9-14}
function Parent() {
@@ -612,7 +612,7 @@ function Child({ onFetched }) {
}
```
-In React, data flows from the parent components to their children. When you see something wrong on the screen, you can trace where the information comes from by going up the component chain until you find which component passes the wrong prop or has the wrong state. When child components update the state of their parent components in Effects, the data flow becomes very difficult to trace. Since both the child and the parent need the same data, let the parent component fetch that data, and *pass it down* to the child instead:
+Trong React, dữ liệu chảy từ các component cha xuống children của chúng. Khi bạn thấy điều gì đó sai trên màn hình, bạn có thể theo dõi thông tin đến từ đâu bằng cách đi lên chuỗi component cho đến khi bạn tìm thấy component nào truyền prop sai hoặc có state sai. Khi các component con cập nhật state của component cha trong Effect, data flow trở nên rất khó theo dõi. Vì cả con và cha đều cần cùng dữ liệu, hãy để component cha fetch dữ liệu đó, và *truyền nó xuống* cho con thay thế:
```js {4-5}
function Parent() {
@@ -627,11 +627,11 @@ function Child({ data }) {
}
```
-This is simpler and keeps the data flow predictable: the data flows down from the parent to the child.
+Điều này đơn giản hơn và giữ cho data flow có thể dự đoán được: dữ liệu chảy xuống từ cha đến con.
-### Subscribing to an external store {/*subscribing-to-an-external-store*/}
+### Subscribe vào external store {/*subscribing-to-an-external-store*/}
-Sometimes, your components may need to subscribe to some data outside of the React state. This data could be from a third-party library or a built-in browser API. Since this data can change without React's knowledge, you need to manually subscribe your components to it. This is often done with an Effect, for example:
+Đôi khi, các component của bạn có thể cần subscribe vào một số dữ liệu bên ngoài state React. Dữ liệu này có thể đến từ thư viện bên thứ ba hoặc API trình duyệt tích hợp. Vì dữ liệu này có thể thay đổi mà React không biết, bạn cần manually subscribe các component của bạn vào nó. Điều này thường được thực hiện với một Effect, ví dụ:
```js {2-17}
function useOnlineStatus() {
@@ -660,9 +660,9 @@ function ChatIndicator() {
}
```
-Here, the component subscribes to an external data store (in this case, the browser `navigator.onLine` API). Since this API does not exist on the server (so it can't be used for the initial HTML), initially the state is set to `true`. Whenever the value of that data store changes in the browser, the component updates its state.
+Ở đây, component subscribe vào một external data store (trong trường hợp này là API `navigator.onLine` của trình duyệt). Vì API này không tồn tại trên server (vì vậy nó không thể được sử dụng cho HTML ban đầu), ban đầu state được đặt thành `true`. Bất cứ khi nào giá trị của data store đó thay đổi trong trình duyệt, component cập nhật state của nó.
-Although it's common to use Effects for this, React has a purpose-built Hook for subscribing to an external store that is preferred instead. Delete the Effect and replace it with a call to [`useSyncExternalStore`](/reference/react/useSyncExternalStore):
+Mặc dù việc sử dụng Effect cho điều này là phổ biến, React có một Hook được xây dựng có mục đích để subscribe vào external store và được ưa chuộng thay thế. Xóa Effect và thay thế nó bằng một lời gọi đến [`useSyncExternalStore`](/reference/react/useSyncExternalStore):
```js {11-16}
function subscribe(callback) {
@@ -689,11 +689,11 @@ function ChatIndicator() {
}
```
-This approach is less error-prone than manually syncing mutable data to React state with an Effect. Typically, you'll write a custom Hook like `useOnlineStatus()` above so that you don't need to repeat this code in the individual components. [Read more about subscribing to external stores from React components.](/reference/react/useSyncExternalStore)
+Cách tiếp cận này ít lỗi hơn so với việc manually đồng bộ hóa dữ liệu có thể thay đổi với state React bằng Effect. Thông thường, bạn sẽ viết một custom Hook như `useOnlineStatus()` ở trên để bạn không cần lặp lại code này trong các component riêng lẻ. [Đọc thêm về việc subscribe vào external store từ các component React.](/reference/react/useSyncExternalStore)
-### Fetching data {/*fetching-data*/}
+### Fetch dữ liệu {/*fetching-data*/}
-Many apps use Effects to kick off data fetching. It is quite common to write a data fetching Effect like this:
+Nhiều ứng dụng sử dụng Effect để khởi động fetch dữ liệu. Việc viết một Effect fetch dữ liệu như thế này khá phổ biến:
```js {5-10}
function SearchResults({ query }) {
@@ -714,15 +714,15 @@ function SearchResults({ query }) {
}
```
-You *don't* need to move this fetch to an event handler.
+Bạn *không* cần chuyển fetch này sang event handler.
-This might seem like a contradiction with the earlier examples where you needed to put the logic into the event handlers! However, consider that it's not *the typing event* that's the main reason to fetch. Search inputs are often prepopulated from the URL, and the user might navigate Back and Forward without touching the input.
+Điều này có thể trông như một mâu thuẫn với các ví dụ trước đó khi bạn cần đặt logic vào event handler! Tuy nhiên, hãy xem xét rằng không phải *sự kiện gõ* là lý do chính để fetch. Input tìm kiếm thường được điền sẵn từ URL, và người dùng có thể điều hướng Back và Forward mà không chạm vào input.
-It doesn't matter where `page` and `query` come from. While this component is visible, you want to keep `results` [synchronized](/learn/synchronizing-with-effects) with data from the network for the current `page` and `query`. This is why it's an Effect.
+Không quan trọng `page` và `query` đến từ đâu. Trong khi component này hiển thị, bạn muốn giữ `results` [đồng bộ](/learn/synchronizing-with-effects) với dữ liệu từ mạng cho `page` và `query` hiện tại. Đây là lý do tại sao nó là một Effect.
-However, the code above has a bug. Imagine you type `"hello"` fast. Then the `query` will change from `"h"`, to `"he"`, `"hel"`, `"hell"`, and `"hello"`. This will kick off separate fetches, but there is no guarantee about which order the responses will arrive in. For example, the `"hell"` response may arrive *after* the `"hello"` response. Since it will call `setResults()` last, you will be displaying the wrong search results. This is called a ["race condition"](https://en.wikipedia.org/wiki/Race_condition): two different requests "raced" against each other and came in a different order than you expected.
+Tuy nhiên, code trên có một bug. Hãy tưởng tượng bạn gõ `"hello"` nhanh. Khi đó `query` sẽ thay đổi từ `"h"`, đến `"he"`, `"hel"`, `"hell"`, và `"hello"`. Điều này sẽ khởi động các fetch riêng biệt, nhưng không có gì đảm bảo về thứ tự mà các response sẽ đến. Ví dụ, response `"hell"` có thể đến *sau* response `"hello"`. Vì nó sẽ gọi `setResults()` cuối cùng, bạn sẽ hiển thị kết quả tìm kiếm sai. Điều này được gọi là ["race condition"](https://en.wikipedia.org/wiki/Race_condition): hai request khác nhau "đua" với nhau và đến theo thứ tự khác với mong đợi của bạn.
-**To fix the race condition, you need to [add a cleanup function](/learn/synchronizing-with-effects#fetching-data) to ignore stale responses:**
+**Để sửa race condition, bạn cần [thêm một cleanup function](/learn/synchronizing-with-effects#fetching-data) để bỏ qua các response cũ:**
```js {5,7,9,11-13}
function SearchResults({ query }) {
@@ -747,13 +747,13 @@ function SearchResults({ query }) {
}
```
-This ensures that when your Effect fetches data, all responses except the last requested one will be ignored.
+Điều này đảm bảo rằng khi Effect của bạn fetch dữ liệu, tất cả các response trừ response cuối cùng được yêu cầu sẽ bị bỏ qua.
-Handling race conditions is not the only difficulty with implementing data fetching. You might also want to think about caching responses (so that the user can click Back and see the previous screen instantly), how to fetch data on the server (so that the initial server-rendered HTML contains the fetched content instead of a spinner), and how to avoid network waterfalls (so that a child can fetch data without waiting for every parent).
+Xử lý race condition không phải là khó khăn duy nhất khi triển khai fetch dữ liệu. Bạn có thể cũng muốn nghĩ về việc cache response (để người dùng có thể click Back và thấy màn hình trước đó ngay lập tức), cách fetch dữ liệu trên server (để HTML được render ban đầu từ server chứa nội dung đã fetch thay vì một spinner), và cách tránh network waterfall (để con có thể fetch dữ liệu mà không cần chờ mọi cha).
-**These issues apply to any UI library, not just React. Solving them is not trivial, which is why modern [frameworks](/learn/start-a-new-react-project#production-grade-react-frameworks) provide more efficient built-in data fetching mechanisms than fetching data in Effects.**
+**Những vấn đề này áp dụng cho bất kỳ thư viện UI nào, không chỉ React. Giải quyết chúng không đơn giản, đó là lý do tại sao các [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) hiện đại cung cấp các cơ chế fetch dữ liệu tích hợp hiệu quả hơn so với việc fetch dữ liệu trong Effect.**
-If you don't use a framework (and don't want to build your own) but would like to make data fetching from Effects more ergonomic, consider extracting your fetching logic into a custom Hook like in this example:
+Nếu bạn không sử dụng framework (và không muốn xây dựng của riêng bạn) nhưng muốn làm cho việc fetch dữ liệu từ Effect trở nên ergonomic hơn, hãy xem xét việc trích xuất logic fetch của bạn vào một custom Hook như trong ví dụ này:
```js {4}
function SearchResults({ query }) {
@@ -786,30 +786,30 @@ function useData(url) {
}
```
-You'll likely also want to add some logic for error handling and to track whether the content is loading. You can build a Hook like this yourself or use one of the many solutions already available in the React ecosystem. **Although this alone won't be as efficient as using a framework's built-in data fetching mechanism, moving the data fetching logic into a custom Hook will make it easier to adopt an efficient data fetching strategy later.**
+Bạn có thể cũng sẽ muốn thêm một số logic cho việc xử lý lỗi và theo dõi xem nội dung có đang loading hay không. Bạn có thể tự xây dựng một Hook như thế này hoặc sử dụng một trong nhiều giải pháp đã có sẵn trong React ecosystem. **Mặc dù điều này một mình sẽ không hiệu quả bằng việc sử dụng cơ chế fetch dữ liệu tích hợp của framework, việc chuyển logic fetch dữ liệu vào một custom Hook sẽ giúp việc áp dụng chiến lược fetch dữ liệu hiệu quả sau này dễ dàng hơn.**
-In general, whenever you have to resort to writing Effects, keep an eye out for when you can extract a piece of functionality into a custom Hook with a more declarative and purpose-built API like `useData` above. The fewer raw `useEffect` calls you have in your components, the easier you will find to maintain your application.
+Nói chung, bất cứ khi nào bạn phải viết Effect, hãy chú ý đến khi bạn có thể trích xuất một phần chức năng vào một custom Hook với API khai báo và có mục đích hơn như `useData` ở trên. Càng ít lời gọi `useEffect` thô bạn có trong các component của bạn, bạn sẽ càng dễ dàng duy trì ứng dụng của mình.
-- If you can calculate something during render, you don't need an Effect.
-- To cache expensive calculations, add `useMemo` instead of `useEffect`.
-- To reset the state of an entire component tree, pass a different `key` to it.
-- To reset a particular bit of state in response to a prop change, set it during rendering.
-- Code that runs because a component was *displayed* should be in Effects, the rest should be in events.
-- If you need to update the state of several components, it's better to do it during a single event.
-- Whenever you try to synchronize state variables in different components, consider lifting state up.
-- You can fetch data with Effects, but you need to implement cleanup to avoid race conditions.
+- Nếu bạn có thể tính toán điều gì đó trong quá trình render, bạn không cần Effect.
+- Để cache các phép tính đắt đỏ, hãy thêm `useMemo` thay vì `useEffect`.
+- Để reset state của toàn bộ cây component, hãy truyền một `key` khác cho nó.
+- Để reset một bit cụ thể của state để đáp ứng thay đổi prop, hãy đặt nó trong quá trình render.
+- Code chạy vì một component đã được *hiển thị* nên ở trong Effect, phần còn lại nên ở trong event.
+- Nếu bạn cần cập nhật state của nhiều component, tốt hơn là làm điều đó trong một sự kiện duy nhất.
+- Bất cứ khi nào bạn cố gắng đồng bộ hóa các biến state trong các component khác nhau, hãy xem xét việc nâng state lên.
+- Bạn có thể fetch dữ liệu với Effect, nhưng bạn cần triển khai cleanup để tránh race condition.
-#### Transform data without Effects {/*transform-data-without-effects*/}
+#### Biến đổi dữ liệu mà không cần Effect {/*transform-data-without-effects*/}
-The `TodoList` below displays a list of todos. When the "Show only active todos" checkbox is ticked, completed todos are not displayed in the list. Regardless of which todos are visible, the footer displays the count of todos that are not yet completed.
+`TodoList` bên dưới hiển thị một danh sách các todo. Khi checkbox "Show only active todos" được tích, các todo đã hoàn thành không được hiển thị trong danh sách. Bất kể todo nào hiển thị, footer hiển thị số lượng todo chưa hoàn thành.
-Simplify this component by removing all the unnecessary state and Effects.
+Đơn giản hóa component này bằng cách loại bỏ tất cả state và Effect không cần thiết.
@@ -909,15 +909,15 @@ input { margin-top: 10px; }
-If you can calculate something during rendering, you don't need state or an Effect that updates it.
+Nếu bạn có thể tính toán điều gì đó trong quá trình render, bạn không cần state hoặc Effect để cập nhật nó.
-There are only two essential pieces of state in this example: the list of `todos` and the `showActive` state variable which represents whether the checkbox is ticked. All of the other state variables are [redundant](/learn/choosing-the-state-structure#avoid-redundant-state) and can be calculated during rendering instead. This includes the `footer` which you can move directly into the surrounding JSX.
+Chỉ có hai phần state cần thiết trong ví dụ này: danh sách `todos` và biến state `showActive` đại diện cho việc checkbox có được tích hay không. Tất cả các biến state khác đều [dư thừa](/learn/choosing-the-state-structure#avoid-redundant-state) và có thể được tính toán trong quá trình render thay thế. Điều này bao gồm `footer` mà bạn có thể chuyển trực tiếp vào JSX xung quanh.
-Your result should end up looking like this:
+Kết quả của bạn nên trông như thế này:
@@ -1002,15 +1002,15 @@ input { margin-top: 10px; }
-#### Cache a calculation without Effects {/*cache-a-calculation-without-effects*/}
+#### Cache một phép tính mà không cần Effect {/*cache-a-calculation-without-effects*/}
-In this example, filtering the todos was extracted into a separate function called `getVisibleTodos()`. This function contains a `console.log()` call inside of it which helps you notice when it's being called. Toggle "Show only active todos" and notice that it causes `getVisibleTodos()` to re-run. This is expected because visible todos change when you toggle which ones to display.
+Trong ví dụ này, việc lọc todo đã được trích xuất vào một function riêng biệt được gọi là `getVisibleTodos()`. Function này chứa một lời gọi `console.log()` bên trong giúp bạn nhận ra khi nào nó được gọi. Toggle "Show only active todos" và nhận thấy rằng nó khiến `getVisibleTodos()` chạy lại. Điều này như mong đợi vì các todo hiển thị thay đổi khi bạn toggle những cái nào để hiển thị.
-Your task is to remove the Effect that recomputes the `visibleTodos` list in the `TodoList` component. However, you need to make sure that `getVisibleTodos()` does *not* re-run (and so does not print any logs) when you type into the input.
+Nhiệm vụ của bạn là loại bỏ Effect tính toán lại danh sách `visibleTodos` trong component `TodoList`. Tuy nhiên, bạn cần đảm bảo rằng `getVisibleTodos()` *không* chạy lại (và do đó không in bất kỳ log nào) khi bạn gõ vào input.
-One solution is to add a `useMemo` call to cache the visible todos. There is also another, less obvious solution.
+Một giải pháp là thêm một lời gọi `useMemo` để cache các todo hiển thị. Cũng có một giải pháp khác ít rõ ràng hơn.
@@ -1096,7 +1096,7 @@ input { margin-top: 10px; }
-Remove the state variable and the Effect, and instead add a `useMemo` call to cache the result of calling `getVisibleTodos()`:
+Loại bỏ biến state và Effect, và thay vào đó thêm một lời gọi `useMemo` để cache kết quả của việc gọi `getVisibleTodos()`:
@@ -1177,9 +1177,9 @@ input { margin-top: 10px; }
-With this change, `getVisibleTodos()` will be called only if `todos` or `showActive` change. Typing into the input only changes the `text` state variable, so it does not trigger a call to `getVisibleTodos()`.
+Với thay đổi này, `getVisibleTodos()` sẽ chỉ được gọi nếu `todos` hoặc `showActive` thay đổi. Gõ vào input chỉ thay đổi biến state `text`, vì vậy nó không kích hoạt lời gọi đến `getVisibleTodos()`.
-There is also another solution which does not need `useMemo`. Since the `text` state variable can't possibly affect the list of todos, you can extract the `NewTodo` form into a separate component, and move the `text` state variable inside of it:
+Cũng có một giải pháp khác không cần `useMemo`. Vì biến state `text` không thể ảnh hưởng đến danh sách todo, bạn có thể trích xuất form `NewTodo` thành một component riêng biệt, và chuyển biến state `text` vào trong đó:
@@ -1266,15 +1266,15 @@ input { margin-top: 10px; }
-This approach satisfies the requirements too. When you type into the input, only the `text` state variable updates. Since the `text` state variable is in the child `NewTodo` component, the parent `TodoList` component won't get re-rendered. This is why `getVisibleTodos()` doesn't get called when you type. (It would still be called if the `TodoList` re-renders for another reason.)
+Cách tiếp cận này cũng thỏa mãn yêu cầu. Khi bạn gõ vào input, chỉ biến state `text` cập nhật. Vì biến state `text` ở trong component con `NewTodo`, component cha `TodoList` sẽ không được re-render. Đây là lý do tại sao `getVisibleTodos()` không được gọi khi bạn gõ. (Nó vẫn sẽ được gọi nếu `TodoList` re-render vì lý do khác.)
-#### Reset state without Effects {/*reset-state-without-effects*/}
+#### Reset state mà không cần Effect {/*reset-state-without-effects*/}
-This `EditContact` component receives a contact object shaped like `{ id, name, email }` as the `savedContact` prop. Try editing the name and email input fields. When you press Save, the contact's button above the form updates to the edited name. When you press Reset, any pending changes in the form are discarded. Play around with this UI to get a feel for it.
+Component `EditContact` này nhận một đối tượng contact có dạng `{ id, name, email }` như prop `savedContact`. Thử chỉnh sửa các trường input name và email. Khi bạn nhấn Save, nút contact phía trên form sẽ cập nhật thành tên đã chỉnh sửa. Khi bạn nhấn Reset, bất kỳ thay đổi nào đang chờ trong form sẽ bị hủy bỏ. Chơi với UI này để cảm nhận nó.
-When you select a contact with the buttons at the top, the form resets to reflect that contact's details. This is done with an Effect inside `EditContact.js`. Remove this Effect. Find another way to reset the form when `savedContact.id` changes.
+Khi bạn chọn một contact với các nút ở phía trên, form sẽ reset để phản ánh chi tiết của contact đó. Điều này được thực hiện với một Effect bên trong `EditContact.js`. Loại bỏ Effect này. Tìm cách khác để reset form khi `savedContact.id` thay đổi.
@@ -1432,13 +1432,13 @@ button {
-It would be nice if there was a way to tell React that when `savedContact.id` is different, the `EditContact` form is conceptually a _different contact's form_ and should not preserve state. Do you recall any such way?
+Sẽ tuyệt vời nếu có cách để nói với React rằng khi `savedContact.id` khác, form `EditContact` về mặt khái niệm là *form của contact khác* và không nên bảo toàn state. Bạn có nhớ cách nào như vậy không?
-Split the `EditContact` component in two. Move all the form state into the inner `EditForm` component. Export the outer `EditContact` component, and make it pass `savedContact.id` as the `key` to the inner `EditForm` component. As a result, the inner `EditForm` component resets all of the form state and recreates the DOM whenever you select a different contact.
+Chia component `EditContact` thành hai. Chuyển tất cả form state vào component `EditForm` bên trong. Export component `EditContact` bên ngoài, và làm cho nó truyền `savedContact.id` làm `key` cho component `EditForm` bên trong. Kết quả là, component `EditForm` bên trong reset tất cả form state và tạo lại DOM bất cứ khi nào bạn chọn một contact khác.
@@ -1600,17 +1600,17 @@ button {
-#### Submit a form without Effects {/*submit-a-form-without-effects*/}
+#### Submit form mà không cần Effect {/*submit-a-form-without-effects*/}
-This `Form` component lets you send a message to a friend. When you submit the form, the `showForm` state variable is set to `false`. This triggers an Effect calling `sendMessage(message)`, which sends the message (you can see it in the console). After the message is sent, you see a "Thank you" dialog with an "Open chat" button that lets you get back to the form.
+Component `Form` này cho phép bạn gửi tin nhắn cho bạn bè. Khi bạn submit form, biến state `showForm` được đặt thành `false`. Điều này kích hoạt một Effect gọi `sendMessage(message)`, gửi tin nhắn (bạn có thể thấy nó trong console). Sau khi tin nhắn được gửi, bạn thấy dialog "Thank you" với nút "Open chat" cho phép bạn quay lại form.
-Your app's users are sending way too many messages. To make chatting a little bit more difficult, you've decided to show the "Thank you" dialog *first* rather than the form. Change the `showForm` state variable to initialize to `false` instead of `true`. As soon as you make that change, the console will show that an empty message was sent. Something in this logic is wrong!
+Người dùng ứng dụng của bạn đang gửi quá nhiều tin nhắn. Để làm cho việc chat khó khăn hơn một chút, bạn quyết định hiển thị dialog "Thank you" *trước* thay vì form. Thay đổi biến state `showForm` để khởi tạo thành `false` thay vì `true`. Ngay khi bạn thực hiện thay đổi đó, console sẽ hiển thị rằng một tin nhắn trống đã được gửi. Có điều gì đó trong logic này không đúng!
-What's the root cause of this problem? And how can you fix it?
+Nguyên nhân gốc rễ của vấn đề này là gì? Và bạn có thể sửa nó như thế nào?
-Should the message be sent _because_ the user saw the "Thank you" dialog? Or is it the other way around?
+Tin nhắn có nên được gửi *vì* người dùng đã thấy dialog "Thank you" không? Hay là ngược lại?
@@ -1675,7 +1675,7 @@ label, textarea { margin-bottom: 10px; display: block; }
-The `showForm` state variable determines whether to show the form or the "Thank you" dialog. However, you aren't sending the message because the "Thank you" dialog was _displayed_. You want to send the message because the user has _submitted the form._ Delete the misleading Effect and move the `sendMessage` call inside the `handleSubmit` event handler:
+Biến state `showForm` xác định có hiển thị form hay dialog "Thank you". Tuy nhiên, bạn không gửi tin nhắn vì dialog "Thank you" đã được *hiển thị*. Bạn muốn gửi tin nhắn vì người dùng đã *submit form.* Xóa Effect gây nhầm lẫn và chuyển lời gọi `sendMessage` vào trong event handler `handleSubmit`:
@@ -1731,7 +1731,7 @@ label, textarea { margin-bottom: 10px; display: block; }
-Notice how in this version, only _submitting the form_ (which is an event) causes the message to be sent. It works equally well regardless of whether `showForm` is initially set to `true` or `false`. (Set it to `false` and notice no extra console messages.)
+Lưu ý rằng trong phiên bản này, chỉ *submit form* (đó là một sự kiện) mới khiến tin nhắn được gửi. Nó hoạt động tốt bất kể `showForm` ban đầu được đặt thành `true` hay `false`. (Đặt nó thành `false` và chú ý không có tin nhắn console bổ sung.)