diff --git a/next-env.d.ts b/next-env.d.ts index 3cd7048ed..52e831b43 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,5 @@ /// <reference types="next" /> /// <reference types="next/image-types/global" /> -/// <reference types="next/navigation-types/compat/navigation" /> // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx index 9cdf256fb..9b53af7ea 100644 --- a/src/components/Layout/Footer.tsx +++ b/src/components/Layout/Footer.tsx @@ -284,7 +284,7 @@ export function Footer() { <div className="text-xs text-left rtl:text-right mt-2 pe-0.5" dir="ltr"> - Copyright © Meta Platforms, Inc + Bản quyền © Meta Platforms, Inc </div> <div className="uwu-visible text-xs cursor-pointer hover:text-link hover:dark:text-link-dark hover:underline" @@ -292,7 +292,7 @@ export function Footer() { // @ts-ignore window.__setUwu(false); }}> - no uwu plz + tắt uwu </div> <div className="uwu-hidden text-xs cursor-pointer hover:text-link hover:dark:text-link-dark hover:underline" @@ -303,7 +303,7 @@ export function Footer() { uwu? </div> <div className="uwu-visible text-xs"> - Logo by + Logo bởi <ExternalLink className="ms-1" href="https://twitter.com/sawaratsuki1004"> @@ -313,50 +313,48 @@ export function Footer() { </div> <div className="flex flex-col"> <FooterLink href="/learn" isHeader={true}> - Learn React - </FooterLink> - <FooterLink href="/learn/">Quick Start</FooterLink> - <FooterLink href="/learn/installation">Installation</FooterLink> - <FooterLink href="/learn/describing-the-ui"> - Describing the UI + Học React </FooterLink> + <FooterLink href="/learn/">Bắt Đầu Nhanh</FooterLink> + <FooterLink href="/learn/installation">Cài Đặt</FooterLink> + <FooterLink href="/learn/describing-the-ui">Mô Tả UI</FooterLink> <FooterLink href="/learn/adding-interactivity"> - Adding Interactivity + Thêm Tính Tương Tác + </FooterLink> + <FooterLink href="/learn/managing-state"> + Quản Lý Trạng Thái </FooterLink> - <FooterLink href="/learn/managing-state">Managing State</FooterLink> - <FooterLink href="/learn/escape-hatches">Escape Hatches</FooterLink> + <FooterLink href="/learn/escape-hatches">Các Lối Thoát</FooterLink> </div> <div className="flex flex-col"> <FooterLink href="/reference/react" isHeader={true}> - API Reference + Tham Khảo API </FooterLink> <FooterLink href="/reference/react">React APIs</FooterLink> <FooterLink href="/reference/react-dom">React DOM APIs</FooterLink> </div> <div className="md:col-start-2 xl:col-start-4 flex flex-col"> <FooterLink href="/community" isHeader={true}> - Community + Cộng Đồng </FooterLink> <FooterLink href="https://github.com/facebook/react/blob/main/CODE_OF_CONDUCT.md"> - Code of Conduct + Quy Tắc Ứng Xử </FooterLink> - <FooterLink href="/community/team">Meet the Team</FooterLink> + <FooterLink href="/community/team">Gặp Gỡ Đội Ngũ</FooterLink> <FooterLink href="/community/docs-contributors"> - Docs Contributors - </FooterLink> - <FooterLink href="/community/acknowledgements"> - Acknowledgements + Người Đóng Góp Tài Liệu </FooterLink> + <FooterLink href="/community/acknowledgements">Lời Cảm Ơn</FooterLink> </div> <div className="flex flex-col"> - <FooterLink isHeader={true}>More</FooterLink> + <FooterLink isHeader={true}>Thêm</FooterLink> <FooterLink href="/blog">Blog</FooterLink> <FooterLink href="https://reactnative.dev/">React Native</FooterLink> <FooterLink href="https://opensource.facebook.com/legal/privacy"> - Privacy + Quyền Riêng Tư </FooterLink> <FooterLink href="https://opensource.fb.com/legal/terms/"> - Terms + Điều Khoản </FooterLink> <div className="flex flex-row items-center mt-8 gap-x-2"> <ExternalLink diff --git a/src/components/Layout/HomeContent.js b/src/components/Layout/HomeContent.js index 72ab36884..440141014 100644 --- a/src/components/Layout/HomeContent.js +++ b/src/components/Layout/HomeContent.js @@ -136,7 +136,7 @@ export function HomeContent() { React </h1> <p className="text-4xl font-display max-w-lg md:max-w-full py-1 text-center text-secondary dark:text-primary-dark leading-snug self-center"> - The library for web and native user interfaces + Thư viện để xây dựng giao diện người dùng web và native </p> <div className="mt-5 self-center flex gap-2 w-full sm:w-auto flex-col sm:flex-row"> <ButtonLink @@ -144,29 +144,29 @@ export function HomeContent() { type="primary" size="lg" className="w-full sm:w-auto justify-center" - label="Learn React"> - Learn React + label="Học React"> + Học React </ButtonLink> <ButtonLink href={'/reference/react'} type="secondary" size="lg" className="w-full sm:w-auto justify-center" - label="API Reference"> - API Reference + label="Tham khảo API"> + Tham khảo API </ButtonLink> </div> </div> <Section background="left-card"> <Center> - <Header>Create user interfaces from components</Header> + <Header>Tạo giao diện người dùng từ các component</Header> <Para> - React lets you build user interfaces out of individual pieces - called components. Create your own React components like{' '} - <Code>Thumbnail</Code>, <Code>LikeButton</Code>, and{' '} - <Code>Video</Code>. Then combine them into entire screens, pages, - and apps. + React cho phép bạn xây dựng giao diện người dùng từ các phần riêng + lẻ được gọi là component. Tạo các React component của riêng bạn + như <Code>Thumbnail</Code>, <Code>LikeButton</Code>, và{' '} + <Code>Video</Code>. Sau đó, kết hợp chúng thành toàn bộ màn hình, + trang và ứng dụng. </Para> </Center> <FullBleed> @@ -174,22 +174,22 @@ export function HomeContent() { </FullBleed> <Center> <Para> - Whether you work on your own or with thousands of other - developers, using React feels the same. It is designed to let you - seamlessly combine components written by independent people, - teams, and organizations. + Cho dù bạn làm việc một mình hay với hàng ngàn nhà phát triển + khác, việc sử dụng React đều mang lại cảm giác như nhau. Nó được + thiết kế để cho phép bạn kết hợp liền mạch các component được viết + bởi những người, nhóm và tổ chức độc lập. </Para> </Center> </Section> <Section background="right-card"> <Center> - <Header>Write components with code and markup</Header> + <Header>Viết component bằng code và markup</Header> <Para> - React components are JavaScript functions. Want to show some - content conditionally? Use an <Code>if</Code> statement. - Displaying a list? Try array <Code>map()</Code>. Learning React is - learning programming. + React component là các hàm JavaScript. Bạn muốn hiển thị một số + nội dung có điều kiện? Sử dụng câu lệnh <Code>if</Code>. Hiển thị + một danh sách? Hãy thử <Code>map()</Code> của mảng. Học React là + học lập trình. </Para> </Center> <FullBleed> @@ -197,22 +197,21 @@ export function HomeContent() { </FullBleed> <Center> <Para> - This markup syntax is called JSX. It is a JavaScript syntax - extension popularized by React. Putting JSX markup close to - related rendering logic makes React components easy to create, - maintain, and delete. + Cú pháp đánh dấu này được gọi là JSX. Đây là phần mở rộng cú pháp + JavaScript được React phổ biến. Đặt đánh dấu JSX gần với logic kết + xuất liên quan giúp các thành phần React dễ tạo, bảo trì và xóa. </Para> </Center> </Section> <Section background="left-card"> <Center> - <Header>Add interactivity wherever you need it</Header> + <Header>Thêm tính tương tác ở bất cứ đâu bạn cần</Header> <Para> - React components receive data and return what should appear on the - screen. You can pass them new data in response to an interaction, - like when the user types into an input. React will then update the - screen to match the new data. + React component nhận dữ liệu và trả về những gì sẽ xuất hiện trên + màn hình. Bạn có thể truyền cho chúng dữ liệu mới để đáp ứng một + tương tác, chẳng hạn như khi người dùng nhập vào một input. Sau + đó, React sẽ cập nhật màn hình để phù hợp với dữ liệu mới. </Para> </Center> <FullBleed> @@ -220,16 +219,16 @@ export function HomeContent() { </FullBleed> <Center> <Para> - You don’t have to build your whole page in React. Add React to - your existing HTML page, and render interactive React components - anywhere on it. + Bạn không cần phải xây dựng toàn bộ trang của mình bằng React. + Thêm React vào trang HTML hiện có của bạn và hiển thị các React + component tương tác ở bất kỳ đâu trên đó. </Para> <div className="flex justify-start w-full lg:justify-center"> <CTA color="gray" icon="code" href="/learn/add-react-to-an-existing-project"> - Add React to your page + Thêm React vào trang của bạn </CTA> </div> </Center> @@ -238,14 +237,15 @@ export function HomeContent() { <Section background="right-card"> <Center> <Header> - Go full-stack <br className="hidden lg:inline" /> - with a framework + Đi full-stack <br className="hidden lg:inline" /> + với một framework </Header> <Para> - React is a library. It lets you put components together, but it - doesn’t prescribe how to do routing and data fetching. To build an - entire app with React, we recommend a full-stack React framework - like <Link href="https://nextjs.org">Next.js</Link> or{' '} + React là một thư viện. Nó cho phép bạn ghép các component lại với + nhau, nhưng nó không quy định cách thực hiện routing và tìm nạp dữ + liệu. Để xây dựng toàn bộ ứng dụng bằng React, chúng tôi khuyên + bạn nên sử dụng một React framework full-stack như{' '} + <Link href="https://nextjs.org">Next.js</Link> hoặc{' '} <Link href="https://remix.run">Remix</Link>. </Para> </Center> @@ -254,17 +254,18 @@ export function HomeContent() { </FullBleed> <Center> <Para> - React is also an architecture. Frameworks that implement it let - you fetch data in asynchronous components that run on the server - or even during the build. Read data from a file or a database, and - pass it down to your interactive components. + React cũng là một kiến trúc. Các framework triển khai nó cho phép + bạn tìm nạp dữ liệu trong các component không đồng bộ chạy trên + server hoặc thậm chí trong quá trình build. Đọc dữ liệu từ một + file hoặc một database và chuyển nó xuống các component tương tác + của bạn. </Para> <div className="flex justify-start w-full lg:justify-center"> <CTA color="gray" icon="framework" href="/learn/start-a-new-react-project"> - Get started with a framework + Bắt đầu với một framework </CTA> </div> </Center> @@ -272,12 +273,13 @@ export function HomeContent() { <Section background="left-card"> <div className="mx-auto flex flex-col w-full"> <div className="mx-auto max-w-4xl lg:text-center items-center px-5 flex flex-col"> - <Header>Use the best from every platform</Header> + <Header>Sử dụng những gì tốt nhất từ mọi nền tảng</Header> <Para> - People love web and native apps for different reasons. React - lets you build both web apps and native apps using the same - skills. It leans upon each platform’s unique strengths to let - your interfaces feel just right on every platform. + Mọi người yêu thích các ứng dụng web và native vì những lý do + khác nhau. React cho phép bạn xây dựng cả ứng dụng web và ứng + dụng native bằng cách sử dụng cùng một kỹ năng. Nó dựa vào những + điểm mạnh riêng của mỗi nền tảng để cho phép giao diện của bạn + có cảm giác phù hợp trên mọi nền tảng. </Para> </div> <div className="max-w-7xl mx-auto flex flex-col lg:flex-row mt-16 mb-20 lg:mb-28 px-5 gap-20 lg:gap-5"> @@ -291,15 +293,16 @@ export function HomeContent() { <div className="bg-wash relative h-14 w-full" /> <div className="relative flex items-start justify-center flex-col flex-1 pb-16 pt-5 gap-3 px-5 lg:px-10 lg:pt-8"> <h4 className="leading-tight text-primary font-semibold text-3xl lg:text-4xl"> - Stay true to the web + Luôn đúng với web </h4> <p className="lg:text-xl leading-normal text-secondary"> - People expect web app pages to load fast. On the server, - React lets you start streaming HTML while you’re still - fetching data, progressively filling in the remaining - content before any JavaScript code loads. On the client, - React can use standard web APIs to keep your UI - responsive even in the middle of rendering. + Mọi người mong đợi các trang ứng dụng web tải nhanh. + Trên server, React cho phép bạn bắt đầu streaming HTML + trong khi bạn vẫn đang tìm nạp dữ liệu, lần lượt điền + vào nội dung còn lại trước khi bất kỳ code JavaScript + nào được tải. Trên client, React có thể sử dụng các API + web tiêu chuẩn để giữ cho UI của bạn phản hồi nhanh ngay + cả khi đang hiển thị. </p> </div> </div> @@ -356,42 +359,25 @@ export function HomeContent() { fill="currentColor" /> </svg> - <svg - width="20" - height="20" - viewBox="0 0 72 72" - fill="none" - xmlns="http://www.w3.org/2000/svg"> - <path - d="M12.9533 26.0038C13.224 24.7829 14.3285 24 15.579 24H50.421C51.6715 24 52.776 24.7829 53.0467 26.0038C53.4754 27.937 54 31.2691 54 36C54 40.7309 53.4754 44.063 53.0467 45.9962C52.776 47.2171 51.6715 48 50.421 48H15.579C14.3285 48 13.224 47.2171 12.9533 45.9962C12.5246 44.063 12 40.7309 12 36C12 31.2691 12.5246 27.937 12.9533 26.0038Z" - fill="currentColor" - /> - <path - fillRule="evenodd" - clipRule="evenodd" - d="M12.7887 15C8.77039 15 5.23956 17.668 4.48986 21.6158C3.74326 25.5473 3 30.7737 3 36C3 41.2263 3.74326 46.4527 4.48986 50.3842C5.23956 54.332 8.77039 57 12.7887 57H53.2113C57.2296 57 60.7604 54.332 61.5101 50.3842C61.8155 48.7765 62.1202 46.9522 62.3738 45H63.7918C64.5731 45 65.3283 44.8443 66 44.5491C67.2821 43.9857 68.2596 42.9142 68.5322 41.448C68.7927 40.0466 69 38.2306 69 36C69 33.7694 68.7927 31.9534 68.5322 30.552C68.2596 29.0858 67.2821 28.0143 66 27.4509C65.3283 27.1557 64.5731 27 63.7918 27H62.3738C62.1202 25.0478 61.8155 23.2235 61.5101 21.6158C60.7604 17.668 57.2296 15 53.2113 15H12.7887ZM53.2113 21H12.7887C11.3764 21 10.5466 21.8816 10.3845 22.7352C9.67563 26.4681 9 31.29 9 36C9 40.71 9.67563 45.5319 10.3845 49.2648C10.5466 50.1184 11.3764 51 12.7887 51H53.2113C54.6236 51 55.4534 50.1184 55.6155 49.2648C56.3244 45.5319 57 40.71 57 36C57 31.29 56.3244 26.4681 55.6155 22.7352C55.4534 21.8816 54.6236 21 53.2113 21Z" - fill="currentColor" - /> - </svg> </div> </div> <div className="flex flex-col items-start justify-center pt-0 gap-3 px-2.5 lg:pt-8 lg:px-8"> <h4 className="leading-tight text-primary dark:text-primary-dark font-semibold text-3xl lg:text-4xl"> - Go truly native + Đi thật sự native </h4> <p className="h-full lg:text-xl text-secondary dark:text-secondary-dark leading-normal"> - People expect native apps to look and feel like their - platform.{' '} + Mọi người mong đợi các ứng dụng native trông và cảm + thấy giống như nền tảng của họ. <Link href="https://reactnative.dev"> React Native </Link>{' '} - and{' '} + và{' '} <Link href="https://github.com/expo/expo">Expo</Link>{' '} - let you build apps in React for Android, iOS, and - more. They look and feel native because their UIs{' '} - <i>are</i> truly native. It’s not a web view—your - React components render real Android and iOS views - provided by the platform. + cho phép bạn xây dựng các ứng dụng bằng React cho + Android, iOS, v.v. Chúng trông và cảm thấy native vì + UI của chúng <i>là</i> thực sự native. Đó không phải + là một web view—các React component của bạn hiển thị + các view Android và iOS thực do nền tảng cung cấp. </p> </div> </div> @@ -401,14 +387,15 @@ export function HomeContent() { </div> <div className="px-5 lg:px-0 max-w-4xl mx-auto lg:text-center text-secondary dark:text-secondary-dark"> <Para> - With React, you can be a web <i>and</i> a native developer. Your - team can ship to many platforms without sacrificing the user - experience. Your organization can bridge the platform silos, and - form teams that own entire features end-to-end. + Với React, bạn có thể trở thành một nhà phát triển web <i>và</i>{' '} + native. Nhóm của bạn có thể ship đến nhiều nền tảng mà không làm + giảm trải nghiệm người dùng. Tổ chức của bạn có thể thu hẹp các + silo nền tảng và thành lập các nhóm sở hữu toàn bộ các tính năng + đầu cuối. </Para> <div className="flex justify-start w-full lg:justify-center"> <CTA color="gray" icon="native" href="https://reactnative.dev/"> - Build for native platforms + Xây dựng cho các nền tảng native </CTA> </div> </div> @@ -419,23 +406,24 @@ export function HomeContent() { <div className="max-w-7xl mx-auto flex flex-col lg:flex-row px-5"> <div className="max-w-3xl lg:max-w-7xl gap-5 flex flex-col lg:flex-row lg:px-5"> <div className="w-full lg:w-6/12 max-w-3xl flex flex-col items-start justify-start lg:ps-5 lg:pe-10"> - <Header>Upgrade when the future is ready</Header> + <Header>Nâng cấp khi tương lai đã sẵn sàng</Header> <Para> - React approaches changes with care. Every React commit is - tested on business-critical surfaces with over a billion - users. Over 100,000 React components at Meta help validate - every migration strategy. + React tiếp cận những thay đổi một cách cẩn thận. Mọi commit + React đều được kiểm tra trên các bề mặt quan trọng đối với + doanh nghiệp với hơn một tỷ người dùng. Hơn 100.000 React + component tại Meta giúp xác thực mọi chiến lược di chuyển. </Para> <div className="order-last pt-5"> <Para> - The React team is always researching how to improve React. - Some research takes years to pay off. React has a high bar - for taking a research idea into production. Only proven - approaches become a part of React. + Nhóm React luôn nghiên cứu cách cải thiện React. Một số + nghiên cứu mất nhiều năm để đơm hoa kết trái. React có một + tiêu chuẩn cao để đưa một ý tưởng nghiên cứu vào sản xuất. + Chỉ những cách tiếp cận đã được chứng minh mới trở thành một + phần của React. </Para> <div className="hidden lg:flex justify-start w-full"> <CTA color="gray" icon="news" href="/blog"> - Read more React news + Đọc thêm tin tức React </CTA> </div> </div> @@ -443,7 +431,7 @@ export function HomeContent() { <div className="w-full lg:w-6/12"> <p className="uppercase tracking-wide font-bold text-sm text-tertiary dark:text-tertiary-dark flex flex-row gap-2 items-center mt-5 lg:-mt-2 w-full"> <IconChevron /> - Latest React News + Tin tức React mới nhất </p> <div className="flex-col sm:flex-row flex-wrap flex gap-5 text-start my-5"> <div className="flex-1 min-w-[40%] text-start"> @@ -461,7 +449,7 @@ export function HomeContent() { </div> <div className="flex lg:hidden justify-start w-full"> <CTA color="gray" icon="news" href="/blog"> - Read more React news + Đọc thêm tin tức React </CTA> </div> </div> @@ -474,13 +462,13 @@ export function HomeContent() { <div className="mx-auto flex flex-col max-w-4xl"> <Center> <Header> - Join a community <br className="hidden lg:inline" /> - of millions + Tham gia một cộng đồng <br className="hidden lg:inline" /> + của hàng triệu người </Header> <Para> - You’re not alone. Two million developers from all over the - world visit the React docs every month. React is something - that people and teams can agree on. + Bạn không hề đơn độc. Hai triệu nhà phát triển từ khắp nơi + trên thế giới truy cập tài liệu React mỗi tháng. React là một + thứ mà mọi người và các nhóm có thể đồng ý. </Para> </Center> </div> @@ -488,13 +476,14 @@ export function HomeContent() { <div className="mx-auto flex flex-col max-w-4xl"> <Center> <Para> - This is why React is more than a library, an architecture, or - even an ecosystem. React is a community. It’s a place where - you can ask for help, find opportunities, and meet new - friends. You will meet both developers and designers, - beginners and experts, researchers and artists, teachers and - students. Our backgrounds may be very different, but React - lets us all create user interfaces together. + Đây là lý do tại sao React không chỉ là một thư viện, một kiến + trúc hay thậm chí là một hệ sinh thái. React là một cộng đồng. + Đó là một nơi mà bạn có thể yêu cầu giúp đỡ, tìm kiếm cơ hội + và gặp gỡ những người bạn mới. Bạn sẽ gặp cả nhà phát triển và + nhà thiết kế, người mới bắt đầu và chuyên gia, nhà nghiên cứu + và nghệ sĩ, giáo viên và học sinh. Nền tảng của chúng ta có + thể rất khác nhau, nhưng React cho phép tất cả chúng ta cùng + nhau tạo ra giao diện người dùng. </Para> </Center> </div> @@ -511,15 +500,15 @@ export function HomeContent() { </div> <Logo className="uwu-hidden text-brand dark:text-brand-dark w-24 lg:w-28 mb-10 lg:mb-8 mt-12 h-auto mx-auto self-start" /> <Header> - Welcome to the <br className="hidden lg:inline" /> - React community + Chào mừng đến với <br className="hidden lg:inline" /> + cộng đồng React </Header> <ButtonLink href={'/learn'} type="primary" size="lg" - label="Take the Tutorial"> - Get Started + label="Tham gia Hướng dẫn"> + Bắt đầu </ButtonLink> </div> </Section> diff --git a/src/components/Layout/TopNav/TopNav.tsx b/src/components/Layout/TopNav/TopNav.tsx index cc5c654e3..d60628383 100644 --- a/src/components/Layout/TopNav/TopNav.tsx +++ b/src/components/Layout/TopNav/TopNav.tsx @@ -308,7 +308,7 @@ export default function TopNav({ )} onClick={onOpenSearch}> <IconSearch className="align-middle me-3 text-gray-30 shrink-0 group-betterhover:hover:text-gray-70" /> - Search + Tìm kiếm <span className="hidden ms-auto sm:flex item-center me-1"> <Kbd data-platform="mac">⌘</Kbd> <Kbd data-platform="win" wide> @@ -321,15 +321,15 @@ export default function TopNav({ <div className="text-base justify-center items-center gap-1.5 flex 3xl:flex-1 flex-row 3xl:justify-end"> <div className="mx-2.5 gap-1.5 hidden lg:flex"> <NavItem isActive={section === 'learn'} url="/learn"> - Learn + Học </NavItem> <NavItem isActive={section === 'reference'} url="/reference/react"> - Reference + Tài liệu tham khảo </NavItem> <NavItem isActive={section === 'community'} url="/community"> - Community + Cộng đồng </NavItem> <NavItem isActive={section === 'blog'} url="/blog"> Blog @@ -339,7 +339,7 @@ export default function TopNav({ <div className="flex items-center -space-x-2.5 xs:space-x-0 "> <div className="flex md:hidden"> <button - aria-label="Search" + aria-label="Tìm kiếm" type="button" className="flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 md:hidden hover:bg-secondary-button hover:dark:bg-secondary-button-dark outline-link" onClick={onOpenSearch}> @@ -349,7 +349,7 @@ export default function TopNav({ <div className="flex dark:hidden"> <button type="button" - aria-label="Use Dark Mode" + aria-label="Sử dụng Chế độ Tối" onClick={() => { window.__setPreferredTheme('dark'); }} @@ -360,7 +360,7 @@ export default function TopNav({ <div className="hidden dark:flex"> <button type="button" - aria-label="Use Light Mode" + aria-label="Sử dụng Chế độ Sáng" onClick={() => { window.__setPreferredTheme('light'); }} @@ -371,7 +371,7 @@ export default function TopNav({ <div className="flex"> <Link href="/community/translations" - aria-label="Translations" + aria-label="Bản dịch" className="active:scale-95 transition-transform flex w-12 h-12 rounded-full items-center justify-center hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link"> {languageIcon} </Link> @@ -381,7 +381,7 @@ export default function TopNav({ href="https://github.com/facebook/react/releases" target="_blank" rel="noreferrer noopener" - aria-label="Open on GitHub" + aria-label="Mở trên GitHub" className="flex items-center justify-center w-12 h-12 transition-transform rounded-full active:scale-95 hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link"> {githubIcon} </Link> @@ -408,17 +408,17 @@ export default function TopNav({ <Suspense fallback={null}> <div className="ps-3 xs:ps-5 xs:gap-0.5 xs:text-base overflow-x-auto flex flex-row lg:hidden text-base font-bold text-secondary dark:text-secondary-dark"> <NavItem isActive={section === 'learn'} url="/learn"> - Learn + Học </NavItem> <NavItem isActive={section === 'reference'} url="/reference/react"> - Reference + Tài liệu tham khảo </NavItem> <NavItem isActive={section === 'community'} url="/community"> - Community + Cộng đồng </NavItem> <NavItem isActive={section === 'blog'} url="/blog"> Blog diff --git a/src/content/community/acknowledgements.md b/src/content/community/acknowledgements.md index bfe67f55a..8b054f9a6 100644 --- a/src/content/community/acknowledgements.md +++ b/src/content/community/acknowledgements.md @@ -1,16 +1,16 @@ --- -title: Acknowledgements +title: Lời Cảm Ơn --- <Intro> -React was originally created by [Jordan Walke.](https://github.com/jordwalke) Today, React has a [dedicated full-time team working on it](/community/team), as well as over a thousand [open source contributors.](https://github.com/facebook/react/graphs/contributors) +React ban đầu được tạo ra bởi [Jordan Walke.](https://github.com/jordwalke) Ngày nay, React có một [đội ngũ toàn thời gian tận tâm làm việc](/community/team), cũng như hơn một nghìn [người đóng góp mã nguồn mở.](https://github.com/facebook/react/graphs/contributors) </Intro> -## Past contributors {/*past-contributors*/} +## Những người đóng góp trước đây {/*past-contributors*/} -We'd like to recognize a few people who have made significant contributions to React and its documentation in the past and have helped maintain them over the years: +Chúng tôi xin ghi nhận một vài người đã có những đóng góp đáng kể cho React và tài liệu của nó trong quá khứ và đã giúp duy trì chúng trong những năm qua: * [Almero Steyn](https://github.com/AlmeroSteyn) * [Andreas Svensson](https://github.com/syranide) @@ -60,15 +60,15 @@ We'd like to recognize a few people who have made significant contributions to R * [Tim Yung](https://github.com/yungsters) * [Xuan Huang](https://github.com/huxpro) -This list is not exhaustive. +Danh sách này không đầy đủ. -We'd like to give special thanks to [Tom Occhino](https://github.com/tomocchino) and [Adam Wolff](https://github.com/wolffiex) for their guidance and support over the years. We are also thankful to all the volunteers who [translated React into other languages.](https://translations.react.dev/) +Chúng tôi xin đặc biệt cảm ơn [Tom Occhino](https://github.com/tomocchino) và [Adam Wolff](https://github.com/wolffiex) vì sự hướng dẫn và hỗ trợ của họ trong những năm qua. Chúng tôi cũng biết ơn tất cả các tình nguyện viên đã [dịch React sang các ngôn ngữ khác.](https://translations.react.dev/) -## Additional Thanks {/*additional-thanks*/} +## Lời Cảm Ơn Thêm {/*additional-thanks*/} -Additionally, we're grateful to: +Ngoài ra, chúng tôi rất biết ơn: -* [Jeff Barczewski](https://github.com/jeffbski) for allowing us to use the `react` package name on npm -* [Christopher Aue](https://christopheraue.net/) for letting us use the reactjs.com domain name and the [@reactjs](https://twitter.com/reactjs) username on Twitter -* [ProjectMoon](https://github.com/ProjectMoon) for letting us use the [flux](https://www.npmjs.com/package/flux) package name on npm -* Shane Anderson for allowing us to use the [react](https://github.com/react) org on GitHub +* [Jeff Barczewski](https://github.com/jeffbski) vì đã cho phép chúng tôi sử dụng tên gói `react` trên npm +* [Christopher Aue](https://christopheraue.net/) vì đã cho phép chúng tôi sử dụng tên miền reactjs.com và tên người dùng [@reactjs](https://twitter.com/reactjs) trên Twitter +* [ProjectMoon](https://github.com/ProjectMoon) vì đã cho phép chúng tôi sử dụng tên gói [flux](https://www.npmjs.com/package/flux) trên npm +* Shane Anderson vì đã cho phép chúng tôi sử dụng tổ chức [react](https://github.com/react) trên GitHub diff --git a/src/content/community/translations.md b/src/content/community/translations.md index 4c07e6a1e..541cde46d 100644 --- a/src/content/community/translations.md +++ b/src/content/community/translations.md @@ -1,35 +1,35 @@ --- -title: Translations +title: Bản dịch --- <Intro> -React docs are translated by the global community into many languages all over the world. +Tài liệu React được cộng đồng toàn cầu dịch sang nhiều ngôn ngữ trên khắp thế giới. </Intro> -## Source site {/*main-site*/} +## Trang gốc {/*main-site*/} -All translations are provided from the canonical source docs: +Tất cả các bản dịch được cung cấp từ tài liệu gốc chính thức: -- [English](https://react.dev/) — [Contribute](https://github.com/reactjs/react.dev/) +- [Tiếng Anh](https://react.dev/) — [Đóng góp](https://github.com/reactjs/react.dev/) -## Full translations {/*full-translations*/} +## Bản dịch đầy đủ {/*full-translations*/} -{/* If you are a language maintainer and want to add your language here, finish the "Core" translations and edit `deployedTranslations` under `src/utils`. */} +{/* Nếu bạn là người bảo trì ngôn ngữ và muốn thêm ngôn ngữ của mình vào đây, hãy hoàn thành bản dịch "Core" và chỉnh sửa `deployedTranslations` trong `src/utils`. */} <LanguageList progress="complete" /> -## In-progress translations {/*in-progress-translations*/} +## Bản dịch đang tiến hành {/*in-progress-translations*/} -For the progress of each translation, see: [Is React Translated Yet?](https://translations.react.dev/) +Để biết tiến độ của từng bản dịch, hãy xem: [React Đã Được Dịch Chưa?](https://translations.react.dev/) <LanguageList progress="in-progress" /> -## How to contribute {/*how-to-contribute*/} +## Cách đóng góp {/*how-to-contribute*/} -You can contribute to the translation efforts! +Bạn có thể đóng góp vào nỗ lực dịch thuật! -The community conducts the translation work for the React docs on each language-specific fork of react.dev. Typical translation work involves directly translating a Markdown file and creating a pull request. Click the "contribute" link above to the GitHub repository for your language, and follow the instructions there to help with the translation effort. +Cộng đồng thực hiện công việc dịch tài liệu React trên mỗi nhánh dành riêng cho ngôn ngữ của react.dev. Công việc dịch thuật điển hình bao gồm dịch trực tiếp tệp Markdown và tạo một pull request. Nhấp vào liên kết "đóng góp" ở trên để đến kho lưu trữ GitHub cho ngôn ngữ của bạn và làm theo hướng dẫn ở đó để giúp nỗ lực dịch thuật. -If you want to start a new translation for your language, visit: [translations.react.dev](https://github.com/reactjs/translations.react.dev) \ No newline at end of file +Nếu bạn muốn bắt đầu một bản dịch mới cho ngôn ngữ của mình, hãy truy cập: [translations.react.dev](https://github.com/reactjs/translations.react.dev) \ No newline at end of file diff --git a/src/content/errors/377.md b/src/content/errors/377.md index 6269e9d06..c27ade954 100644 --- a/src/content/errors/377.md +++ b/src/content/errors/377.md @@ -1,13 +1,13 @@ <Intro> -In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire. +Trong bản dựng production đã rút gọn của React, chúng tôi tránh gửi toàn bộ thông báo lỗi để giảm số lượng byte được gửi qua mạng. </Intro> -We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message. +Chúng tôi đặc biệt khuyên bạn nên sử dụng bản dựng development cục bộ khi gỡ lỗi ứng dụng của mình vì nó theo dõi thông tin gỡ lỗi bổ sung và cung cấp các cảnh báo hữu ích về các sự cố tiềm ẩn trong ứng dụng của bạn, nhưng nếu bạn gặp phải ngoại lệ khi sử dụng bản dựng production, trang này sẽ tập hợp lại thông báo lỗi ban đầu. -The full text of the error you just encountered is: +Toàn văn của lỗi bạn vừa gặp phải là: <ErrorDecoder /> -This error occurs when you pass a BigInt value from a Server Component to a Client Component. +Lỗi này xảy ra khi bạn truyền giá trị BigInt từ một Server Component sang một Client Component. diff --git a/src/content/errors/generic.md b/src/content/errors/generic.md index 27c3ca52d..e55eed93d 100644 --- a/src/content/errors/generic.md +++ b/src/content/errors/generic.md @@ -1,11 +1,11 @@ <Intro> -In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire. +Trong bản dựng production đã rút gọn của React, chúng tôi tránh gửi toàn bộ thông báo lỗi để giảm số lượng byte được gửi qua mạng. </Intro> -We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, this page will reassemble the original error message. +Chúng tôi đặc biệt khuyên bạn nên sử dụng bản dựng development cục bộ khi gỡ lỗi ứng dụng của mình vì nó theo dõi thông tin gỡ lỗi bổ sung và cung cấp các cảnh báo hữu ích về các sự cố tiềm ẩn trong ứng dụng của bạn, nhưng nếu bạn gặp phải ngoại lệ khi sử dụng bản dựng production, trang này sẽ tập hợp lại thông báo lỗi ban đầu. -The full text of the error you just encountered is: +Toàn văn của lỗi bạn vừa gặp phải là: <ErrorDecoder /> diff --git a/src/content/errors/index.md b/src/content/errors/index.md index 25746d25d..0499a1d49 100644 --- a/src/content/errors/index.md +++ b/src/content/errors/index.md @@ -1,10 +1,10 @@ <Intro> -In the minified production build of React, we avoid sending down full error messages in order to reduce the number of bytes sent over the wire. +Trong bản dựng production đã rút gọn của React, chúng tôi tránh gửi toàn bộ thông báo lỗi để giảm số lượng byte được gửi qua mạng. </Intro> -We highly recommend using the development build locally when debugging your app since it tracks additional debug info and provides helpful warnings about potential problems in your apps, but if you encounter an exception while using the production build, the error message will include just a link to the docs for the error. +Chúng tôi đặc biệt khuyên bạn nên sử dụng bản dựng development cục bộ khi gỡ lỗi ứng dụng của mình vì nó theo dõi thông tin gỡ lỗi bổ sung và cung cấp các cảnh báo hữu ích về các sự cố tiềm ẩn trong ứng dụng của bạn, nhưng nếu bạn gặp phải ngoại lệ khi sử dụng bản dựng production, thông báo lỗi sẽ chỉ bao gồm một liên kết đến tài liệu về lỗi đó. -For an example, see: [https://react.dev/errors/149](/errors/421). +Ví dụ: [https://react.dev/errors/149](/errors/421). diff --git a/src/content/learn/adding-interactivity.md b/src/content/learn/adding-interactivity.md index 5c87a3e79..19d63a5a0 100644 --- a/src/content/learn/adding-interactivity.md +++ b/src/content/learn/adding-interactivity.md @@ -1,30 +1,30 @@ --- -title: Adding Interactivity +title: Thêm tính tương tác --- <Intro> -Some things on the screen update in response to user input. For example, clicking an image gallery switches the active image. In React, data that changes over time is called *state.* You can add state to any component, and update it as needed. In this chapter, you'll learn how to write components that handle interactions, update their state, and display different output over time. +Một số thứ trên màn hình cập nhật để đáp ứng đầu vào của người dùng. Ví dụ: nhấp vào thư viện ảnh sẽ chuyển ảnh đang hoạt động. Trong React, dữ liệu thay đổi theo thời gian được gọi là *state (trạng thái).* Bạn có thể thêm trạng thái vào bất kỳ component nào và cập nhật nó khi cần. Trong chương này, bạn sẽ học cách viết các component xử lý tương tác, cập nhật trạng thái của chúng và hiển thị các đầu ra khác nhau theo thời gian. </Intro> <YouWillLearn isChapter={true}> -* [How to handle user-initiated events](/learn/responding-to-events) -* [How to make components "remember" information with state](/learn/state-a-components-memory) -* [How React updates the UI in two phases](/learn/render-and-commit) -* [Why state doesn't update right after you change it](/learn/state-as-a-snapshot) -* [How to queue multiple state updates](/learn/queueing-a-series-of-state-updates) -* [How to update an object in state](/learn/updating-objects-in-state) -* [How to update an array in state](/learn/updating-arrays-in-state) +* [Cách xử lý các sự kiện do người dùng khởi tạo](/learn/responding-to-events) +* [Cách làm cho các component "ghi nhớ" thông tin bằng state](/learn/state-a-components-memory) +* [Cách React cập nhật UI theo hai giai đoạn](/learn/render-and-commit) +* [Tại sao state không cập nhật ngay sau khi bạn thay đổi nó](/learn/state-as-a-snapshot) +* [Cách xếp hàng đợi nhiều bản cập nhật state](/learn/queueing-a-series-of-state-updates) +* [Cách cập nhật một đối tượng trong state](/learn/updating-objects-in-state) +* [Cách cập nhật một mảng trong state](/learn/updating-arrays-in-state) </YouWillLearn> -## Responding to events {/*responding-to-events*/} +## Phản hồi các sự kiện {/*responding-to-events*/} -React lets you add *event handlers* to your JSX. Event handlers are your own functions that will be triggered in response to user interactions like clicking, hovering, focusing on form inputs, and so on. +React cho phép bạn thêm *trình xử lý sự kiện* vào JSX của mình. Trình xử lý sự kiện là các hàm của riêng bạn sẽ được kích hoạt để đáp ứng các tương tác của người dùng như nhấp, di chuột, tập trung vào các đầu vào biểu mẫu, v.v. -Built-in components like `<button>` only support built-in browser events like `onClick`. However, you can also create your own components, and give their event handler props any application-specific names that you like. +Các component tích hợp sẵn như `<button>` chỉ hỗ trợ các sự kiện trình duyệt tích hợp sẵn như `onClick`. Tuy nhiên, bạn cũng có thể tạo các component của riêng mình và cung cấp cho các đạo cụ trình xử lý sự kiện của chúng bất kỳ tên dành riêng cho ứng dụng nào mà bạn thích. <Sandpack> @@ -32,8 +32,8 @@ Built-in components like `<button>` only support built-in browser events like `o export default function App() { return ( <Toolbar - onPlayMovie={() => alert('Playing!')} - onUploadImage={() => alert('Uploading!')} + onPlayMovie={() => alert('Đang phát!')} + onUploadImage={() => alert('Đang tải lên!')} /> ); } @@ -42,10 +42,10 @@ function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> - Play Movie + Phát phim </Button> <Button onClick={onUploadImage}> - Upload Image + Tải ảnh lên </Button> </div> ); @@ -68,22 +68,22 @@ button { margin-right: 10px; } <LearnMore path="/learn/responding-to-events"> -Read **[Responding to Events](/learn/responding-to-events)** to learn how to add event handlers. +Đọc **[Phản hồi các sự kiện](/learn/responding-to-events)** để tìm hiểu cách thêm trình xử lý sự kiện. </LearnMore> -## State: a component's memory {/*state-a-components-memory*/} +## State: bộ nhớ của một component {/*state-a-components-memory*/} -Components often need to change what's on the screen as a result of an interaction. Typing into the form should update the input field, clicking "next" on an image carousel should change which image is displayed, clicking "buy" puts a product in the shopping cart. Components need to "remember" things: the current input value, the current image, the shopping cart. In React, this kind of component-specific memory is called *state.* +Các component thường cần thay đổi những gì trên màn hình do kết quả của một tương tác. Nhập vào biểu mẫu sẽ cập nhật trường nhập liệu, nhấp vào "tiếp theo" trên băng chuyền hình ảnh sẽ thay đổi hình ảnh nào được hiển thị, nhấp vào "mua" sẽ đưa một sản phẩm vào giỏ hàng. Các component cần "ghi nhớ" mọi thứ: giá trị đầu vào hiện tại, hình ảnh hiện tại, giỏ hàng. Trong React, loại bộ nhớ dành riêng cho component này được gọi là *state.* -You can add state to a component with a [`useState`](/reference/react/useState) Hook. *Hooks* are special functions that let your components use React features (state is one of those features). The `useState` Hook lets you declare a state variable. It takes the initial state and returns a pair of values: the current state, and a state setter function that lets you update it. +Bạn có thể thêm state vào một component bằng Hook [`useState`](/reference/react/useState). *Hook* là các hàm đặc biệt cho phép các component của bạn sử dụng các tính năng của React (state là một trong những tính năng đó). Hook `useState` cho phép bạn khai báo một biến state. Nó lấy state ban đầu và trả về một cặp giá trị: state hiện tại và một hàm setter state cho phép bạn cập nhật nó. ```js const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); ``` -Here is how an image gallery uses and updates state on click: +Đây là cách một thư viện ảnh sử dụng và cập nhật state khi nhấp: <Sandpack> @@ -112,17 +112,17 @@ export default function Gallery() { return ( <> <button onClick={handleNextClick}> - Next + Tiếp theo </button> <h2> <i>{sculpture.name} </i> - by {sculpture.artist} + bởi {sculpture.artist} </h2> <h3> - ({index + 1} of {sculptureList.length}) + ({index + 1} trên {sculptureList.length}) </h3> <button onClick={handleMoreClick}> - {showMore ? 'Hide' : 'Show'} details + {showMore ? 'Ẩn' : 'Hiện'} chi tiết </button> {showMore && <p>{sculpture.description}</p>} <img @@ -229,43 +229,43 @@ button { <LearnMore path="/learn/state-a-components-memory"> -Read **[State: A Component's Memory](/learn/state-a-components-memory)** to learn how to remember a value and update it on interaction. +Đọc **[State: Bộ nhớ của một component](/learn/state-a-components-memory)** để tìm hiểu cách ghi nhớ một giá trị và cập nhật nó khi tương tác. </LearnMore> -## Render and commit {/*render-and-commit*/} +## Render và commit {/*render-and-commit*/} -Before your components are displayed on the screen, they must be rendered by React. Understanding the steps in this process will help you think about how your code executes and explain its behavior. +Trước khi các component của bạn được hiển thị trên màn hình, chúng phải được render bởi React. Hiểu các bước trong quy trình này sẽ giúp bạn suy nghĩ về cách mã của bạn thực thi và giải thích hành vi của nó. -Imagine that your components are cooks in the kitchen, assembling tasty dishes from ingredients. In this scenario, React is the waiter who puts in requests from customers and brings them their orders. This process of requesting and serving UI has three steps: +Hãy tưởng tượng rằng các component của bạn là những đầu bếp trong bếp, lắp ráp các món ăn ngon từ các nguyên liệu. Trong kịch bản này, React là người phục vụ đưa các yêu cầu từ khách hàng và mang chúng đến cho họ. Quá trình yêu cầu và phục vụ UI này có ba bước: -1. **Triggering** a render (delivering the diner's order to the kitchen) -2. **Rendering** the component (preparing the order in the kitchen) -3. **Committing** to the DOM (placing the order on the table) +1. **Kích hoạt** render (giao đơn đặt hàng của khách ăn tối cho nhà bếp) +2. **Rendering** component (chuẩn bị đơn hàng trong bếp) +3. **Committing** vào DOM (đặt hàng lên bàn) <IllustrationBlock sequential> - <Illustration caption="Trigger" alt="React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen." src="/images/docs/illustrations/i_render-and-commit1.png" /> + <Illustration caption="Kích hoạt" alt="React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen." src="/images/docs/illustrations/i_render-and-commit1.png" /> <Illustration caption="Render" alt="The Card Chef gives React a fresh Card component." src="/images/docs/illustrations/i_render-and-commit2.png" /> <Illustration caption="Commit" alt="React delivers the Card to the user at their table." src="/images/docs/illustrations/i_render-and-commit3.png" /> </IllustrationBlock> <LearnMore path="/learn/render-and-commit"> -Read **[Render and Commit](/learn/render-and-commit)** to learn the lifecycle of a UI update. +Đọc **[Render và Commit](/learn/render-and-commit)** để tìm hiểu vòng đời của một bản cập nhật UI. </LearnMore> -## State as a snapshot {/*state-as-a-snapshot*/} +## State như một snapshot {/*state-as-a-snapshot*/} -Unlike regular JavaScript variables, React state behaves more like a snapshot. Setting it does not change the state variable you already have, but instead triggers a re-render. This can be surprising at first! +Không giống như các biến JavaScript thông thường, state của React hoạt động giống như một snapshot hơn. Đặt nó không thay đổi biến state bạn đã có, mà thay vào đó kích hoạt một lần render lại. Điều này có thể gây ngạc nhiên lúc đầu! ```js console.log(count); // 0 -setCount(count + 1); // Request a re-render with 1 -console.log(count); // Still 0! +setCount(count + 1); // Yêu cầu render lại với 1 +console.log(count); // Vẫn là 0! ``` -This behavior helps you avoid subtle bugs. Here is a little chat app. Try to guess what happens if you press "Send" first and *then* change the recipient to Bob. Whose name will appear in the `alert` five seconds later? +Hành vi này giúp bạn tránh các lỗi tinh vi. Đây là một ứng dụng trò chuyện nhỏ. Hãy thử đoán điều gì xảy ra nếu bạn nhấn "Gửi" trước và *sau đó* thay đổi người nhận thành Bob. Tên của ai sẽ xuất hiện trong `alert` năm giây sau đó? <Sandpack> @@ -279,14 +279,14 @@ export default function Form() { function handleSubmit(e) { e.preventDefault(); setTimeout(() => { - alert(`You said ${message} to ${to}`); + alert(`Bạn đã nói ${message} với ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> - To:{' '} + Đến:{' '} <select value={to} onChange={e => setTo(e.target.value)}> @@ -295,11 +295,11 @@ export default function Form() { </select> </label> <textarea - placeholder="Message" + placeholder="Tin nhắn" value={message} onChange={e => setMessage(e.target.value)} /> - <button type="submit">Send</button> + <button type="submit">Gửi</button> </form> ); } @@ -314,13 +314,13 @@ label, textarea { margin-bottom: 10px; display: block; } <LearnMore path="/learn/state-as-a-snapshot"> -Read **[State as a Snapshot](/learn/state-as-a-snapshot)** to learn why state appears "fixed" and unchanging inside the event handlers. +Đọc **[State như một Snapshot](/learn/state-as-a-snapshot)** để tìm hiểu lý do tại sao state xuất hiện "cố định" và không thay đổi bên trong các trình xử lý sự kiện. </LearnMore> -## Queueing a series of state updates {/*queueing-a-series-of-state-updates*/} +## Xếp hàng đợi một loạt các bản cập nhật state {/*queueing-a-series-of-state-updates*/} -This component is buggy: clicking "+3" increments the score only once. +Component này bị lỗi: nhấp vào "+3" chỉ tăng điểm một lần. <Sandpack> @@ -342,7 +342,7 @@ export default function Counter() { increment(); increment(); }}>+3</button> - <h1>Score: {score}</h1> + <h1>Điểm: {score}</h1> </> ) } @@ -354,7 +354,7 @@ button { display: inline-block; margin: 10px; font-size: 20px; } </Sandpack> -[State as a Snapshot](/learn/state-as-a-snapshot) explains why this is happening. Setting state requests a new re-render, but does not change it in the already running code. So `score` continues to be `0` right after you call `setScore(score + 1)`. +[State như một Snapshot](/learn/state-as-a-snapshot) giải thích tại sao điều này xảy ra. Đặt state yêu cầu một lần render lại mới, nhưng không thay đổi nó trong mã đã chạy. Vì vậy, `score` tiếp tục là `0` ngay sau khi bạn gọi `setScore(score + 1)`. ```js console.log(score); // 0 @@ -366,7 +366,7 @@ setScore(score + 1); // setScore(0 + 1); console.log(score); // 0 ``` -You can fix this by passing an *updater function* when setting state. Notice how replacing `setScore(score + 1)` with `setScore(s => s + 1)` fixes the "+3" button. This lets you queue multiple state updates. +Bạn có thể khắc phục điều này bằng cách truyền một *hàm cập nhật* khi đặt state. Lưu ý cách thay thế `setScore(score + 1)` bằng `setScore(s => s + 1)` sẽ sửa nút "+3". Điều này cho phép bạn xếp hàng đợi nhiều bản cập nhật state. <Sandpack> @@ -388,7 +388,7 @@ export default function Counter() { increment(); increment(); }}>+3</button> - <h1>Score: {score}</h1> + <h1>Điểm: {score}</h1> </> ) } @@ -402,15 +402,15 @@ button { display: inline-block; margin: 10px; font-size: 20px; } <LearnMore path="/learn/queueing-a-series-of-state-updates"> -Read **[Queueing a Series of State Updates](/learn/queueing-a-series-of-state-updates)** to learn how to queue a sequence of state updates. +Đọc **[Xếp hàng đợi một loạt các bản cập nhật state](/learn/queueing-a-series-of-state-updates)** để tìm hiểu cách xếp hàng đợi một chuỗi các bản cập nhật state. </LearnMore> -## Updating objects in state {/*updating-objects-in-state*/} +## Cập nhật các đối tượng trong state {/*updating-objects-in-state*/} -State can hold any kind of JavaScript value, including objects. But you shouldn't change objects and arrays that you hold in the React state directly. Instead, when you want to update an object and array, you need to create a new one (or make a copy of an existing one), and then update the state to use that copy. +State có thể giữ bất kỳ loại giá trị JavaScript nào, bao gồm cả đối tượng. Nhưng bạn không nên thay đổi trực tiếp các đối tượng và mảng mà bạn giữ trong state của React. Thay vào đó, khi bạn muốn cập nhật một đối tượng và mảng, bạn cần tạo một đối tượng mới (hoặc tạo một bản sao của một đối tượng hiện có), sau đó cập nhật state để sử dụng bản sao đó. -Usually, you will use the `...` spread syntax to copy objects and arrays that you want to change. For example, updating a nested object could look like this: +Thông thường, bạn sẽ sử dụng cú pháp spread `...` để sao chép các đối tượng và mảng mà bạn muốn thay đổi. Ví dụ: cập nhật một đối tượng lồng nhau có thể trông như thế này: <Sandpack> @@ -467,28 +467,28 @@ export default function Form() { return ( <> <label> - Name: + Tên: <input value={person.name} onChange={handleNameChange} /> </label> <label> - Title: + Tiêu đề: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> - City: + Thành phố: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> - Image: + Hình ảnh: <input value={person.artwork.image} onChange={handleImageChange} @@ -496,10 +496,10 @@ export default function Form() { </label> <p> <i>{person.artwork.title}</i> - {' by '} + {' bởi '} {person.name} <br /> - (located in {person.artwork.city}) + (tọa lạc tại {person.artwork.city}) </p> <img src={person.artwork.image} @@ -518,7 +518,7 @@ img { width: 200px; height: 200px; } </Sandpack> -If copying objects in code gets tedious, you can use a library like [Immer](https://github.com/immerjs/use-immer) to reduce repetitive code: +Nếu việc sao chép các đối tượng trong mã trở nên tẻ nhạt, bạn có thể sử dụng một thư viện như [Immer](https://github.com/immerjs/use-immer) để giảm mã lặp đi lặp lại: <Sandpack> @@ -562,28 +562,28 @@ export default function Form() { return ( <> <label> - Name: + Tên: <input value={person.name} onChange={handleNameChange} /> </label> <label> - Title: + Tiêu đề: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> - City: + Thành phố: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> - Image: + Hình ảnh: <input value={person.artwork.image} onChange={handleImageChange} @@ -591,10 +591,10 @@ export default function Form() { </label> <p> <i>{person.artwork.title}</i> - {' by '} + {' bởi '} {person.name} <br /> - (located in {person.artwork.city}) + (tọa lạc tại {person.artwork.city}) </p> <img src={person.artwork.image} @@ -633,13 +633,13 @@ img { width: 200px; height: 200px; } <LearnMore path="/learn/updating-objects-in-state"> -Read **[Updating Objects in State](/learn/updating-objects-in-state)** to learn how to update objects correctly. +Đọc **[Cập nhật các đối tượng trong state](/learn/updating-objects-in-state)** để tìm hiểu cách cập nhật các đối tượng một cách chính xác. </LearnMore> -## Updating arrays in state {/*updating-arrays-in-state*/} +## Cập nhật các mảng trong state {/*updating-arrays-in-state*/} -Arrays are another type of mutable JavaScript objects you can store in state and should treat as read-only. Just like with objects, when you want to update an array stored in state, you need to create a new one (or make a copy of an existing one), and then set state to use the new array: +Mảng là một loại đối tượng JavaScript có thể thay đổi khác mà bạn có thể lưu trữ trong state và nên coi là chỉ đọc. Giống như với các đối tượng, khi bạn muốn cập nhật một mảng được lưu trữ trong state, bạn cần tạo một mảng mới (hoặc tạo một bản sao của một mảng hiện có), sau đó đặt state để sử dụng mảng mới: <Sandpack> @@ -669,8 +669,8 @@ export default function BucketList() { return ( <> - <h1>Art Bucket List</h1> - <h2>My list of art to see:</h2> + <h1>Danh sách việc nên làm nghệ thuật</h1> + <h2>Danh sách nghệ thuật của tôi để xem:</h2> <ItemList artworks={list} onToggle={handleToggle} /> @@ -705,7 +705,7 @@ function ItemList({ artworks, onToggle }) { </Sandpack> -If copying arrays in code gets tedious, you can use a library like [Immer](https://github.com/immerjs/use-immer) to reduce repetitive code: +Nếu việc sao chép các mảng trong mã trở nên tẻ nhạt, bạn có thể sử dụng một thư viện như [Immer](https://github.com/immerjs/use-immer) để giảm mã lặp đi lặp lại: <Sandpack> @@ -733,8 +733,8 @@ export default function BucketList() { return ( <> - <h1>Art Bucket List</h1> - <h2>My list of art to see:</h2> + <h1>Danh sách việc nên làm nghệ thuật</h1> + <h2>Danh sách nghệ thuật của tôi để xem:</h2> <ItemList artworks={list} onToggle={handleToggle} /> @@ -789,12 +789,12 @@ function ItemList({ artworks, onToggle }) { <LearnMore path="/learn/updating-arrays-in-state"> -Read **[Updating Arrays in State](/learn/updating-arrays-in-state)** to learn how to update arrays correctly. +Đọc **[Cập nhật các mảng trong state](/learn/updating-arrays-in-state)** để tìm hiểu cách cập nhật các mảng một cách chính xác. </LearnMore> -## What's next? {/*whats-next*/} +## Tiếp theo là gì? {/*whats-next*/} -Head over to [Responding to Events](/learn/responding-to-events) to start reading this chapter page by page! +Đi tới [Phản hồi các sự kiện](/learn/responding-to-events) để bắt đầu đọc trang chương này theo từng trang! -Or, if you're already familiar with these topics, why not read about [Managing State](/learn/managing-state)? +Hoặc, nếu bạn đã quen thuộc với các chủ đề này, tại sao không đọc về [Quản lý state](/learn/managing-state)? diff --git a/src/content/learn/build-a-react-app-from-scratch.md b/src/content/learn/build-a-react-app-from-scratch.md index 721f670dd..360239435 100644 --- a/src/content/learn/build-a-react-app-from-scratch.md +++ b/src/content/learn/build-a-react-app-from-scratch.md @@ -1,143 +1,140 @@ --- -title: Build a React app from Scratch +title: Xây dựng một ứng dụng React từ đầu --- <Intro> -If your app has constraints not well-served by existing frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, you can build a React app from scratch. +Nếu ứng dụng của bạn có những ràng buộc mà các framework hiện có không đáp ứng được, bạn thích tự xây dựng framework của riêng mình hoặc bạn chỉ muốn tìm hiểu những điều cơ bản của một ứng dụng React, bạn có thể xây dựng một ứng dụng React từ đầu. </Intro> <DeepDive> -#### Consider using a framework {/*consider-using-a-framework*/} +#### Cân nhắc sử dụng một framework {/*consider-using-a-framework*/} -Starting from scratch is an easy way to get started using React, but a major tradeoff to be aware of is that going this route is often the same as building your own adhoc framework. As your requirements evolve, you may need to solve more framework-like problems that our recommended frameworks already have well developed and supported solutions for. +Bắt đầu từ đầu là một cách dễ dàng để bắt đầu sử dụng React, nhưng một nhược điểm lớn cần lưu ý là đi theo con đường này thường giống như xây dựng framework adhoc của riêng bạn. Khi các yêu cầu của bạn phát triển, bạn có thể cần giải quyết nhiều vấn đề giống như framework mà các framework được đề xuất của chúng tôi đã có các giải pháp được phát triển và hỗ trợ tốt. -For example, if in the future your app needs support for server-side rendering (SSR), static site generation (SSG), and/or React Server Components (RSC), you will have to implement those on your own. Similarly, future React features that require integrating at the framework level will have to be implemented on your own if you want to use them. +Ví dụ: nếu trong tương lai ứng dụng của bạn cần hỗ trợ hiển thị phía máy chủ (SSR), tạo trang web tĩnh (SSG) và/hoặc React Server Components (RSC), bạn sẽ phải tự triển khai chúng. Tương tự, các tính năng React trong tương lai yêu cầu tích hợp ở cấp framework sẽ phải được triển khai bởi bạn nếu bạn muốn sử dụng chúng. -Our recommended frameworks also help you build better performing apps. For example, reducing or eliminating waterfalls from network requests makes for a better user experience. This might not be a high priority when you are building a toy project, but if your app gains users you may want to improve its performance. +Các framework được đề xuất của chúng tôi cũng giúp bạn xây dựng các ứng dụng hoạt động tốt hơn. Ví dụ: giảm hoặc loại bỏ các thác nước từ các yêu cầu mạng giúp cải thiện trải nghiệm người dùng. Điều này có thể không phải là ưu tiên hàng đầu khi bạn đang xây dựng một dự án đồ chơi, nhưng nếu ứng dụng của bạn có được người dùng, bạn có thể muốn cải thiện hiệu suất của nó. -Going this route also makes it more difficult to get support, since the way you develop routing, data-fetching, and other features will be unique to your situation. You should only choose this option if you are comfortable tackling these problems on your own, or if you’re confident that you will never need these features. +Đi theo con đường này cũng gây khó khăn hơn trong việc nhận được hỗ trợ, vì cách bạn phát triển định tuyến, tìm nạp dữ liệu và các tính năng khác sẽ là duy nhất cho tình huống của bạn. Bạn chỉ nên chọn tùy chọn này nếu bạn cảm thấy thoải mái khi tự mình giải quyết những vấn đề này hoặc nếu bạn tự tin rằng bạn sẽ không bao giờ cần những tính năng này. -For a list of recommended frameworks, check out [Creating a React App](/learn/creating-a-react-app). +Để có danh sách các framework được đề xuất, hãy xem [Tạo một ứng dụng React](/learn/creating-a-react-app). </DeepDive> +## Bước 1: Cài đặt một công cụ xây dựng {/*step-1-install-a-build-tool*/} -## Step 1: Install a build tool {/*step-1-install-a-build-tool*/} - -The first step is to install a build tool like `vite`, `parcel`, or `rsbuild`. These build tools provide features to package and run source code, provide a development server for local development and a build command to deploy your app to a production server. +Bước đầu tiên là cài đặt một công cụ xây dựng như `vite`, `parcel` hoặc `rsbuild`. Các công cụ xây dựng này cung cấp các tính năng để đóng gói và chạy mã nguồn, cung cấp một máy chủ phát triển để phát triển cục bộ và một lệnh xây dựng để triển khai ứng dụng của bạn lên một máy chủ sản xuất. ### Vite {/*vite*/} -[Vite](https://vite.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects. +[Vite](https://vite.dev/) là một công cụ xây dựng nhằm mục đích cung cấp trải nghiệm phát triển nhanh hơn và tinh gọn hơn cho các dự án web hiện đại. <TerminalBlock> {`npm create vite@latest my-app -- --template react`} </TerminalBlock> -Vite is opinionated and comes with sensible defaults out of the box. Vite has a rich ecosystem of plugins to support fast refresh, JSX, Babel/SWC, and other common features. See Vite's [React plugin](https://vite.dev/plugins/#vitejs-plugin-react) or [React SWC plugin](https://vite.dev/plugins/#vitejs-plugin-react-swc) and [React SSR example project](https://vite.dev/guide/ssr.html#example-projects) to get started. +Vite có ý kiến riêng và đi kèm với các mặc định hợp lý ngay lập tức. Vite có một hệ sinh thái plugin phong phú để hỗ trợ làm mới nhanh, JSX, Babel/SWC và các tính năng phổ biến khác. Xem [plugin React](https://vite.dev/plugins/#vitejs-plugin-react) hoặc [plugin React SWC](https://vite.dev/plugins/#vitejs-plugin-react-swc) và [dự án ví dụ React SSR](https://vite.dev/guide/ssr.html#example-projects) của Vite để bắt đầu. -Vite is already being used as a build tool in one of our [recommended frameworks](/learn/creating-a-react-app): [React Router](https://reactrouter.com/start/framework/installation). +Vite đã được sử dụng làm công cụ xây dựng trong một trong các [framework được đề xuất](/learn/creating-a-react-app) của chúng tôi: [React Router](https://reactrouter.com/start/framework/installation). ### Parcel {/*parcel*/} -[Parcel](https://parceljs.org/) combines a great out-of-the-box development experience with a scalable architecture that can take your project from just getting started to massive production applications. +[Parcel](https://parceljs.org/) kết hợp trải nghiệm phát triển tuyệt vời ngay lập tức với một kiến trúc có thể mở rộng, có thể đưa dự án của bạn từ việc chỉ mới bắt đầu đến các ứng dụng sản xuất lớn. <TerminalBlock> {`npm install --save-dev parcel`} </TerminalBlock> -Parcel supports fast refresh, JSX, TypeScript, Flow, and styling out of the box. See [Parcel's React recipe](https://parceljs.org/recipes/react/#getting-started) to get started. +Parcel hỗ trợ làm mới nhanh, JSX, TypeScript, Flow và tạo kiểu ngay lập tức. Xem [công thức React của Parcel](https://parceljs.org/recipes/react/#getting-started) để bắt đầu. ### Rsbuild {/*rsbuild*/} -[Rsbuild](https://rsbuild.dev/) is an Rspack-powered build tool that provides a seamless development experience for React applications. It comes with carefully tuned defaults and performance optimizations ready to use. +[Rsbuild](https://rsbuild.dev/) là một công cụ xây dựng được hỗ trợ bởi Rspack, cung cấp trải nghiệm phát triển liền mạch cho các ứng dụng React. Nó đi kèm với các mặc định được điều chỉnh cẩn thận và các tối ưu hóa hiệu suất sẵn sàng để sử dụng. <TerminalBlock> {`npx create-rsbuild --template react`} </TerminalBlock> -Rsbuild includes built-in support for React features like fast refresh, JSX, TypeScript, and styling. See [Rsbuild's React guide](https://rsbuild.dev/guide/framework/react) to get started. +Rsbuild bao gồm hỗ trợ tích hợp cho các tính năng React như làm mới nhanh, JSX, TypeScript và tạo kiểu. Xem [hướng dẫn React của Rsbuild](https://rsbuild.dev/guide/framework/react) để bắt đầu. <Note> -#### Metro for React Native {/*react-native*/} +#### Metro cho React Native {/*react-native*/} -If you're starting from scratch with React Native you'll need to use [Metro](https://metrobundler.dev/), the JavaScript bundler for React Native. Metro supports bundling for platforms like iOS and Android, but lacks many features when compared to the tools here. We recommend starting with Vite, Parcel, or Rsbuild unless your project requires React Native support. +Nếu bạn bắt đầu từ đầu với React Native, bạn sẽ cần sử dụng [Metro](https://metrobundler.dev/), trình đóng gói JavaScript cho React Native. Metro hỗ trợ đóng gói cho các nền tảng như iOS và Android, nhưng thiếu nhiều tính năng so với các công cụ ở đây. Chúng tôi khuyên bạn nên bắt đầu với Vite, Parcel hoặc Rsbuild trừ khi dự án của bạn yêu cầu hỗ trợ React Native. </Note> -## Step 2: Build Common Application Patterns {/*step-2-build-common-application-patterns*/} +## Bước 2: Xây dựng các mẫu ứng dụng phổ biến {/*step-2-build-common-application-patterns*/} -The build tools listed above start off with a client-only, single-page app (SPA), but don't include any further solutions for common functionality like routing, data fetching, or styling. +Các công cụ xây dựng được liệt kê ở trên bắt đầu với một ứng dụng một trang (SPA) chỉ dành cho máy khách, nhưng không bao gồm bất kỳ giải pháp nào khác cho các chức năng phổ biến như định tuyến, tìm nạp dữ liệu hoặc tạo kiểu. -The React ecosystem includes many tools for these problems. We've listed a few that are widely used as a starting point, but feel free to choose other tools if those work better for you. +Hệ sinh thái React bao gồm nhiều công cụ cho những vấn đề này. Chúng tôi đã liệt kê một vài công cụ được sử dụng rộng rãi làm điểm khởi đầu, nhưng bạn có thể tự do chọn các công cụ khác nếu chúng phù hợp hơn với bạn. -### Routing {/*routing*/} +### Định tuyến {/*routing*/} -Routing determines what content or pages to display when a user visits a particular URL. You need to set up a router to map URLs to different parts of your app. You'll also need to handle nested routes, route parameters, and query parameters. Routers can be configured within your code, or defined based on your component folder and file structures. +Định tuyến xác định nội dung hoặc trang nào sẽ hiển thị khi người dùng truy cập một URL cụ thể. Bạn cần thiết lập một bộ định tuyến để ánh xạ các URL đến các phần khác nhau của ứng dụng của bạn. Bạn cũng sẽ cần xử lý các tuyến lồng nhau, các tham số tuyến và các tham số truy vấn. Bộ định tuyến có thể được định cấu hình trong mã của bạn hoặc được xác định dựa trên cấu trúc thư mục và tệp thành phần của bạn. -Routers are a core part of modern applications, and are usually integrated with data fetching (including prefetching data for a whole page for faster loading), code splitting (to minimize client bundle sizes), and page rendering approaches (to decide how each page gets generated). +Bộ định tuyến là một phần cốt lõi của các ứng dụng hiện đại và thường được tích hợp với tìm nạp dữ liệu (bao gồm tìm nạp trước dữ liệu cho toàn bộ trang để tải nhanh hơn), phân chia mã (để giảm thiểu kích thước gói máy khách) và các phương pháp hiển thị trang (để quyết định cách tạo từng trang). -We suggest using: +Chúng tôi khuyên bạn nên sử dụng: - [React Router](https://reactrouter.com/start/data/custom) - [Tanstack Router](https://tanstack.com/router/latest) +### Tìm nạp dữ liệu {/*data-fetching*/} -### Data Fetching {/*data-fetching*/} - -Fetching data from a server or other data source is a key part of most applications. Doing this properly requires handling loading states, error states, and caching the fetched data, which can be complex. +Tìm nạp dữ liệu từ máy chủ hoặc nguồn dữ liệu khác là một phần quan trọng của hầu hết các ứng dụng. Thực hiện việc này đúng cách đòi hỏi phải xử lý các trạng thái tải, trạng thái lỗi và lưu vào bộ nhớ cache dữ liệu đã tìm nạp, điều này có thể phức tạp. -Purpose-built data fetching libraries do the hard work of fetching and caching the data for you, letting you focus on what data your app needs and how to display it. These libraries are typically used directly in your components, but can also be integrated into routing loaders for faster pre-fetching and better performance, and in server rendering as well. +Các thư viện tìm nạp dữ liệu chuyên dụng thực hiện công việc khó khăn là tìm nạp và lưu vào bộ nhớ cache dữ liệu cho bạn, cho phép bạn tập trung vào dữ liệu mà ứng dụng của bạn cần và cách hiển thị nó. Các thư viện này thường được sử dụng trực tiếp trong các thành phần của bạn, nhưng cũng có thể được tích hợp vào bộ tải định tuyến để tìm nạp trước nhanh hơn và hiệu suất tốt hơn, cũng như trong hiển thị phía máy chủ. -Note that fetching data directly in components can lead to slower loading times due to network request waterfalls, so we recommend prefetching data in router loaders or on the server as much as possible! This allows a page's data to be fetched all at once as the page is being displayed. +Lưu ý rằng việc tìm nạp dữ liệu trực tiếp trong các thành phần có thể dẫn đến thời gian tải chậm hơn do thác nước yêu cầu mạng, vì vậy chúng tôi khuyên bạn nên tìm nạp trước dữ liệu trong bộ tải định tuyến hoặc trên máy chủ càng nhiều càng tốt! Điều này cho phép dữ liệu của một trang được tìm nạp tất cả cùng một lúc khi trang đang được hiển thị. -If you're fetching data from most backends or REST-style APIs, we suggest using: +Nếu bạn đang tìm nạp dữ liệu từ hầu hết các backend hoặc API kiểu REST, chúng tôi khuyên bạn nên sử dụng: - [React Query](https://react-query.tanstack.com/) - [SWR](https://swr.vercel.app/) - [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) -If you're fetching data from a GraphQL API, we suggest using: +Nếu bạn đang tìm nạp dữ liệu từ API GraphQL, chúng tôi khuyên bạn nên sử dụng: - [Apollo](https://www.apollographql.com/docs/react) - [Relay](https://relay.dev/) +### Phân chia mã {/*code-splitting*/} -### Code-splitting {/*code-splitting*/} - -Code-splitting is the process of breaking your app into smaller bundles that can be loaded on demand. An app's code size increases with every new feature and additional dependency. Apps can become slow to load because all of the code for the entire app needs to be sent before it can be used. Caching, reducing features/dependencies, and moving some code to run on the server can help mitigate slow loading but are incomplete solutions that can sacrifice functionality if overused. +Phân chia mã là quá trình chia ứng dụng của bạn thành các gói nhỏ hơn có thể được tải theo yêu cầu. Kích thước mã của một ứng dụng tăng lên với mỗi tính năng mới và phụ thuộc bổ sung. Các ứng dụng có thể trở nên chậm tải vì tất cả mã cho toàn bộ ứng dụng cần được gửi trước khi nó có thể được sử dụng. Lưu vào bộ nhớ cache, giảm các tính năng/phụ thuộc và di chuyển một số mã để chạy trên máy chủ có thể giúp giảm tải chậm nhưng là các giải pháp không đầy đủ có thể hy sinh chức năng nếu lạm dụng. -Similarly, if you rely on the apps using your framework to split the code, you might encounter situations where loading becomes slower than if no code splitting were happening at all. For example, [lazily loading](/reference/react/lazy) a chart delays sending the code needed to render the chart, splitting the chart code from the rest of the app. [Parcel supports code splitting with React.lazy](https://parceljs.org/recipes/react/#code-splitting). However, if the chart loads its data *after* it has been initially rendered you are now waiting twice. This is a waterfall: rather than fetching the data for the chart and sending the code to render it simultaneously, you must wait for each step to complete one after the other. +Tương tự, nếu bạn dựa vào các ứng dụng sử dụng framework của bạn để phân chia mã, bạn có thể gặp phải các tình huống mà việc tải trở nên chậm hơn so với khi không có phân chia mã nào xảy ra. Ví dụ: [tải một cách lười biếng](/reference/react/lazy) một biểu đồ sẽ trì hoãn việc gửi mã cần thiết để hiển thị biểu đồ, tách mã biểu đồ khỏi phần còn lại của ứng dụng. [Parcel hỗ trợ phân chia mã với React.lazy](https://parceljs.org/recipes/react/#code-splitting). Tuy nhiên, nếu biểu đồ tải dữ liệu của nó *sau khi* nó đã được hiển thị ban đầu, bạn sẽ phải đợi hai lần. Đây là một thác nước: thay vì tìm nạp dữ liệu cho biểu đồ và gửi mã để hiển thị nó đồng thời, bạn phải đợi từng bước hoàn thành lần lượt. -Splitting code by route, when integrated with bundling and data fetching, can reduce the initial load time of your app and the time it takes for the largest visible content of the app to render ([Largest Contentful Paint](https://web.dev/articles/lcp)). +Việc phân chia mã theo tuyến, khi được tích hợp với đóng gói và tìm nạp dữ liệu, có thể giảm thời gian tải ban đầu của ứng dụng của bạn và thời gian cần thiết để hiển thị nội dung hiển thị lớn nhất của ứng dụng ([Largest Contentful Paint](https://web.dev/articles/lcp)). -For code-splitting instructions, see your build tool docs: -- [Vite build optimizations](https://v3.vitejs.dev/guide/features.html#build-optimizations) -- [Parcel code splitting](https://parceljs.org/features/code-splitting/) -- [Rsbuild code splitting](https://rsbuild.dev/guide/optimization/code-splitting) +Để biết hướng dẫn phân chia mã, hãy xem tài liệu công cụ xây dựng của bạn: +- [Tối ưu hóa bản dựng Vite](https://v3.vitejs.dev/guide/features.html#build-optimizations) +- [Phân chia mã Parcel](https://parceljs.org/features/code-splitting/) +- [Phân chia mã Rsbuild](https://rsbuild.dev/guide/optimization/code-splitting) -### Improving Application Performance {/*improving-application-performance*/} +### Cải thiện hiệu suất ứng dụng {/*improving-application-performance*/} -Since the build tool you select only support single page apps (SPAs) you'll need to implement other [rendering patterns](https://www.patterns.dev/vanilla/rendering-patterns) like server-side rendering (SSR), static site generation (SSG), and/or React Server Components (RSC). Even if you don't need these features at first, in the future there may be some routes that would benefit SSR, SSG or RSC. +Vì công cụ xây dựng bạn chọn chỉ hỗ trợ các ứng dụng một trang (SPA), bạn sẽ cần triển khai các [mẫu hiển thị](https://www.patterns.dev/vanilla/rendering-patterns) khác như hiển thị phía máy chủ (SSR), tạo trang web tĩnh (SSG) và/hoặc React Server Components (RSC). Ngay cả khi bạn không cần những tính năng này ngay từ đầu, trong tương lai có thể có một số tuyến sẽ được hưởng lợi từ SSR, SSG hoặc RSC. -* **Single-page apps (SPA)** load a single HTML page and dynamically updates the page as the user interacts with the app. SPAs are easier to get started with, but they can have slower initial load times. SPAs are the default architecture for most build tools. +* **Ứng dụng một trang (SPA)** tải một trang HTML duy nhất và cập nhật trang một cách động khi người dùng tương tác với ứng dụng. SPA dễ bắt đầu hơn, nhưng chúng có thể có thời gian tải ban đầu chậm hơn. SPA là kiến trúc mặc định cho hầu hết các công cụ xây dựng. -* **Streaming Server-side rendering (SSR)** renders a page on the server and sends the fully rendered page to the client. SSR can improve performance, but it can be more complex to set up and maintain than a single-page app. With the addition of streaming, SSR can be very complex to set up and maintain. See [Vite's SSR guide]( https://vite.dev/guide/ssr). +* **Hiển thị phía máy chủ (SSR)** hiển thị một trang trên máy chủ và gửi trang được hiển thị đầy đủ đến máy khách. SSR có thể cải thiện hiệu suất, nhưng nó có thể phức tạp hơn để thiết lập và duy trì so với một ứng dụng một trang. Với việc bổ sung tính năng phát trực tuyến, SSR có thể rất phức tạp để thiết lập và duy trì. Xem [hướng dẫn SSR của Vite](https://vite.dev/guide/ssr). -* **Static site generation (SSG)** generates static HTML files for your app at build time. SSG can improve performance, but it can be more complex to set up and maintain than server-side rendering. See [Vite's SSG guide](https://vite.dev/guide/ssr.html#pre-rendering-ssg). +* **Tạo trang web tĩnh (SSG)** tạo các tệp HTML tĩnh cho ứng dụng của bạn tại thời điểm xây dựng. SSG có thể cải thiện hiệu suất, nhưng nó có thể phức tạp hơn để thiết lập và duy trì so với hiển thị phía máy chủ. Xem [hướng dẫn SSG của Vite](https://vite.dev/guide/ssr.html#pre-rendering-ssg). -* **React Server Components (RSC)** lets you mix build-time, server-only, and interactive components in a single React tree. RSC can improve performance, but it currently requires deep expertise to set up and maintain. See [Parcel's RSC examples](https://github.com/parcel-bundler/rsc-examples). +* **React Server Components (RSC)** cho phép bạn kết hợp các thành phần chỉ dành cho máy chủ, thời gian xây dựng và tương tác trong một cây React duy nhất. RSC có thể cải thiện hiệu suất, nhưng hiện tại nó đòi hỏi chuyên môn sâu để thiết lập và duy trì. Xem [ví dụ RSC của Parcel](https://github.com/parcel-bundler/rsc-examples). -Your rendering strategies need to integrate with your router so apps built with your framework can choose the rendering strategy on a per-route level. This will enable different rendering strategies without having to rewrite your whole app. For example, the landing page for your app might benefit from being statically generated (SSG), while a page with a content feed might perform best with server-side rendering. +Các chiến lược hiển thị của bạn cần tích hợp với bộ định tuyến của bạn để các ứng dụng được xây dựng bằng framework của bạn có thể chọn chiến lược hiển thị trên cơ sở mỗi tuyến. Điều này sẽ cho phép các chiến lược hiển thị khác nhau mà không cần phải viết lại toàn bộ ứng dụng của bạn. Ví dụ: trang đích cho ứng dụng của bạn có thể được hưởng lợi từ việc được tạo tĩnh (SSG), trong khi một trang có nguồn cấp nội dung có thể hoạt động tốt nhất với hiển thị phía máy chủ. -Using the right rendering strategy for the right routes can decrease the time it takes for the first byte of content to be loaded ([Time to First Byte](https://web.dev/articles/ttfb)), the first piece of content to render ([First Contentful Paint](https://web.dev/articles/fcp)), and the largest visible content of the app to render ([Largest Contentful Paint](https://web.dev/articles/lcp)). +Sử dụng đúng chiến lược hiển thị cho đúng tuyến có thể giảm thời gian cần thiết để tải byte nội dung đầu tiên ([Time to First Byte](https://web.dev/articles/ttfb)), phần nội dung đầu tiên được hiển thị ([First Contentful Paint](https://web.dev/articles/fcp)) và nội dung hiển thị lớn nhất của ứng dụng được hiển thị ([Largest Contentful Paint](https://web.dev/articles/lcp)). -### And more... {/*and-more*/} +### Và hơn thế nữa... {/*and-more*/} -These are just a few examples of the features a new app will need to consider when building from scratch. Many limitations you'll hit can be difficult to solve as each problem is interconnected with the others and can require deep expertise in problem areas you may not be familiar with. +Đây chỉ là một vài ví dụ về các tính năng mà một ứng dụng mới sẽ cần xem xét khi xây dựng từ đầu. Nhiều hạn chế bạn sẽ gặp phải có thể khó giải quyết vì mỗi vấn đề được kết nối với những vấn đề khác và có thể yêu cầu chuyên môn sâu trong các lĩnh vực vấn đề mà bạn có thể không quen thuộc. -If you don't want to solve these problems on your own, you can [get started with a framework](/learn/creating-a-react-app) that provides these features out of the box. +Nếu bạn không muốn tự mình giải quyết những vấn đề này, bạn có thể [bắt đầu với một framework](/learn/creating-a-react-app) cung cấp các tính năng này ngay lập tức. diff --git a/src/content/learn/conditional-rendering.md b/src/content/learn/conditional-rendering.md index 95be5d2e0..ee20bc11d 100644 --- a/src/content/learn/conditional-rendering.md +++ b/src/content/learn/conditional-rendering.md @@ -1,24 +1,24 @@ --- -title: Conditional Rendering +title: Kết xuất có điều kiện --- <Intro> -Your components will often need to display different things depending on different conditions. In React, you can conditionally render JSX using JavaScript syntax like `if` statements, `&&`, and `? :` operators. +Các component của bạn thường cần hiển thị những thứ khác nhau tùy thuộc vào các điều kiện khác nhau. Trong React, bạn có thể kết xuất JSX có điều kiện bằng cú pháp JavaScript như câu lệnh `if`, `&&` và toán tử `? :`. </Intro> <YouWillLearn> -* How to return different JSX depending on a condition -* How to conditionally include or exclude a piece of JSX -* Common conditional syntax shortcuts you’ll encounter in React codebases +* Cách trả về JSX khác nhau tùy thuộc vào một điều kiện +* Cách bao gồm hoặc loại trừ một phần JSX có điều kiện +* Các phím tắt cú pháp có điều kiện phổ biến mà bạn sẽ gặp trong các codebase React </YouWillLearn> -## Conditionally returning JSX {/*conditionally-returning-jsx*/} +## Kết xuất JSX có điều kiện {/*conditionally-returning-jsx*/} -Let’s say you have a `PackingList` component rendering several `Item`s, which can be marked as packed or not: +Giả sử bạn có một component `PackingList` kết xuất một số `Item`, có thể được đánh dấu là đã đóng gói hoặc chưa: <Sandpack> @@ -30,19 +30,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -52,9 +52,9 @@ export default function PackingList() { </Sandpack> -Notice that some of the `Item` components have their `isPacked` prop set to `true` instead of `false`. You want to add a checkmark (✅) to packed items if `isPacked={true}`. +Lưu ý rằng một số component `Item` có prop `isPacked` được đặt thành `true` thay vì `false`. Bạn muốn thêm dấu kiểm (✅) vào các mục đã đóng gói nếu `isPacked={true}`. -You can write this as an [`if`/`else` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) like so: +Bạn có thể viết điều này dưới dạng câu lệnh [`if`/`else`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) như sau: ```js if (isPacked) { @@ -63,7 +63,7 @@ if (isPacked) { return <li className="item">{name}</li>; ``` -If the `isPacked` prop is `true`, this code **returns a different JSX tree.** With this change, some of the items get a checkmark at the end: +Nếu prop `isPacked` là `true`, đoạn code này **trả về một cây JSX khác.** Với thay đổi này, một số mục sẽ có dấu kiểm ở cuối: <Sandpack> @@ -78,19 +78,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -100,13 +100,13 @@ export default function PackingList() { </Sandpack> -Try editing what gets returned in either case, and see how the result changes! +Hãy thử chỉnh sửa những gì được trả về trong mỗi trường hợp và xem kết quả thay đổi như thế nào! -Notice how you're creating branching logic with JavaScript's `if` and `return` statements. In React, control flow (like conditions) is handled by JavaScript. +Lưu ý cách bạn đang tạo logic phân nhánh với các câu lệnh `if` và `return` của JavaScript. Trong React, luồng điều khiển (như điều kiện) được xử lý bởi JavaScript. -### Conditionally returning nothing with `null` {/*conditionally-returning-nothing-with-null*/} +### Kết xuất có điều kiện không có gì với `null` {/*conditionally-returning-nothing-with-null*/} -In some situations, you won't want to render anything at all. For example, say you don't want to show packed items at all. A component must return something. In this case, you can return `null`: +Trong một số trường hợp, bạn sẽ không muốn kết xuất bất cứ thứ gì. Ví dụ: giả sử bạn không muốn hiển thị các mục đã đóng gói. Một component phải trả về một cái gì đó. Trong trường hợp này, bạn có thể trả về `null`: ```js if (isPacked) { @@ -115,7 +115,7 @@ if (isPacked) { return <li className="item">{name}</li>; ``` -If `isPacked` is true, the component will return nothing, `null`. Otherwise, it will return JSX to render. +Nếu `isPacked` là true, component sẽ không trả về gì, `null`. Nếu không, nó sẽ trả về JSX để kết xuất. <Sandpack> @@ -130,19 +130,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -152,23 +152,23 @@ export default function PackingList() { </Sandpack> -In practice, returning `null` from a component isn't common because it might surprise a developer trying to render it. More often, you would conditionally include or exclude the component in the parent component's JSX. Here's how to do that! +Trong thực tế, việc trả về `null` từ một component không phổ biến vì nó có thể gây bất ngờ cho một developer đang cố gắng kết xuất nó. Thông thường, bạn sẽ bao gồm hoặc loại trừ component một cách có điều kiện trong JSX của component cha. Đây là cách thực hiện! -## Conditionally including JSX {/*conditionally-including-jsx*/} +## Bao gồm JSX có điều kiện {/*conditionally-including-jsx*/} -In the previous example, you controlled which (if any!) JSX tree would be returned by the component. You may already have noticed some duplication in the render output: +Trong ví dụ trước, bạn đã kiểm soát cây JSX nào (nếu có!) sẽ được trả về bởi component. Bạn có thể đã nhận thấy một số trùng lặp trong đầu ra kết xuất: ```js <li className="item">{name} ✅</li> ``` -is very similar to +rất giống với ```js <li className="item">{name}</li> ``` -Both of the conditional branches return `<li className="item">...</li>`: +Cả hai nhánh có điều kiện đều trả về `<li className="item">...</li>`: ```js if (isPacked) { @@ -177,13 +177,13 @@ if (isPacked) { return <li className="item">{name}</li>; ``` -While this duplication isn't harmful, it could make your code harder to maintain. What if you want to change the `className`? You'd have to do it in two places in your code! In such a situation, you could conditionally include a little JSX to make your code more [DRY.](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) +Mặc dù sự trùng lặp này không có hại, nhưng nó có thể khiến code của bạn khó bảo trì hơn. Điều gì sẽ xảy ra nếu bạn muốn thay đổi `className`? Bạn sẽ phải thực hiện nó ở hai nơi trong code của bạn! Trong một tình huống như vậy, bạn có thể bao gồm một chút JSX có điều kiện để làm cho code của bạn [DRY hơn.](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) -### Conditional (ternary) operator (`? :`) {/*conditional-ternary-operator--*/} +### Toán tử điều kiện (bậc ba) (`? :`) {/*conditional-ternary-operator--*/} -JavaScript has a compact syntax for writing a conditional expression -- the [conditional operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) or "ternary operator". +JavaScript có một cú pháp ngắn gọn để viết một biểu thức điều kiện -- [toán tử điều kiện](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) hoặc "toán tử bậc ba". -Instead of this: +Thay vì thế này: ```js if (isPacked) { @@ -192,7 +192,7 @@ if (isPacked) { return <li className="item">{name}</li>; ``` -You can write this: +Bạn có thể viết thế này: ```js return ( @@ -202,17 +202,17 @@ return ( ); ``` -You can read it as *"if `isPacked` is true, then (`?`) render `name + ' ✅'`, otherwise (`:`) render `name`"*. +Bạn có thể đọc nó là *"nếu `isPacked` là true, thì (`?`) kết xuất `name + ' ✅'`, nếu không (`:`) kết xuất `name`"*. <DeepDive> -#### Are these two examples fully equivalent? {/*are-these-two-examples-fully-equivalent*/} +#### Hai ví dụ này có hoàn toàn tương đương không? {/*are-these-two-examples-fully-equivalent*/} -If you're coming from an object-oriented programming background, you might assume that the two examples above are subtly different because one of them may create two different "instances" of `<li>`. But JSX elements aren't "instances" because they don't hold any internal state and aren't real DOM nodes. They're lightweight descriptions, like blueprints. So these two examples, in fact, *are* completely equivalent. [Preserving and Resetting State](/learn/preserving-and-resetting-state) goes into detail about how this works. +Nếu bạn đến từ nền tảng lập trình hướng đối tượng, bạn có thể cho rằng hai ví dụ trên hơi khác nhau vì một trong số chúng có thể tạo ra hai "thể hiện" khác nhau của `<li>`. Nhưng các phần tử JSX không phải là "thể hiện" vì chúng không giữ bất kỳ trạng thái bên trong nào và không phải là các nút DOM thực. Chúng là các mô tả nhẹ, giống như bản thiết kế. Vì vậy, hai ví dụ này, trên thực tế, *hoàn toàn* tương đương. [Bảo toàn và Đặt lại Trạng thái](/learn/preserving-and-resetting-state) đi vào chi tiết về cách thức hoạt động của điều này. </DeepDive> -Now let's say you want to wrap the completed item's text into another HTML tag, like `<del>` to strike it out. You can add even more newlines and parentheses so that it's easier to nest more JSX in each of the cases: +Bây giờ, giả sử bạn muốn bọc văn bản của mục đã hoàn thành vào một thẻ HTML khác, chẳng hạn như `<del>` để gạch bỏ nó. Bạn có thể thêm nhiều dòng mới và dấu ngoặc đơn hơn để dễ dàng lồng thêm JSX vào mỗi trường hợp: <Sandpack> @@ -234,19 +234,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -256,11 +256,11 @@ export default function PackingList() { </Sandpack> -This style works well for simple conditions, but use it in moderation. If your components get messy with too much nested conditional markup, consider extracting child components to clean things up. In React, markup is a part of your code, so you can use tools like variables and functions to tidy up complex expressions. +Kiểu này hoạt động tốt cho các điều kiện đơn giản, nhưng hãy sử dụng nó một cách điều độ. Nếu các component của bạn trở nên lộn xộn với quá nhiều đánh dấu có điều kiện lồng nhau, hãy cân nhắc trích xuất các component con để làm cho mọi thứ gọn gàng hơn. Trong React, đánh dấu là một phần của code của bạn, vì vậy bạn có thể sử dụng các công cụ như biến và hàm để sắp xếp các biểu thức phức tạp. -### Logical AND operator (`&&`) {/*logical-and-operator-*/} +### Toán tử AND logic (`&&`) {/*logical-and-operator-*/} -Another common shortcut you'll encounter is the [JavaScript logical AND (`&&`) operator.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#:~:text=The%20logical%20AND%20(%20%26%26%20)%20operator,it%20returns%20a%20Boolean%20value.) Inside React components, it often comes up when you want to render some JSX when the condition is true, **or render nothing otherwise.** With `&&`, you could conditionally render the checkmark only if `isPacked` is `true`: +Một phím tắt phổ biến khác mà bạn sẽ gặp là [toán tử AND logic (`&&`) của JavaScript.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#:~:text=The%20logical%20AND%20(%20%26%26%20)%20operator,it%20returns%20a%20Boolean%20value.) Bên trong các component React, nó thường xuất hiện khi bạn muốn kết xuất một số JSX khi điều kiện là true, **hoặc không kết xuất gì nếu không.** Với `&&`, bạn có thể kết xuất dấu kiểm một cách có điều kiện chỉ khi `isPacked` là `true`: ```js return ( @@ -270,9 +270,9 @@ return ( ); ``` -You can read this as *"if `isPacked`, then (`&&`) render the checkmark, otherwise, render nothing"*. +Bạn có thể đọc điều này là *"nếu `isPacked`, thì (`&&`) kết xuất dấu kiểm, nếu không, không kết xuất gì"*. -Here it is in action: +Đây là cách nó hoạt động: <Sandpack> @@ -288,19 +288,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -310,30 +310,29 @@ export default function PackingList() { </Sandpack> -A [JavaScript && expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND) returns the value of its right side (in our case, the checkmark) if the left side (our condition) is `true`. But if the condition is `false`, the whole expression becomes `false`. React considers `false` as a "hole" in the JSX tree, just like `null` or `undefined`, and doesn't render anything in its place. - +Một [biểu thức && JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND) trả về giá trị của phía bên phải của nó (trong trường hợp của chúng ta, dấu kiểm) nếu phía bên trái (điều kiện của chúng ta) là `true`. Nhưng nếu điều kiện là `false`, toàn bộ biểu thức sẽ trở thành `false`. React coi `false` là một "lỗ hổng" trong cây JSX, giống như `null` hoặc `undefined`, và không kết xuất bất cứ thứ gì ở vị trí của nó. <Pitfall> -**Don't put numbers on the left side of `&&`.** +**Không đặt số ở phía bên trái của `&&`.** -To test the condition, JavaScript converts the left side to a boolean automatically. However, if the left side is `0`, then the whole expression gets that value (`0`), and React will happily render `0` rather than nothing. +Để kiểm tra điều kiện, JavaScript tự động chuyển đổi phía bên trái thành boolean. Tuy nhiên, nếu phía bên trái là `0`, thì toàn bộ biểu thức sẽ nhận giá trị đó (`0`) và React sẽ vui vẻ kết xuất `0` thay vì không có gì. -For example, a common mistake is to write code like `messageCount && <p>New messages</p>`. It's easy to assume that it renders nothing when `messageCount` is `0`, but it really renders the `0` itself! +Ví dụ: một lỗi phổ biến là viết code như `messageCount && <p>Tin nhắn mới</p>`. Thật dễ dàng để cho rằng nó không kết xuất gì khi `messageCount` là `0`, nhưng nó thực sự kết xuất chính `0`! -To fix it, make the left side a boolean: `messageCount > 0 && <p>New messages</p>`. +Để khắc phục, hãy làm cho phía bên trái thành boolean: `messageCount > 0 && <p>Tin nhắn mới</p>`. </Pitfall> -### Conditionally assigning JSX to a variable {/*conditionally-assigning-jsx-to-a-variable*/} +### Gán JSX có điều kiện cho một biến {/*conditionally-assigning-jsx-to-a-variable*/} -When the shortcuts get in the way of writing plain code, try using an `if` statement and a variable. You can reassign variables defined with [`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let), so start by providing the default content you want to display, the name: +Khi các phím tắt cản trở việc viết code đơn giản, hãy thử sử dụng câu lệnh `if` và một biến. Bạn có thể gán lại các biến được xác định bằng [`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let), vì vậy hãy bắt đầu bằng cách cung cấp nội dung mặc định bạn muốn hiển thị, tên: ```js let itemContent = name; ``` -Use an `if` statement to reassign a JSX expression to `itemContent` if `isPacked` is `true`: +Sử dụng câu lệnh `if` để gán lại một biểu thức JSX cho `itemContent` nếu `isPacked` là `true`: ```js if (isPacked) { @@ -341,7 +340,7 @@ if (isPacked) { } ``` -[Curly braces open the "window into JavaScript".](/learn/javascript-in-jsx-with-curly-braces#using-curly-braces-a-window-into-the-javascript-world) Embed the variable with curly braces in the returned JSX tree, nesting the previously calculated expression inside of JSX: +[Dấu ngoặc nhọn mở "cửa sổ vào JavaScript".](/learn/javascript-in-jsx-with-curly-braces#using-curly-braces-a-window-into-the-javascript-world) Nhúng biến với dấu ngoặc nhọn trong cây JSX được trả về, lồng biểu thức đã tính trước đó bên trong JSX: ```js <li className="item"> @@ -349,7 +348,7 @@ if (isPacked) { </li> ``` -This style is the most verbose, but it's also the most flexible. Here it is in action: +Kiểu này là dài dòng nhất, nhưng nó cũng linh hoạt nhất. Đây là cách nó hoạt động: <Sandpack> @@ -369,19 +368,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -391,7 +390,7 @@ export default function PackingList() { </Sandpack> -Like before, this works not only for text, but for arbitrary JSX too: +Giống như trước đây, điều này không chỉ hoạt động cho văn bản mà còn cho cả JSX tùy ý: <Sandpack> @@ -415,19 +414,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -437,26 +436,24 @@ export default function PackingList() { </Sandpack> -If you're not familiar with JavaScript, this variety of styles might seem overwhelming at first. However, learning them will help you read and write any JavaScript code -- and not just React components! Pick the one you prefer for a start, and then consult this reference again if you forget how the other ones work. +Nếu bạn không quen thuộc với JavaScript, sự đa dạng của các kiểu này có thể gây choáng ngợp lúc đầu. Tuy nhiên, việc học chúng sẽ giúp bạn đọc và viết bất kỳ code JavaScript nào -- và không chỉ các component React! Hãy chọn một kiểu bạn thích để bắt đầu, và sau đó tham khảo lại tài liệu này nếu bạn quên cách các kiểu khác hoạt động. <Recap> -* In React, you control branching logic with JavaScript. -* You can return a JSX expression conditionally with an `if` statement. -* You can conditionally save some JSX to a variable and then include it inside other JSX by using the curly braces. -* In JSX, `{cond ? <A /> : <B />}` means *"if `cond`, render `<A />`, otherwise `<B />`"*. -* In JSX, `{cond && <A />}` means *"if `cond`, render `<A />`, otherwise nothing"*. -* The shortcuts are common, but you don't have to use them if you prefer plain `if`. +* Trong React, bạn kiểm soát logic phân nhánh bằng JavaScript. +* Bạn có thể trả về một biểu thức JSX có điều kiện bằng câu lệnh `if`. +* Bạn có thể lưu một số JSX vào một biến có điều kiện và sau đó bao gồm nó bên trong JSX khác bằng cách sử dụng dấu ngoặc nhọn. +* Trong JSX, `{cond ? <A /> : <B />}` có nghĩa là *"nếu `cond`, kết xuất `<A />`, nếu không `<B />`"*. +* Trong JSX, `{cond && <A />}` có nghĩa là *"nếu `cond`, kết xuất `<A />`, nếu không không có gì"*. +* Các phím tắt là phổ biến, nhưng bạn không cần phải sử dụng chúng nếu bạn thích `if` đơn giản. </Recap> - - <Challenges> -#### Show an icon for incomplete items with `? :` {/*show-an-icon-for-incomplete-items-with--*/} +#### Hiển thị một biểu tượng cho các mục chưa hoàn thành với `? :` {/*show-an-icon-for-incomplete-items-with--*/} -Use the conditional operator (`cond ? a : b`) to render a ❌ if `isPacked` isn’t `true`. +Sử dụng toán tử điều kiện (`cond ? a : b`) để kết xuất một ❌ nếu `isPacked` không phải là `true`. <Sandpack> @@ -472,19 +469,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -510,19 +507,19 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item isPacked={true} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item isPacked={false} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -534,15 +531,15 @@ export default function PackingList() { </Solution> -#### Show the item importance with `&&` {/*show-the-item-importance-with-*/} +#### Hiển thị tầm quan trọng của mục với `&&` {/*show-the-item-importance-with-*/} -In this example, each `Item` receives a numerical `importance` prop. Use the `&&` operator to render "_(Importance: X)_" in italics, but only for items that have non-zero importance. Your item list should end up looking like this: +Trong ví dụ này, mỗi `Item` nhận một prop `importance` bằng số. Sử dụng toán tử `&&` để kết xuất "_(Tầm quan trọng: X)_" in nghiêng, nhưng chỉ dành cho các mục có tầm quan trọng khác không. Danh sách mục của bạn sẽ trông như thế này: -* Space suit _(Importance: 9)_ -* Helmet with a golden leaf -* Photo of Tam _(Importance: 6)_ +* Bộ đồ vũ trụ _(Tầm quan trọng: 9)_ +* Mũ bảo hiểm với một chiếc lá vàng +* Ảnh của Tam _(Tầm quan trọng: 6)_ -Don't forget to add a space between the two labels! +Đừng quên thêm một khoảng trắng giữa hai nhãn! <Sandpack> @@ -558,19 +555,19 @@ function Item({ name, importance }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item importance={9} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item importance={0} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item importance={6} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -582,7 +579,7 @@ export default function PackingList() { <Solution> -This should do the trick: +Điều này sẽ làm được: <Sandpack> @@ -593,7 +590,7 @@ function Item({ name, importance }) { {name} {importance > 0 && ' '} {importance > 0 && - <i>(Importance: {importance})</i> + <i>(Tầm quan trọng: {importance})</i> } </li> ); @@ -602,19 +599,19 @@ function Item({ name, importance }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item importance={9} - name="Space suit" + name="Bộ đồ vũ trụ" /> <Item importance={0} - name="Helmet with a golden leaf" + name="Mũ bảo hiểm với một chiếc lá vàng" /> <Item importance={6} - name="Photo of Tam" + name="Ảnh của Tam" /> </ul> </section> @@ -624,15 +621,15 @@ export default function PackingList() { </Sandpack> -Note that you must write `importance > 0 && ...` rather than `importance && ...` so that if the `importance` is `0`, `0` isn't rendered as the result! +Lưu ý rằng bạn phải viết `importance > 0 && ...` thay vì `importance && ...` để nếu `importance` là `0`, `0` không được kết xuất làm kết quả! -In this solution, two separate conditions are used to insert a space between the name and the importance label. Alternatively, you could use a Fragment with a leading space: `importance > 0 && <> <i>...</i></>` or add a space immediately inside the `<i>`: `importance > 0 && <i> ...</i>`. +Trong giải pháp này, hai điều kiện riêng biệt được sử dụng để chèn một khoảng trắng giữa tên và nhãn tầm quan trọng. Ngoài ra, bạn có thể sử dụng một Fragment với một khoảng trắng ở đầu: `importance > 0 && <> <i>...</i></>` hoặc thêm một khoảng trắng ngay bên trong `<i>`: `importance > 0 && <i> ...</i>`. </Solution> -#### Refactor a series of `? :` to `if` and variables {/*refactor-a-series-of---to-if-and-variables*/} +#### Tái cấu trúc một loạt `? :` thành `if` và các biến {/*refactor-a-series-of---to-if-and-variables*/} -This `Drink` component uses a series of `? :` conditions to show different information depending on whether the `name` prop is `"tea"` or `"coffee"`. The problem is that the information about each drink is spread across multiple conditions. Refactor this code to use a single `if` statement instead of three `? :` conditions. +Component `Drink` này sử dụng một loạt các điều kiện `? :` để hiển thị thông tin khác nhau tùy thuộc vào việc prop `name` là `"tea"` hay `"coffee"`. Vấn đề là thông tin về mỗi loại đồ uống được trải rộng trên nhiều điều kiện. Tái cấu trúc code này để sử dụng một câu lệnh `if` duy nhất thay vì ba điều kiện `? :`. <Sandpack> @@ -642,12 +639,12 @@ function Drink({ name }) { <section> <h1>{name}</h1> <dl> - <dt>Part of plant</dt> - <dd>{name === 'tea' ? 'leaf' : 'bean'}</dd> - <dt>Caffeine content</dt> - <dd>{name === 'tea' ? '15–70 mg/cup' : '80–185 mg/cup'}</dd> - <dt>Age</dt> - <dd>{name === 'tea' ? '4,000+ years' : '1,000+ years'}</dd> + <dt>Bộ phận của cây</dt> + <dd>{name === 'tea' ? 'lá' : 'hạt'}</dd> + <dt>Hàm lượng caffeine</dt> + <dd>{name === 'tea' ? '15–70 mg/cốc' : '80–185 mg/cốc'}</dd> + <dt>Tuổi</dt> + <dd>{name === 'tea' ? '4.000+ năm' : '1.000+ năm'}</dd> </dl> </section> ); @@ -665,11 +662,11 @@ export default function DrinkList() { </Sandpack> -Once you've refactored the code to use `if`, do you have further ideas on how to simplify it? +Sau khi bạn đã tái cấu trúc code để sử dụng `if`, bạn có ý tưởng nào khác về cách đơn giản hóa nó không? <Solution> -There are multiple ways you could go about this, but here is one starting point: +Có nhiều cách bạn có thể thực hiện điều này, nhưng đây là một điểm khởi đầu: <Sandpack> @@ -677,23 +674,23 @@ There are multiple ways you could go about this, but here is one starting point: function Drink({ name }) { let part, caffeine, age; if (name === 'tea') { - part = 'leaf'; - caffeine = '15–70 mg/cup'; - age = '4,000+ years'; + part = 'lá'; + caffeine = '15–70 mg/cốc'; + age = '4.000+ năm'; } else if (name === 'coffee') { - part = 'bean'; - caffeine = '80–185 mg/cup'; - age = '1,000+ years'; + part = 'hạt'; + caffeine = '80–185 mg/cốc'; + age = '1.000+ năm'; } return ( <section> <h1>{name}</h1> <dl> - <dt>Part of plant</dt> + <dt>Bộ phận của cây</dt> <dd>{part}</dd> - <dt>Caffeine content</dt> + <dt>Hàm lượng caffeine</dt> <dd>{caffeine}</dd> - <dt>Age</dt> + <dt>Tuổi</dt> <dd>{age}</dd> </dl> </section> @@ -712,23 +709,23 @@ export default function DrinkList() { </Sandpack> -Here the information about each drink is grouped together instead of being spread across multiple conditions. This makes it easier to add more drinks in the future. +Ở đây, thông tin về mỗi loại đồ uống được nhóm lại với nhau thay vì trải rộng trên nhiều điều kiện. Điều này giúp bạn dễ dàng thêm nhiều loại đồ uống hơn trong tương lai. -Another solution would be to remove the condition altogether by moving the information into objects: +Một giải pháp khác là loại bỏ hoàn toàn điều kiện bằng cách chuyển thông tin vào các đối tượng: <Sandpack> ```js const drinks = { tea: { - part: 'leaf', - caffeine: '15–70 mg/cup', - age: '4,000+ years' + part: 'lá', + caffeine: '15–70 mg/cốc', + age: '4.000+ năm' }, coffee: { - part: 'bean', - caffeine: '80–185 mg/cup', - age: '1,000+ years' + part: 'hạt', + caffeine: '80–185 mg/cốc', + age: '1.000+ năm' } }; @@ -738,11 +735,11 @@ function Drink({ name }) { <section> <h1>{name}</h1> <dl> - <dt>Part of plant</dt> + <dt>Bộ phận của cây</dt> <dd>{info.part}</dd> - <dt>Caffeine content</dt> + <dt>Hàm lượng caffeine</dt> <dd>{info.caffeine}</dd> - <dt>Age</dt> + <dt>Tuổi</dt> <dd>{info.age}</dd> </dl> </section> diff --git a/src/content/learn/creating-a-react-app.md b/src/content/learn/creating-a-react-app.md index fc6c956d4..ccda7d3e2 100644 --- a/src/content/learn/creating-a-react-app.md +++ b/src/content/learn/creating-a-react-app.md @@ -1,94 +1,93 @@ --- -title: Creating a React App +title: Tạo một ứng dụng React --- <Intro> -If you want to build a new app or website with React, we recommend starting with a framework. +Nếu bạn muốn xây dựng một ứng dụng hoặc trang web mới bằng React, chúng tôi khuyên bạn nên bắt đầu với một framework. </Intro> -If your app has constraints not well-served by existing frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, you can [build a React app from scratch](/learn/build-a-react-app-from-scratch). +Nếu ứng dụng của bạn có những hạn chế mà các framework hiện có không đáp ứng được, bạn thích tự xây dựng framework của riêng mình hoặc bạn chỉ muốn tìm hiểu những điều cơ bản của một ứng dụng React, bạn có thể [xây dựng một ứng dụng React từ đầu](/learn/build-a-react-app-from-scratch). -## Full-stack frameworks {/*full-stack-frameworks*/} +## Các framework Full-stack {/*full-stack-frameworks*/} -These recommended frameworks support all the features you need to deploy and scale your app in production. They have integrated the latest React features and take advantage of React’s architecture. +Các framework được đề xuất này hỗ trợ tất cả các tính năng bạn cần để triển khai và mở rộng ứng dụng của mình trong môi trường production. Chúng đã tích hợp các tính năng React mới nhất và tận dụng kiến trúc của React. <Note> -#### Full-stack frameworks do not require a server. {/*react-frameworks-do-not-require-a-server*/} +#### Các framework Full-stack không yêu cầu máy chủ. {/*react-frameworks-do-not-require-a-server*/} -All the frameworks on this page support client-side rendering ([CSR](https://developer.mozilla.org/en-US/docs/Glossary/CSR)), single-page apps ([SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA)), and static-site generation ([SSG](https://developer.mozilla.org/en-US/docs/Glossary/SSG)). These apps can be deployed to a [CDN](https://developer.mozilla.org/en-US/docs/Glossary/CDN) or static hosting service without a server. Additionally, these frameworks allow you to add server-side rendering on a per-route basis, when it makes sense for your use case. +Tất cả các framework trên trang này đều hỗ trợ hiển thị phía máy khách ([CSR](https://developer.mozilla.org/en-US/docs/Glossary/CSR)), ứng dụng một trang ([SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA)) và tạo trang web tĩnh ([SSG](https://developer.mozilla.org/en-US/docs/Glossary/SSG)). Các ứng dụng này có thể được triển khai tới [CDN](https://developer.mozilla.org/en-US/docs/Glossary/CDN) hoặc dịch vụ lưu trữ tĩnh mà không cần máy chủ. Ngoài ra, các framework này cho phép bạn thêm hiển thị phía máy chủ trên cơ sở từng route, khi nó có ý nghĩa đối với trường hợp sử dụng của bạn. -This allows you to start with a client-only app, and if your needs change later, you can opt-in to using server features on individual routes without rewriting your app. See your framework's documentation for configuring the rendering strategy. +Điều này cho phép bạn bắt đầu với một ứng dụng chỉ dành cho máy khách và nếu nhu cầu của bạn thay đổi sau này, bạn có thể chọn sử dụng các tính năng của máy chủ trên các route riêng lẻ mà không cần viết lại ứng dụng của mình. Xem tài liệu của framework của bạn để biết cách định cấu hình chiến lược hiển thị. </Note> ### Next.js (App Router) {/*nextjs-app-router*/} -**[Next.js's App Router](https://nextjs.org/docs) is a React framework that takes full advantage of React's architecture to enable full-stack React apps.** +**[App Router của Next.js](https://nextjs.org/docs) là một framework React tận dụng tối đa kiến trúc của React để cho phép các ứng dụng React full-stack.** <TerminalBlock> npx create-next-app@latest </TerminalBlock> -Next.js is maintained by [Vercel](https://vercel.com/). You can [deploy a Next.js app](https://nextjs.org/docs/app/building-your-application/deploying) to any Node.js or serverless hosting, or to your own server. Next.js also supports [static export](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) which doesn't require a server. Vercel additionally provides opt-in paid cloud services. +Next.js được duy trì bởi [Vercel](https://vercel.com/). Bạn có thể [triển khai một ứng dụng Next.js](https://nextjs.org/docs/app/building-your-application/deploying) tới bất kỳ Node.js hoặc dịch vụ lưu trữ serverless nào hoặc tới máy chủ của riêng bạn. Next.js cũng hỗ trợ [xuất tĩnh](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) mà không yêu cầu máy chủ. Vercel cũng cung cấp các dịch vụ đám mây trả phí tùy chọn. ### React Router (v7) {/*react-router-v7*/} -**[React Router](https://reactrouter.com/start/framework/installation) is the most popular routing library for React and can be paired with Vite to create a full-stack React framework**. It emphasizes standard Web APIs and has several [ready to deploy templates](https://github.com/remix-run/react-router-templates) for various JavaScript runtimes and platforms. +**[React Router](https://reactrouter.com/start/framework/installation) là thư viện định tuyến phổ biến nhất cho React và có thể được ghép nối với Vite để tạo một framework React full-stack**. Nó nhấn mạnh các API Web tiêu chuẩn và có một số [template sẵn sàng để triển khai](https://github.com/remix-run/react-router-templates) cho các runtime và nền tảng JavaScript khác nhau. -To create a new React Router framework project, run: +Để tạo một dự án framework React Router mới, hãy chạy: <TerminalBlock> npx create-react-router@latest </TerminalBlock> -React Router is maintained by [Shopify](https://www.shopify.com). +React Router được duy trì bởi [Shopify](https://www.shopify.com). -### Expo (for native apps) {/*expo*/} +### Expo (cho ứng dụng native) {/*expo*/} -**[Expo](https://expo.dev/) is a React framework that lets you create universal Android, iOS, and web apps with truly native UIs.** It provides an SDK for [React Native](https://reactnative.dev/) that makes the native parts easier to use. To create a new Expo project, run: +**[Expo](https://expo.dev/) là một framework React cho phép bạn tạo các ứng dụng Android, iOS và web đa năng với giao diện người dùng native thực sự.** Nó cung cấp một SDK cho [React Native](https://reactnative.dev/) giúp các phần native dễ sử dụng hơn. Để tạo một dự án Expo mới, hãy chạy: <TerminalBlock> npx create-expo-app@latest </TerminalBlock> -If you're new to Expo, check out the [Expo tutorial](https://docs.expo.dev/tutorial/introduction/). +Nếu bạn mới làm quen với Expo, hãy xem [hướng dẫn về Expo](https://docs.expo.dev/tutorial/introduction/). -Expo is maintained by [Expo (the company)](https://expo.dev/about). Building apps with Expo is free, and you can submit them to the Google and Apple app stores without restrictions. Expo additionally provides opt-in paid cloud services. +Expo được duy trì bởi [Expo (công ty)](https://expo.dev/about). Xây dựng ứng dụng bằng Expo là miễn phí và bạn có thể gửi chúng lên các cửa hàng ứng dụng của Google và Apple mà không bị hạn chế. Expo cũng cung cấp các dịch vụ đám mây trả phí tùy chọn. +## Các framework khác {/*other-frameworks*/} -## Other frameworks {/*other-frameworks*/} +Có những framework mới nổi khác đang hướng tới tầm nhìn React full-stack của chúng tôi: -There are other up-and-coming frameworks that are working towards our full stack React vision: - -- [TanStack Start (Beta)](https://tanstack.com/): TanStack Start is a full-stack React framework powered by TanStack Router. It provides a full-document SSR, streaming, server functions, bundling, and more using tools like Nitro and Vite. -- [RedwoodJS](https://redwoodjs.com/): Redwood is a full stack React framework with lots of pre-installed packages and configuration that makes it easy to build full-stack web applications. +- [TanStack Start (Beta)](https://tanstack.com/): TanStack Start là một framework React full-stack được hỗ trợ bởi TanStack Router. Nó cung cấp SSR toàn bộ tài liệu, streaming, các hàm server, bundling và hơn thế nữa bằng cách sử dụng các công cụ như Nitro và Vite. +- [RedwoodJS](https://redwoodjs.com/): Redwood là một framework React full-stack với rất nhiều gói và cấu hình được cài đặt sẵn, giúp bạn dễ dàng xây dựng các ứng dụng web full-stack. <DeepDive> -#### Which features make up the React team’s full-stack architecture vision? {/*which-features-make-up-the-react-teams-full-stack-architecture-vision*/} +#### Những tính năng nào tạo nên tầm nhìn kiến trúc full-stack của nhóm React? {/*which-features-make-up-the-react-teams-full-stack-architecture-vision*/} -Next.js's App Router bundler fully implements the official [React Server Components specification](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md). This lets you mix build-time, server-only, and interactive components in a single React tree. +Bundler App Router của Next.js triển khai đầy đủ [đặc tả React Server Components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md) chính thức. Điều này cho phép bạn kết hợp các thành phần build-time, chỉ dành cho server và tương tác trong một cây React duy nhất. -For example, you can write a server-only React component as an `async` function that reads from a database or from a file. Then you can pass data down from it to your interactive components: +Ví dụ: bạn có thể viết một thành phần React chỉ dành cho server dưới dạng một hàm `async` đọc từ cơ sở dữ liệu hoặc từ một tệp. Sau đó, bạn có thể truyền dữ liệu từ đó xuống các thành phần tương tác của mình: ```js -// This component runs *only* on the server (or during the build). +// Thành phần này chỉ chạy trên server (hoặc trong quá trình build). async function Talks({ confId }) { - // 1. You're on the server, so you can talk to your data layer. API endpoint not required. + // 1. Bạn đang ở trên server, vì vậy bạn có thể nói chuyện với lớp dữ liệu của mình. Không yêu cầu API endpoint. const talks = await db.Talks.findAll({ confId }); - // 2. Add any amount of rendering logic. It won't make your JavaScript bundle larger. + // 2. Thêm bất kỳ lượng logic hiển thị nào. Nó sẽ không làm cho bundle JavaScript của bạn lớn hơn. const videos = talks.map(talk => talk.video); - // 3. Pass the data down to the components that will run in the browser. + // 3. Truyền dữ liệu xuống các thành phần sẽ chạy trong trình duyệt. return <SearchableVideoList videos={videos} />; } ``` -Next.js's App Router also integrates [data fetching with Suspense](/blog/2022/03/29/react-v18#suspense-in-data-frameworks). This lets you specify a loading state (like a skeleton placeholder) for different parts of your user interface directly in your React tree: +App Router của Next.js cũng tích hợp [tìm nạp dữ liệu với Suspense](/blog/2022/03/29/react-v18#suspense-in-data-frameworks). Điều này cho phép bạn chỉ định trạng thái tải (như một placeholder skeleton) cho các phần khác nhau của giao diện người dùng của bạn trực tiếp trong cây React của bạn: ```js <Suspense fallback={<TalksLoading />}> @@ -96,18 +95,18 @@ Next.js's App Router also integrates [data fetching with Suspense](/blog/2022/03 </Suspense> ``` -Server Components and Suspense are React features rather than Next.js features. However, adopting them at the framework level requires buy-in and non-trivial implementation work. At the moment, the Next.js App Router is the most complete implementation. The React team is working with bundler developers to make these features easier to implement in the next generation of frameworks. +Server Components và Suspense là các tính năng của React chứ không phải là tính năng của Next.js. Tuy nhiên, việc áp dụng chúng ở cấp độ framework đòi hỏi sự chấp thuận và công việc triển khai không hề nhỏ. Hiện tại, Next.js App Router là triển khai hoàn chỉnh nhất. Nhóm React đang làm việc với các nhà phát triển bundler để giúp các tính năng này dễ triển khai hơn trong thế hệ framework tiếp theo. </DeepDive> -## Start From Scratch {/*start-from-scratch*/} +## Bắt đầu từ đầu {/*start-from-scratch*/} -If your app has constraints not well-served by existing frameworks, you prefer to build your own framework, or you just want to learn the basics of a React app, there are other options available for starting a React project from scratch. +Nếu ứng dụng của bạn có những hạn chế mà các framework hiện có không đáp ứng được, bạn thích tự xây dựng framework của riêng mình hoặc bạn chỉ muốn tìm hiểu những điều cơ bản của một ứng dụng React, thì có những tùy chọn khác để bắt đầu một dự án React từ đầu. -Starting from scratch gives you more flexibility, but does require that you make choices on which tools to use for routing, data fetching, and other common usage patterns. It's a lot like building your own framework, instead of using a framework that already exists. The [frameworks we recommend](#full-stack-frameworks) have built-in solutions for these problems. +Bắt đầu từ đầu mang lại cho bạn sự linh hoạt hơn, nhưng đòi hỏi bạn phải đưa ra lựa chọn về công cụ nào sẽ sử dụng cho định tuyến, tìm nạp dữ liệu và các mẫu sử dụng phổ biến khác. Nó giống như việc xây dựng framework của riêng bạn hơn là sử dụng một framework đã tồn tại. Các [framework mà chúng tôi khuyên dùng](#full-stack-frameworks) có các giải pháp tích hợp cho những vấn đề này. -If you want to build your own solutions, see our guide to [build a React app from Scratch](/learn/build-a-react-app-from-scratch) for instructions on how to set up a new React project starting with a built tool like [Vite](https://vite.dev/), [Parcel](https://parceljs.org/), or [RSbuild](https://rsbuild.dev/). +Nếu bạn muốn xây dựng các giải pháp của riêng mình, hãy xem hướng dẫn của chúng tôi về [xây dựng một ứng dụng React từ đầu](/learn/build-a-react-app-from-scratch) để biết hướng dẫn về cách thiết lập một dự án React mới bắt đầu với một công cụ build như [Vite](https://vite.dev/), [Parcel](https://parceljs.org/) hoặc [RSbuild](https://rsbuild.dev/). ----- -_If you’re a framework author interested in being included on this page, [please let us know](https://github.com/reactjs/react.dev/issues/new?assignees=&labels=type%3A+framework&projects=&template=3-framework.yml&title=%5BFramework%5D%3A+)._ +_Nếu bạn là tác giả của một framework và muốn được đưa vào trang này, [vui lòng cho chúng tôi biết](https://github.com/reactjs/react.dev/issues/new?assignees=&labels=type%3A+framework&projects=&template=3-framework.yml&title=%5BFramework%5D%3A+)._ diff --git a/src/content/learn/describing-the-ui.md b/src/content/learn/describing-the-ui.md index 34ee0c01a..13445cd5d 100644 --- a/src/content/learn/describing-the-ui.md +++ b/src/content/learn/describing-the-ui.md @@ -1,30 +1,30 @@ --- -title: Describing the UI +title: Mô tả giao diện người dùng --- <Intro> -React is a JavaScript library for rendering user interfaces (UI). UI is built from small units like buttons, text, and images. React lets you combine them into reusable, nestable *components.* From web sites to phone apps, everything on the screen can be broken down into components. In this chapter, you'll learn to create, customize, and conditionally display React components. +React là một thư viện JavaScript để hiển thị giao diện người dùng (UI). UI được xây dựng từ các đơn vị nhỏ như nút, văn bản và hình ảnh. React cho phép bạn kết hợp chúng thành các *component* có thể tái sử dụng và lồng vào nhau. Từ các trang web đến ứng dụng điện thoại, mọi thứ trên màn hình đều có thể được chia thành các component. Trong chương này, bạn sẽ học cách tạo, tùy chỉnh và hiển thị có điều kiện các component React. </Intro> <YouWillLearn isChapter={true}> -* [How to write your first React component](/learn/your-first-component) -* [When and how to create multi-component files](/learn/importing-and-exporting-components) -* [How to add markup to JavaScript with JSX](/learn/writing-markup-with-jsx) -* [How to use curly braces with JSX to access JavaScript functionality from your components](/learn/javascript-in-jsx-with-curly-braces) -* [How to configure components with props](/learn/passing-props-to-a-component) -* [How to conditionally render components](/learn/conditional-rendering) -* [How to render multiple components at a time](/learn/rendering-lists) -* [How to avoid confusing bugs by keeping components pure](/learn/keeping-components-pure) -* [Why understanding your UI as trees is useful](/learn/understanding-your-ui-as-a-tree) +* [Cách viết component React đầu tiên của bạn](/learn/your-first-component) +* [Khi nào và làm thế nào để tạo các tệp nhiều component](/learn/importing-and-exporting-components) +* [Cách thêm markup vào JavaScript bằng JSX](/learn/writing-markup-with-jsx) +* [Cách sử dụng dấu ngoặc nhọn với JSX để truy cập chức năng JavaScript từ các component của bạn](/learn/javascript-in-jsx-with-curly-braces) +* [Cách định cấu hình component với props](/learn/passing-props-to-a-component) +* [Cách hiển thị có điều kiện các component](/learn/conditional-rendering) +* [Cách hiển thị nhiều component cùng một lúc](/learn/rendering-lists) +* [Cách tránh các lỗi khó hiểu bằng cách giữ cho các component thuần khiết](/learn/keeping-components-pure) +* [Tại sao việc hiểu UI của bạn dưới dạng cây lại hữu ích](/learn/understanding-your-ui-as-a-tree) </YouWillLearn> -## Your first component {/*your-first-component*/} +## Component đầu tiên của bạn {/*your-first-component*/} -React applications are built from isolated pieces of UI called *components*. A React component is a JavaScript function that you can sprinkle with markup. Components can be as small as a button, or as large as an entire page. Here is a `Gallery` component rendering three `Profile` components: +Các ứng dụng React được xây dựng từ các phần UI riêng biệt được gọi là *component*. Một component React là một hàm JavaScript mà bạn có thể thêm markup vào. Các component có thể nhỏ như một nút hoặc lớn như toàn bộ trang. Dưới đây là một component `Gallery` hiển thị ba component `Profile`: <Sandpack> @@ -41,7 +41,7 @@ function Profile() { export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -58,14 +58,13 @@ img { margin: 0 10px 10px 0; height: 90px; } <LearnMore path="/learn/your-first-component"> -Read **[Your First Component](/learn/your-first-component)** to learn how to declare and use React components. +Đọc **[Component Đầu Tiên Của Bạn](/learn/your-first-component)** để tìm hiểu cách khai báo và sử dụng các component React. </LearnMore> -## Importing and exporting components {/*importing-and-exporting-components*/} - -You can declare many components in one file, but large files can get difficult to navigate. To solve this, you can *export* a component into its own file, and then *import* that component from another file: +## Nhập và xuất component {/*importing-and-exporting-components*/} +Bạn có thể khai báo nhiều component trong một tệp, nhưng các tệp lớn có thể gây khó khăn cho việc điều hướng. Để giải quyết vấn đề này, bạn có thể *xuất* một component vào tệp riêng của nó, sau đó *nhập* component đó từ một tệp khác: <Sandpack> @@ -85,7 +84,7 @@ import Profile from './Profile.js'; export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -113,22 +112,22 @@ img { margin: 0 10px 10px 0; } <LearnMore path="/learn/importing-and-exporting-components"> -Read **[Importing and Exporting Components](/learn/importing-and-exporting-components)** to learn how to split components into their own files. +Đọc **[Nhập và Xuất Component](/learn/importing-and-exporting-components)** để tìm hiểu cách chia các component thành các tệp riêng của chúng. </LearnMore> -## Writing markup with JSX {/*writing-markup-with-jsx*/} +## Viết markup với JSX {/*writing-markup-with-jsx*/} -Each React component is a JavaScript function that may contain some markup that React renders into the browser. React components use a syntax extension called JSX to represent that markup. JSX looks a lot like HTML, but it is a bit stricter and can display dynamic information. +Mỗi component React là một hàm JavaScript có thể chứa một số markup mà React hiển thị trong trình duyệt. Các component React sử dụng một phần mở rộng cú pháp gọi là JSX để biểu diễn markup đó. JSX trông rất giống HTML, nhưng nó chặt chẽ hơn một chút và có thể hiển thị thông tin động. -If we paste existing HTML markup into a React component, it won't always work: +Nếu chúng ta dán markup HTML hiện có vào một component React, nó sẽ không phải lúc nào cũng hoạt động: <Sandpack> ```js export default function TodoList() { return ( - // This doesn't quite work! + // Điều này không hoàn toàn hoạt động! <h1>Hedy Lamarr's Todos</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" @@ -150,7 +149,7 @@ img { height: 90px; } </Sandpack> -If you have existing HTML like this, you can fix it using a [converter](https://transform.tools/html-to-jsx): +Nếu bạn có HTML hiện có như thế này, bạn có thể sửa nó bằng [converter](https://transform.tools/html-to-jsx): <Sandpack> @@ -182,13 +181,13 @@ img { height: 90px; } <LearnMore path="/learn/writing-markup-with-jsx"> -Read **[Writing Markup with JSX](/learn/writing-markup-with-jsx)** to learn how to write valid JSX. +Đọc **[Viết Markup với JSX](/learn/writing-markup-with-jsx)** để tìm hiểu cách viết JSX hợp lệ. </LearnMore> -## JavaScript in JSX with curly braces {/*javascript-in-jsx-with-curly-braces*/} +## JavaScript trong JSX với dấu ngoặc nhọn {/*javascript-in-jsx-with-curly-braces*/} -JSX lets you write HTML-like markup inside a JavaScript file, keeping rendering logic and content in the same place. Sometimes you will want to add a little JavaScript logic or reference a dynamic property inside that markup. In this situation, you can use curly braces in your JSX to "open a window" to JavaScript: +JSX cho phép bạn viết markup giống HTML bên trong tệp JavaScript, giữ cho logic hiển thị và nội dung ở cùng một nơi. Đôi khi bạn sẽ muốn thêm một chút logic JavaScript hoặc tham chiếu một thuộc tính động bên trong markup đó. Trong tình huống này, bạn có thể sử dụng dấu ngoặc nhọn trong JSX của mình để "mở một cửa sổ" cho JavaScript: <Sandpack> @@ -230,13 +229,13 @@ body > div > div { padding: 20px; } <LearnMore path="/learn/javascript-in-jsx-with-curly-braces"> -Read **[JavaScript in JSX with Curly Braces](/learn/javascript-in-jsx-with-curly-braces)** to learn how to access JavaScript data from JSX. +Đọc **[JavaScript trong JSX với Dấu Ngoặc Nhọn](/learn/javascript-in-jsx-with-curly-braces)** để tìm hiểu cách truy cập dữ liệu JavaScript từ JSX. </LearnMore> -## Passing props to a component {/*passing-props-to-a-component*/} +## Truyền props cho một component {/*passing-props-to-a-component*/} -React components use *props* to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, functions, and even JSX! +Các component React sử dụng *props* để giao tiếp với nhau. Mỗi component cha có thể truyền một số thông tin cho các component con của nó bằng cách cung cấp cho chúng các props. Props có thể nhắc bạn về các thuộc tính HTML, nhưng bạn có thể truyền bất kỳ giá trị JavaScript nào thông qua chúng, bao gồm các đối tượng, mảng, hàm và thậm chí cả JSX! <Sandpack> @@ -311,15 +310,15 @@ export function getImageUrl(person, size = 's') { <LearnMore path="/learn/passing-props-to-a-component"> -Read **[Passing Props to a Component](/learn/passing-props-to-a-component)** to learn how to pass and read props. +Đọc **[Truyền Props cho một Component](/learn/passing-props-to-a-component)** để tìm hiểu cách truyền và đọc props. </LearnMore> -## Conditional rendering {/*conditional-rendering*/} +## Hiển thị có điều kiện {/*conditional-rendering*/} -Your components will often need to display different things depending on different conditions. In React, you can conditionally render JSX using JavaScript syntax like `if` statements, `&&`, and `? :` operators. +Các component của bạn thường sẽ cần hiển thị những thứ khác nhau tùy thuộc vào các điều kiện khác nhau. Trong React, bạn có thể hiển thị JSX có điều kiện bằng cú pháp JavaScript như câu lệnh `if`, `&&` và toán tử `? :`. -In this example, the JavaScript `&&` operator is used to conditionally render a checkmark: +Trong ví dụ này, toán tử `&&` của JavaScript được sử dụng để hiển thị có điều kiện một dấu kiểm: <Sandpack> @@ -335,7 +334,7 @@ function Item({ name, isPacked }) { export default function PackingList() { return ( <section> - <h1>Sally Ride's Packing List</h1> + <h1>Danh sách đóng gói của Sally Ride</h1> <ul> <Item isPacked={true} @@ -359,15 +358,15 @@ export default function PackingList() { <LearnMore path="/learn/conditional-rendering"> -Read **[Conditional Rendering](/learn/conditional-rendering)** to learn the different ways to render content conditionally. +Đọc **[Hiển Thị Có Điều Kiện](/learn/conditional-rendering)** để tìm hiểu các cách khác nhau để hiển thị nội dung có điều kiện. </LearnMore> -## Rendering lists {/*rendering-lists*/} +## Hiển thị danh sách {/*rendering-lists*/} -You will often want to display multiple similar components from a collection of data. You can use JavaScript's `filter()` and `map()` with React to filter and transform your array of data into an array of components. +Bạn thường sẽ muốn hiển thị nhiều component tương tự từ một tập hợp dữ liệu. Bạn có thể sử dụng `filter()` và `map()` của JavaScript với React để lọc và chuyển đổi mảng dữ liệu của bạn thành một mảng các component. -For each array item, you will need to specify a `key`. Usually, you will want to use an ID from the database as a `key`. Keys let React keep track of each item's place in the list even if the list changes. +Đối với mỗi mục trong mảng, bạn sẽ cần chỉ định một `key`. Thông thường, bạn sẽ muốn sử dụng ID từ cơ sở dữ liệu làm `key`. Các key cho phép React theo dõi vị trí của từng mục trong danh sách ngay cả khi danh sách thay đổi. <Sandpack> @@ -459,18 +458,18 @@ h2 { font-size: 20px; } <LearnMore path="/learn/rendering-lists"> -Read **[Rendering Lists](/learn/rendering-lists)** to learn how to render a list of components, and how to choose a key. +Đọc **[Hiển Thị Danh Sách](/learn/rendering-lists)** để tìm hiểu cách hiển thị danh sách các component và cách chọn key. </LearnMore> -## Keeping components pure {/*keeping-components-pure*/} +## Giữ cho các component thuần khiết {/*keeping-components-pure*/} -Some JavaScript functions are *pure.* A pure function: +Một số hàm JavaScript là *thuần khiết*. Một hàm thuần khiết: -* **Minds its own business.** It does not change any objects or variables that existed before it was called. -* **Same inputs, same output.** Given the same inputs, a pure function should always return the same result. +* **Không can thiệp vào việc riêng.** Nó không thay đổi bất kỳ đối tượng hoặc biến nào đã tồn tại trước khi nó được gọi. +* **Đầu vào giống nhau, đầu ra giống nhau.** Với cùng một đầu vào, một hàm thuần khiết sẽ luôn trả về cùng một kết quả. -By strictly only writing your components as pure functions, you can avoid an entire class of baffling bugs and unpredictable behavior as your codebase grows. Here is an example of an impure component: +Bằng cách chỉ viết các component của bạn dưới dạng các hàm thuần khiết, bạn có thể tránh được toàn bộ một lớp các lỗi khó hiểu và hành vi khó lường khi cơ sở mã của bạn phát triển. Dưới đây là một ví dụ về một component không thuần khiết: <Sandpack> @@ -496,7 +495,7 @@ export default function TeaSet() { </Sandpack> -You can make this component pure by passing a prop instead of modifying a preexisting variable: +Bạn có thể làm cho component này thuần khiết bằng cách truyền một prop thay vì sửa đổi một biến đã tồn tại: <Sandpack> @@ -520,43 +519,42 @@ export default function TeaSet() { <LearnMore path="/learn/keeping-components-pure"> -Read **[Keeping Components Pure](/learn/keeping-components-pure)** to learn how to write components as pure, predictable functions. +Đọc **[Giữ Cho Các Component Thuần Khiết](/learn/keeping-components-pure)** để tìm hiểu cách viết các component dưới dạng các hàm thuần khiết, có thể dự đoán được. </LearnMore> -## Your UI as a tree {/*your-ui-as-a-tree*/} +## UI của bạn dưới dạng một cây {/*your-ui-as-a-tree*/} -React uses trees to model the relationships between components and modules. +React sử dụng cây để mô hình hóa các mối quan hệ giữa các component và module. -A React render tree is a representation of the parent and child relationship between components. +Một cây render React là một biểu diễn của mối quan hệ cha con giữa các component. <Diagram name="generic_render_tree" height={250} width={500} alt="A tree graph with five nodes, with each node representing a component. The root node is located at the top the tree graph and is labelled 'Root Component'. It has two arrows extending down to two nodes labelled 'Component A' and 'Component C'. Each of the arrows is labelled with 'renders'. 'Component A' has a single 'renders' arrow to a node labelled 'Component B'. 'Component C' has a single 'renders' arrow to a node labelled 'Component D'."> -An example React render tree. +Một ví dụ về cây render React. </Diagram> -Components near the top of the tree, near the root component, are considered top-level components. Components with no child components are leaf components. This categorization of components is useful for understanding data flow and rendering performance. +Các component gần đầu cây, gần component gốc, được coi là các component cấp cao nhất. Các component không có component con là các component lá. Việc phân loại các component này rất hữu ích để hiểu luồng dữ liệu và hiệu suất hiển thị. -Modelling the relationship between JavaScript modules is another useful way to understand your app. We refer to it as a module dependency tree. +Mô hình hóa mối quan hệ giữa các module JavaScript là một cách hữu ích khác để hiểu ứng dụng của bạn. Chúng ta gọi nó là cây phụ thuộc module. <Diagram name="generic_dependency_tree" height={250} width={500} alt="A tree graph with five nodes. Each node represents a JavaScript module. The top-most node is labelled 'RootModule.js'. It has three arrows extending to the nodes: 'ModuleA.js', 'ModuleB.js', and 'ModuleC.js'. Each arrow is labelled as 'imports'. 'ModuleC.js' node has a single 'imports' arrow that points to a node labelled 'ModuleD.js'."> -An example module dependency tree. +Một ví dụ về cây phụ thuộc module. </Diagram> -A dependency tree is often used by build tools to bundle all the relevant JavaScript code for the client to download and render. A large bundle size regresses user experience for React apps. Understanding the module dependency tree is helpful to debug such issues. +Một cây phụ thuộc thường được sử dụng bởi các công cụ xây dựng để đóng gói tất cả mã JavaScript có liên quan để máy khách tải xuống và hiển thị. Kích thước bundle lớn làm giảm trải nghiệm người dùng cho các ứng dụng React. Hiểu cây phụ thuộc module là hữu ích để gỡ lỗi các vấn đề như vậy. <LearnMore path="/learn/understanding-your-ui-as-a-tree"> -Read **[Your UI as a Tree](/learn/understanding-your-ui-as-a-tree)** to learn how to create a render and module dependency trees for a React app and how they're useful mental models for improving user experience and performance. +Đọc **[UI Của Bạn Dưới Dạng Một Cây](/learn/understanding-your-ui-as-a-tree)** để tìm hiểu cách tạo cây render và cây phụ thuộc module cho một ứng dụng React và cách chúng là các mô hình tinh thần hữu ích để cải thiện trải nghiệm và hiệu suất người dùng. </LearnMore> +## Tiếp theo là gì? {/*whats-next*/} -## What's next? {/*whats-next*/} - -Head over to [Your First Component](/learn/your-first-component) to start reading this chapter page by page! +Đi tới [Component Đầu Tiên Của Bạn](/learn/your-first-component) để bắt đầu đọc chương này từng trang một! -Or, if you're already familiar with these topics, why not read about [Adding Interactivity](/learn/adding-interactivity)? +Hoặc, nếu bạn đã quen thuộc với các chủ đề này, tại sao không đọc về [Thêm Tương Tác](/learn/adding-interactivity)? diff --git a/src/content/learn/escape-hatches.md b/src/content/learn/escape-hatches.md index 23f11f54e..7cc9ddffd 100644 --- a/src/content/learn/escape-hatches.md +++ b/src/content/learn/escape-hatches.md @@ -1,35 +1,35 @@ --- -title: Escape Hatches +title: Lối Thoát Hiểm --- <Intro> -Some of your components may need to control and synchronize with systems outside of React. For example, you might need to focus an input using the browser API, play and pause a video player implemented without React, or connect and listen to messages from a remote server. In this chapter, you'll learn the escape hatches that let you "step outside" React and connect to external systems. Most of your application logic and data flow should not rely on these features. +Một số component của bạn có thể cần điều khiển và đồng bộ hóa với các hệ thống bên ngoài React. Ví dụ: bạn có thể cần tập trung vào một input bằng API của trình duyệt, phát và tạm dừng trình phát video được triển khai mà không cần React hoặc kết nối và lắng nghe tin nhắn từ một máy chủ từ xa. Trong chương này, bạn sẽ tìm hiểu các lối thoát hiểm cho phép bạn "bước ra ngoài" React và kết nối với các hệ thống bên ngoài. Hầu hết logic ứng dụng và luồng dữ liệu của bạn không nên dựa vào các tính năng này. </Intro> <YouWillLearn isChapter={true}> -* [How to "remember" information without re-rendering](/learn/referencing-values-with-refs) -* [How to access DOM elements managed by React](/learn/manipulating-the-dom-with-refs) -* [How to synchronize components with external systems](/learn/synchronizing-with-effects) -* [How to remove unnecessary Effects from your components](/learn/you-might-not-need-an-effect) -* [How an Effect's lifecycle is different from a component's](/learn/lifecycle-of-reactive-effects) -* [How to prevent some values from re-triggering Effects](/learn/separating-events-from-effects) -* [How to make your Effect re-run less often](/learn/removing-effect-dependencies) -* [How to share logic between components](/learn/reusing-logic-with-custom-hooks) +* [Cách "ghi nhớ" thông tin mà không cần render lại](/learn/referencing-values-with-refs) +* [Cách truy cập các phần tử DOM được quản lý bởi React](/learn/manipulating-the-dom-with-refs) +* [Cách đồng bộ hóa các component với các hệ thống bên ngoài](/learn/synchronizing-with-effects) +* [Cách loại bỏ các Effect không cần thiết khỏi component của bạn](/learn/you-might-not-need-an-effect) +* [Vòng đời của một Effect khác với vòng đời của một component như thế nào](/learn/lifecycle-of-reactive-effects) +* [Cách ngăn một số giá trị kích hoạt lại Effect](/learn/separating-events-from-effects) +* [Cách làm cho Effect của bạn chạy lại ít thường xuyên hơn](/learn/removing-effect-dependencies) +* [Cách chia sẻ logic giữa các component](/learn/reusing-logic-with-custom-hooks) </YouWillLearn> -## Referencing values with refs {/*referencing-values-with-refs*/} +## Tham chiếu các giá trị bằng ref {/*referencing-values-with-refs*/} -When you want a component to "remember" some information, but you don't want that information to [trigger new renders](/learn/render-and-commit), you can use a *ref*: +Khi bạn muốn một component "ghi nhớ" một số thông tin, nhưng bạn không muốn thông tin đó [kích hoạt các lần render mới](/learn/render-and-commit), bạn có thể sử dụng *ref*: ```js const ref = useRef(0); ``` -Like state, refs are retained by React between re-renders. However, setting state re-renders a component. Changing a ref does not! You can access the current value of that ref through the `ref.current` property. +Giống như state, ref được React giữ lại giữa các lần re-render. Tuy nhiên, việc đặt state sẽ re-render một component. Thay đổi một ref thì không! Bạn có thể truy cập giá trị hiện tại của ref đó thông qua thuộc tính `ref.current`. <Sandpack> @@ -41,12 +41,12 @@ export default function Counter() { function handleClick() { ref.current = ref.current + 1; - alert('You clicked ' + ref.current + ' times!'); + alert('Bạn đã nhấp ' + ref.current + ' lần!'); } return ( <button onClick={handleClick}> - Click me! + Nhấp vào tôi! </button> ); } @@ -54,17 +54,17 @@ export default function Counter() { </Sandpack> -A ref is like a secret pocket of your component that React doesn't track. For example, you can use refs to store [timeout IDs](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#return_value), [DOM elements](https://developer.mozilla.org/en-US/docs/Web/API/Element), and other objects that don't impact the component's rendering output. +Một ref giống như một túi bí mật của component mà React không theo dõi. Ví dụ: bạn có thể sử dụng ref để lưu trữ [ID timeout](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#return_value), [các phần tử DOM](https://developer.mozilla.org/en-US/docs/Web/API/Element) và các đối tượng khác không ảnh hưởng đến đầu ra render của component. <LearnMore path="/learn/referencing-values-with-refs"> -Read **[Referencing Values with Refs](/learn/referencing-values-with-refs)** to learn how to use refs to remember information. +Đọc **[Tham chiếu các giá trị bằng Ref](/learn/referencing-values-with-refs)** để tìm hiểu cách sử dụng ref để ghi nhớ thông tin. </LearnMore> -## Manipulating the DOM with refs {/*manipulating-the-dom-with-refs*/} +## Thao tác với DOM bằng ref {/*manipulating-the-dom-with-refs*/} -React automatically updates the DOM to match your render output, so your components won't often need to manipulate it. However, sometimes you might need access to the DOM elements managed by React—for example, to focus a node, scroll to it, or measure its size and position. There is no built-in way to do those things in React, so you will need a ref to the DOM node. For example, clicking the button will focus the input using a ref: +React tự động cập nhật DOM để khớp với đầu ra render của bạn, vì vậy các component của bạn sẽ không thường xuyên cần thao tác với nó. Tuy nhiên, đôi khi bạn có thể cần truy cập vào các phần tử DOM được quản lý bởi React—ví dụ: để tập trung một node, cuộn đến nó hoặc đo kích thước và vị trí của nó. Không có cách tích hợp sẵn để thực hiện những việc đó trong React, vì vậy bạn sẽ cần một ref đến DOM node. Ví dụ: nhấp vào nút sẽ tập trung vào input bằng một ref: <Sandpack> @@ -82,7 +82,7 @@ export default function Form() { <> <input ref={inputRef} /> <button onClick={handleClick}> - Focus the input + Tập trung vào input </button> </> ); @@ -93,15 +93,15 @@ export default function Form() { <LearnMore path="/learn/manipulating-the-dom-with-refs"> -Read **[Manipulating the DOM with Refs](/learn/manipulating-the-dom-with-refs)** to learn how to access DOM elements managed by React. +Đọc **[Thao tác với DOM bằng Ref](/learn/manipulating-the-dom-with-refs)** để tìm hiểu cách truy cập các phần tử DOM được quản lý bởi React. </LearnMore> -## Synchronizing with Effects {/*synchronizing-with-effects*/} +## Đồng bộ hóa với Effect {/*synchronizing-with-effects*/} -Some components need to synchronize with external systems. For example, you might want to control a non-React component based on the React state, set up a server connection, or send an analytics log when a component appears on the screen. Unlike event handlers, which let you handle particular events, *Effects* let you run some code after rendering. Use them to synchronize your component with a system outside of React. +Một số component cần đồng bộ hóa với các hệ thống bên ngoài. Ví dụ: bạn có thể muốn điều khiển một component không phải React dựa trên state của React, thiết lập kết nối máy chủ hoặc gửi nhật ký phân tích khi một component xuất hiện trên màn hình. Không giống như các trình xử lý sự kiện, cho phép bạn xử lý các sự kiện cụ thể, *Effect* cho phép bạn chạy một số code sau khi render. Sử dụng chúng để đồng bộ hóa component của bạn với một hệ thống bên ngoài React. -Press Play/Pause a few times and see how the video player stays synchronized to the `isPlaying` prop value: +Nhấn Play/Pause một vài lần và xem cách trình phát video vẫn được đồng bộ hóa với giá trị prop `isPlaying`: <Sandpack> @@ -127,7 +127,7 @@ export default function App() { return ( <> <button onClick={() => setIsPlaying(!isPlaying)}> - {isPlaying ? 'Pause' : 'Play'} + {isPlaying ? 'Tạm dừng' : 'Phát'} </button> <VideoPlayer isPlaying={isPlaying} @@ -145,7 +145,7 @@ video { width: 250px; } </Sandpack> -Many Effects also "clean up" after themselves. For example, an Effect that sets up a connection to a chat server should return a *cleanup function* that tells React how to disconnect your component from that server: +Nhiều Effect cũng "dọn dẹp" sau khi chúng chạy. Ví dụ: một Effect thiết lập kết nối với máy chủ trò chuyện sẽ trả về một *hàm dọn dẹp* cho React biết cách ngắt kết nối component của bạn khỏi máy chủ đó: <Sandpack> @@ -159,19 +159,19 @@ export default function ChatRoom() { connection.connect(); return () => connection.disconnect(); }, []); - return <h1>Welcome to the chat!</h1>; + return <h1>Chào mừng đến với phòng chat!</h1>; } ``` ```js src/chat.js export function createConnection() { - // A real implementation would actually connect to the server + // Một triển khai thực tế sẽ thực sự kết nối với máy chủ return { connect() { - console.log('✅ Connecting...'); + console.log('✅ Đang kết nối...'); }, disconnect() { - console.log('❌ Disconnected.'); + console.log('❌ Đã ngắt kết nối.'); } }; } @@ -183,30 +183,30 @@ input { display: block; margin-bottom: 20px; } </Sandpack> -In development, React will immediately run and clean up your Effect one extra time. This is why you see `"✅ Connecting..."` printed twice. This ensures that you don't forget to implement the cleanup function. +Trong quá trình phát triển, React sẽ ngay lập tức chạy và dọn dẹp Effect của bạn thêm một lần nữa. Đây là lý do tại sao bạn thấy `"✅ Đang kết nối..."` được in hai lần. Điều này đảm bảo rằng bạn không quên triển khai hàm dọn dẹp. <LearnMore path="/learn/synchronizing-with-effects"> -Read **[Synchronizing with Effects](/learn/synchronizing-with-effects)** to learn how to synchronize components with external systems. +Đọc **[Đồng bộ hóa với Effect](/learn/synchronizing-with-effects)** để tìm hiểu cách đồng bộ hóa các component với các hệ thống bên ngoài. </LearnMore> -## You Might Not Need An Effect {/*you-might-not-need-an-effect*/} +## Bạn có thể không cần Effect {/*you-might-not-need-an-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. 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 hiểm khỏi mô hình 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 số hệ thống bên ngoài. 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 một component khi một số prop hoặc state thay đổi), bạn sẽ không cần Effect. Việc loại bỏ các Effect không cần thiết sẽ giúp code của bạn dễ theo dõi hơn, chạy nhanh hơn và ít bị lỗi hơn. -There are two common cases in which you don't need Effects: -- **You don't need Effects to transform data for rendering.** -- **You don't need Effects to handle user events.** +Có hai trường hợp phổ biến mà bạn không cần Effect: +- **Bạn không cần Effect để chuyển đổi dữ liệu để render.** +- **Bạn không cần Effect để xử lý các sự kiện của người dùng.** -For example, you don't need an Effect to adjust some state based on other state: +Ví dụ: bạn không cần Effect để điều chỉnh một số state dựa trên state khác: ```js {5-9} function Form() { const [firstName, setFirstName] = useState('Taylor'); const [lastName, setLastName] = useState('Swift'); - // 🔴 Avoid: redundant state and unnecessary Effect + // 🔴 Tránh: state dư thừa và Effect không cần thiết const [fullName, setFullName] = useState(''); useEffect(() => { setFullName(firstName + ' ' + lastName); @@ -215,31 +215,31 @@ function Form() { } ``` -Instead, calculate as much as you can while rendering: +Thay vào đó, hãy tính toán càng nhiều càng tốt trong khi render: ```js {4-5} function Form() { const [firstName, setFirstName] = useState('Taylor'); const [lastName, setLastName] = useState('Swift'); - // ✅ Good: calculated during rendering + // ✅ Tốt: được tính toán trong khi render const fullName = firstName + ' ' + lastName; // ... } ``` -However, you *do* need Effects to synchronize with external systems. +Tuy nhiên, bạn *cần* Effect để đồng bộ hóa với các hệ thống bên ngoài. <LearnMore path="/learn/you-might-not-need-an-effect"> -Read **[You Might Not Need an Effect](/learn/you-might-not-need-an-effect)** to learn how to remove unnecessary Effects. +Đọc **[Bạn có thể không cần Effect](/learn/you-might-not-need-an-effect)** để tìm hiểu cách loại bỏ các Effect không cần thiết. </LearnMore> -## Lifecycle of reactive effects {/*lifecycle-of-reactive-effects*/} +## Vòng đời của các effect phản ứng {/*lifecycle-of-reactive-effects*/} -Effects have a different lifecycle from components. Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it. This cycle can happen multiple times if your Effect depends on props and state that change over time. +Effect có vòng đời khác với component. Component có thể mount, update hoặc unmount. Một Effect chỉ có thể làm hai việc: bắt đầu đồng bộ hóa một cái gì đó và sau đó dừng đồng bộ hóa nó. Chu kỳ này có thể xảy ra nhiều lần nếu Effect của bạn phụ thuộc vào các prop và state thay đổi theo thời gian. -This Effect depends on the value of the `roomId` prop. Props are *reactive values,* which means they can change on a re-render. Notice that the Effect *re-synchronizes* (and re-connects to the server) if `roomId` changes: +Effect này phụ thuộc vào giá trị của prop `roomId`. Prop là *các giá trị phản ứng*, có nghĩa là chúng có thể thay đổi khi re-render. Lưu ý rằng Effect *tái đồng bộ hóa* (và kết nối lại với máy chủ) nếu `roomId` thay đổi: <Sandpack> @@ -256,7 +256,7 @@ function ChatRoom({ roomId }) { return () => connection.disconnect(); }, [roomId]); - return <h1>Welcome to the {roomId} room!</h1>; + return <h1>Chào mừng đến với phòng {roomId}!</h1>; } export default function App() { @@ -264,7 +264,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -283,13 +283,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một triển khai thực tế sẽ thực sự kết nối với máy chủ return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -302,25 +302,25 @@ button { margin-left: 10px; } </Sandpack> -React provides a linter rule to check that you've specified your Effect's dependencies correctly. If you forget to specify `roomId` in the list of dependencies in the above example, the linter will find that bug automatically. +React cung cấp một quy tắc linter để kiểm tra xem bạn đã chỉ định các dependency của Effect một cách chính xác hay chưa. Nếu bạn quên chỉ định `roomId` trong danh sách các dependency trong ví dụ trên, linter sẽ tự động tìm thấy lỗi đó. <LearnMore path="/learn/lifecycle-of-reactive-effects"> -Read **[Lifecycle of Reactive Events](/learn/lifecycle-of-reactive-effects)** to learn how an Effect's lifecycle is different from a component's. +Đọc **[Vòng đời của các sự kiện phản ứng](/learn/lifecycle-of-reactive-effects)** để tìm hiểu vòng đời của một Effect khác với vòng đời của một component như thế nào. </LearnMore> -## Separating events from Effects {/*separating-events-from-effects*/} +## Tách các sự kiện khỏi Effect {/*separating-events-from-effects*/} <Wip> -This section describes an **experimental API that has not yet been released** in a stable version of React. +Phần này mô tả một **API thử nghiệm chưa được phát hành** trong phiên bản ổn định của React. </Wip> -Event handlers only re-run when you perform the same interaction again. Unlike event handlers, Effects re-synchronize if any of the values they read, like props or state, are different than during last render. Sometimes, you want a mix of both behaviors: an Effect that re-runs in response to some values but not others. +Trình xử lý sự kiện chỉ chạy lại khi bạn thực hiện lại cùng một tương tác. Không giống như trình xử lý sự kiện, Effect tái đồng bộ hóa nếu bất kỳ giá trị nào chúng đọc, như prop hoặc state, khác với lần render cuối cùng. Đôi khi, bạn muốn kết hợp cả hai hành vi: một Effect chạy lại để đáp ứng với một số giá trị nhưng không phải các giá trị khác. -All code inside Effects is *reactive.* It will run again if some reactive value it reads has changed due to a re-render. For example, this Effect will re-connect to the chat if either `roomId` or `theme` have changed: +Tất cả code bên trong Effect đều *phản ứng*. Nó sẽ chạy lại nếu một số giá trị phản ứng mà nó đọc đã thay đổi do re-render. Ví dụ: Effect này sẽ kết nối lại với chat nếu `roomId` hoặc `theme` đã thay đổi: <Sandpack> @@ -352,13 +352,13 @@ function ChatRoom({ roomId, theme }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { - showNotification('Connected!', theme); + showNotification('Đã kết nối!', theme); }); connection.connect(); return () => connection.disconnect(); }, [roomId, theme]); - return <h1>Welcome to the {roomId} room!</h1> + return <h1>Chào mừng đến với phòng {roomId}!</h1> } export default function App() { @@ -367,7 +367,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -383,7 +383,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Use dark theme + Sử dụng giao diện tối </label> <hr /> <ChatRoom @@ -397,7 +397,7 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một triển khai thực tế sẽ thực sự kết nối với máy chủ let connectedCallback; let timeout; return { @@ -410,10 +410,10 @@ export function createConnection(serverUrl, roomId) { }, on(event, callback) { if (connectedCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Không thể thêm trình xử lý hai lần.'); } if (event !== 'connected') { - throw Error('Only "connected" event is supported.'); + throw Error('Chỉ hỗ trợ sự kiện "connected".'); } connectedCallback = callback; }, @@ -448,7 +448,7 @@ label { display: block; margin-top: 10px; } </Sandpack> -This is not ideal. You want to re-connect to the chat only if the `roomId` has changed. Switching the `theme` shouldn't re-connect to the chat! Move the code reading `theme` out of your Effect into an *Effect Event*: +Điều này không lý tưởng. Bạn chỉ muốn kết nối lại với chat nếu `roomId` đã thay đổi. Việc chuyển đổi `theme` không nên kết nối lại với chat! Di chuyển code đọc `theme` ra khỏi Effect của bạn vào một *Effect Event*: <Sandpack> @@ -479,7 +479,7 @@ const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId, theme }) { const onConnected = useEffectEvent(() => { - showNotification('Connected!', theme); + showNotification('Đã kết nối!', theme); }); useEffect(() => { @@ -491,7 +491,7 @@ function ChatRoom({ roomId, theme }) { return () => connection.disconnect(); }, [roomId]); - return <h1>Welcome to the {roomId} room!</h1> + return <h1>Chào mừng đến với phòng {roomId}!</h1> } export default function App() { @@ -500,7 +500,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -516,7 +516,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Use dark theme + Sử dụng giao diện tối </label> <hr /> <ChatRoom @@ -530,7 +530,7 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một triển khai thực tế sẽ thực sự kết nối với máy chủ let connectedCallback; let timeout; return { @@ -543,10 +543,10 @@ export function createConnection(serverUrl, roomId) { }, on(event, callback) { if (connectedCallback) { - throw Error('Cannot add the handler twice.'); + throw Error('Không thể thêm trình xử lý hai lần.'); } if (event !== 'connected') { - throw Error('Only "connected" event is supported.'); + throw Error('Chỉ hỗ trợ sự kiện "connected".'); } connectedCallback = callback; }, @@ -581,19 +581,19 @@ label { display: block; margin-top: 10px; } </Sandpack> -Code inside Effect Events isn't reactive, so changing the `theme` no longer makes your Effect re-connect. +Code bên trong Effect Event không phản ứng, vì vậy việc thay đổi `theme` không còn khiến Effect của bạn kết nối lại. <LearnMore path="/learn/separating-events-from-effects"> -Read **[Separating Events from Effects](/learn/separating-events-from-effects)** to learn how to prevent some values from re-triggering Effects. +Đọc **[Tách các sự kiện khỏi Effect](/learn/separating-events-from-effects)** để tìm hiểu cách ngăn một số giá trị kích hoạt lại Effect. </LearnMore> -## Removing Effect dependencies {/*removing-effect-dependencies*/} +## Loại bỏ các dependency của Effect {/*removing-effect-dependencies*/} -When you write an Effect, the linter will verify that you've included every reactive value (like props and state) that the Effect reads in the list of your Effect's dependencies. This ensures that your Effect remains synchronized with the latest props and state of your component. Unnecessary dependencies may cause your Effect to run too often, or even create an infinite loop. The way you remove them depends on the case. +Khi bạn viết một Effect, linter sẽ xác minh rằng bạn đã bao gồm mọi giá trị phản ứng (như prop và state) mà Effect đọc trong danh sách các dependency của Effect. Điều này đảm bảo rằng Effect của bạn vẫn được đồng bộ hóa với các prop và state mới nhất của component của bạn. Các dependency không cần thiết có thể khiến Effect của bạn chạy quá thường xuyên hoặc thậm chí tạo ra một vòng lặp vô hạn. Cách bạn loại bỏ chúng phụ thuộc vào trường hợp. -For example, this Effect depends on the `options` object which gets re-created every time you edit the input: +Ví dụ: Effect này phụ thuộc vào đối tượng `options` được tạo lại mỗi khi bạn chỉnh sửa input: <Sandpack> @@ -619,7 +619,7 @@ function ChatRoom({ roomId }) { return ( <> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến với phòng {roomId}!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> </> ); @@ -630,7 +630,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -649,13 +649,13 @@ export default function App() { ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Một triển khai thực tế sẽ thực sự kết nối với máy chủ return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -668,7 +668,7 @@ button { margin-left: 10px; } </Sandpack> -You don't want the chat to re-connect every time you start typing a message in that chat. To fix this problem, move creation of the `options` object inside the Effect so that the Effect only depends on the `roomId` string: +Bạn không muốn chat kết nối lại mỗi khi bạn bắt đầu nhập tin nhắn vào chat đó. Để khắc phục sự cố này, hãy di chuyển việc tạo đối tượng `options` vào bên trong Effect để Effect chỉ phụ thuộc vào chuỗi `roomId`: <Sandpack> @@ -693,7 +693,7 @@ function ChatRoom({ roomId }) { return ( <> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến với phòng {roomId}!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> </> ); @@ -704,7 +704,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -723,13 +723,13 @@ export default function App() { ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Một triển khai thực tế sẽ thực sự kết nối với máy chủ return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -742,19 +742,19 @@ button { margin-left: 10px; } </Sandpack> -Notice that you didn't start by editing the dependency list to remove the `options` dependency. That would be wrong. Instead, you changed the surrounding code so that the dependency became *unnecessary.* Think of the dependency list as a list of all the reactive values used by your Effect's code. You don't intentionally choose what to put on that list. The list describes your code. To change the dependency list, change the code. +Lưu ý rằng bạn không bắt đầu bằng cách chỉnh sửa danh sách dependency để loại bỏ dependency `options`. Điều đó sẽ là sai. Thay vào đó, bạn đã thay đổi code xung quanh để dependency trở nên *không cần thiết*. Hãy nghĩ về danh sách dependency như một danh sách tất cả các giá trị phản ứng được sử dụng bởi code Effect của bạn. Bạn không cố ý chọn những gì để đưa vào danh sách đó. Danh sách mô tả code của bạn. Để thay đổi danh sách dependency, hãy thay đổi code. <LearnMore path="/learn/removing-effect-dependencies"> -Read **[Removing Effect Dependencies](/learn/removing-effect-dependencies)** to learn how to make your Effect re-run less often. +Đọc **[Loại bỏ các dependency của Effect](/learn/removing-effect-dependencies)** để tìm hiểu cách làm cho Effect của bạn chạy lại ít thường xuyên hơn. </LearnMore> -## Reusing logic with custom Hooks {/*reusing-logic-with-custom-hooks*/} +## Sử dụng lại logic với Hook tùy chỉnh {/*reusing-logic-with-custom-hooks*/} -React comes with built-in Hooks like `useState`, `useContext`, and `useEffect`. Sometimes, you’ll wish that there was a Hook for some more specific purpose: for example, to fetch data, to keep track of whether the user is online, or to connect to a chat room. To do this, you can create your own Hooks for your application's needs. +React đi kèm với các Hook tích hợp sẵn như `useState`, `useContext` và `useEffect`. Đôi khi, bạn sẽ ước có một Hook cho một mục đích cụ thể hơn: ví dụ: để tìm nạp dữ liệu, để theo dõi xem người dùng có trực tuyến hay không hoặc để kết nối với phòng chat. Để thực hiện việc này, bạn có thể tạo Hook của riêng mình cho nhu cầu của ứng dụng. -In this example, the `usePointerPosition` custom Hook tracks the cursor position, while `useDelayedValue` custom Hook returns a value that's "lagging behind" the value you passed by a certain number of milliseconds. Move the cursor over the sandbox preview area to see a moving trail of dots following the cursor: +Trong ví dụ này, Hook tùy chỉnh `usePointerPosition` theo dõi vị trí con trỏ, trong khi Hook tùy chỉnh `useDelayedValue` trả về một giá trị "chậm hơn" giá trị bạn đã truyền một số mili giây nhất định. Di chuyển con trỏ qua khu vực xem trước của sandbox để xem một vệt chấm chuyển động theo con trỏ: <Sandpack> @@ -835,14 +835,14 @@ body { min-height: 300px; } </Sandpack> -You can create custom Hooks, compose them together, pass data between them, and reuse them between components. As your app grows, you will write fewer Effects by hand because you'll be able to reuse custom Hooks you already wrote. There are also many excellent custom Hooks maintained by the React community. +Bạn có thể tạo Hook tùy chỉnh, kết hợp chúng với nhau, truyền dữ liệu giữa chúng và sử dụng lại chúng giữa các component. Khi ứng dụng của bạn phát triển, bạn sẽ viết ít Effect thủ công hơn vì bạn sẽ có thể sử dụng lại các Hook tùy chỉnh mà bạn đã viết. Ngoài ra còn có nhiều Hook tùy chỉnh tuyệt vời được duy trì bởi cộng đồng React. <LearnMore path="/learn/reusing-logic-with-custom-hooks"> -Read **[Reusing Logic with Custom Hooks](/learn/reusing-logic-with-custom-hooks)** to learn how to share logic between components. +Đọc **[Sử dụng lại logic với Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks)** để tìm hiểu cách chia sẻ logic giữa các component. </LearnMore> -## What's next? {/*whats-next*/} +## Tiếp theo là gì? {/*whats-next*/} -Head over to [Referencing Values with Refs](/learn/referencing-values-with-refs) to start reading this chapter page by page! +Hãy chuyển đến [Tham chiếu các giá trị bằng Ref](/learn/referencing-values-with-refs) để bắt đầu đọc trang này theo từng trang! diff --git a/src/content/learn/importing-and-exporting-components.md b/src/content/learn/importing-and-exporting-components.md index b458ef402..9324f2062 100644 --- a/src/content/learn/importing-and-exporting-components.md +++ b/src/content/learn/importing-and-exporting-components.md @@ -1,26 +1,26 @@ --- -title: Importing and Exporting Components +title: Nhập và Xuất Component --- <Intro> -The magic of components lies in their reusability: you can create components that are composed of other components. But as you nest more and more components, it often makes sense to start splitting them into different files. This lets you keep your files easy to scan and reuse components in more places. +Điều kỳ diệu của component nằm ở khả năng tái sử dụng của chúng: bạn có thể tạo các component được cấu thành từ các component khác. Nhưng khi bạn lồng ngày càng nhiều component, việc bắt đầu chia chúng thành các file khác nhau thường có ý nghĩa. Điều này cho phép bạn giữ cho các file của mình dễ quét và tái sử dụng các component ở nhiều nơi hơn. </Intro> <YouWillLearn> -* What a root component file is -* How to import and export a component -* When to use default and named imports and exports -* How to import and export multiple components from one file -* How to split components into multiple files +* File component gốc là gì +* Cách nhập và xuất một component +* Khi nào nên sử dụng nhập và xuất mặc định và có tên +* Cách nhập và xuất nhiều component từ một file +* Cách chia component thành nhiều file </YouWillLearn> -## The root component file {/*the-root-component-file*/} +## File component gốc {/*the-root-component-file*/} -In [Your First Component](/learn/your-first-component), you made a `Profile` component and a `Gallery` component that renders it: +Trong [Component Đầu Tiên Của Bạn](/learn/your-first-component), bạn đã tạo một component `Profile` và một component `Gallery` hiển thị nó: <Sandpack> @@ -37,7 +37,7 @@ function Profile() { export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -52,17 +52,17 @@ img { margin: 0 10px 10px 0; height: 90px; } </Sandpack> -These currently live in a **root component file,** named `App.js` in this example. Depending on your setup, your root component could be in another file, though. If you use a framework with file-based routing, such as Next.js, your root component will be different for every page. +Chúng hiện đang nằm trong một **file component gốc,** có tên là `App.js` trong ví dụ này. Tuy nhiên, tùy thuộc vào thiết lập của bạn, component gốc của bạn có thể nằm trong một file khác. Nếu bạn sử dụng một framework có định tuyến dựa trên file, chẳng hạn như Next.js, component gốc của bạn sẽ khác nhau đối với mỗi trang. -## Exporting and importing a component {/*exporting-and-importing-a-component*/} +## Xuất và nhập một component {/*exporting-and-importing-a-component*/} -What if you want to change the landing screen in the future and put a list of science books there? Or place all the profiles somewhere else? It makes sense to move `Gallery` and `Profile` out of the root component file. This will make them more modular and reusable in other files. You can move a component in three steps: +Điều gì sẽ xảy ra nếu bạn muốn thay đổi màn hình đích trong tương lai và đặt một danh sách các cuốn sách khoa học ở đó? Hoặc đặt tất cả các hồ sơ ở một nơi khác? Việc di chuyển `Gallery` và `Profile` ra khỏi file component gốc có ý nghĩa. Điều này sẽ làm cho chúng trở nên mô-đun hơn và có thể tái sử dụng ở các file khác. Bạn có thể di chuyển một component trong ba bước: -1. **Make** a new JS file to put the components in. -2. **Export** your function component from that file (using either [default](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_the_default_export) or [named](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_named_exports) exports). -3. **Import** it in the file where you’ll use the component (using the corresponding technique for importing [default](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#importing_defaults) or [named](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#import_a_single_export_from_a_module) exports). +1. **Tạo** một file JS mới để đặt các component vào. +2. **Xuất** function component của bạn từ file đó (sử dụng [xuất mặc định](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_the_default_export) hoặc [xuất có tên](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/export#using_named_exports)). +3. **Nhập** nó trong file nơi bạn sẽ sử dụng component (sử dụng kỹ thuật tương ứng để nhập [mặc định](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#importing_defaults) hoặc [có tên](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#import_a_single_export_from_a_module)). -Here both `Profile` and `Gallery` have been moved out of `App.js` into a new file called `Gallery.js`. Now you can change `App.js` to import `Gallery` from `Gallery.js`: +Ở đây cả `Profile` và `Gallery` đã được di chuyển ra khỏi `App.js` vào một file mới có tên là `Gallery.js`. Bây giờ bạn có thể thay đổi `App.js` để nhập `Gallery` từ `Gallery.js`: <Sandpack> @@ -89,7 +89,7 @@ function Profile() { export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -104,60 +104,60 @@ img { margin: 0 10px 10px 0; height: 90px; } </Sandpack> -Notice how this example is broken down into two component files now: +Lưu ý cách ví dụ này được chia thành hai file component bây giờ: 1. `Gallery.js`: - - Defines the `Profile` component which is only used within the same file and is not exported. - - Exports the `Gallery` component as a **default export.** + - Định nghĩa component `Profile` chỉ được sử dụng trong cùng một file và không được xuất. + - Xuất component `Gallery` dưới dạng **xuất mặc định.** 2. `App.js`: - - Imports `Gallery` as a **default import** from `Gallery.js`. - - Exports the root `App` component as a **default export.** + - Nhập `Gallery` dưới dạng **nhập mặc định** từ `Gallery.js`. + - Xuất component `App` gốc dưới dạng **xuất mặc định.** <Note> -You may encounter files that leave off the `.js` file extension like so: +Bạn có thể gặp các file bỏ qua phần mở rộng file `.js` như sau: ```js import Gallery from './Gallery'; ``` -Either `'./Gallery.js'` or `'./Gallery'` will work with React, though the former is closer to how [native ES Modules](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules) work. +`'./Gallery.js'` hoặc `'./Gallery'` sẽ hoạt động với React, mặc dù cách đầu tiên gần hơn với cách [ES Modules gốc](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules) hoạt động. </Note> <DeepDive> -#### Default vs named exports {/*default-vs-named-exports*/} +#### Xuất mặc định so với xuất có tên {/*default-vs-named-exports*/} -There are two primary ways to export values with JavaScript: default exports and named exports. So far, our examples have only used default exports. But you can use one or both of them in the same file. **A file can have no more than one _default_ export, but it can have as many _named_ exports as you like.** +Có hai cách chính để xuất các giá trị bằng JavaScript: xuất mặc định và xuất có tên. Cho đến nay, các ví dụ của chúng ta chỉ sử dụng xuất mặc định. Nhưng bạn có thể sử dụng một hoặc cả hai trong cùng một file. **Một file không thể có nhiều hơn một xuất _mặc định_, nhưng nó có thể có bao nhiêu xuất _có tên_ tùy thích.** - + -How you export your component dictates how you must import it. You will get an error if you try to import a default export the same way you would a named export! This chart can help you keep track: +Cách bạn xuất component của mình sẽ quyết định cách bạn phải nhập nó. Bạn sẽ gặp lỗi nếu bạn cố gắng nhập một xuất mặc định giống như cách bạn nhập một xuất có tên! Biểu đồ này có thể giúp bạn theo dõi: -| Syntax | Export statement | Import statement | +| Cú pháp | Câu lệnh xuất | Câu lệnh nhập | | ----------- | ----------- | ----------- | -| Default | `export default function Button() {}` | `import Button from './Button.js';` | -| Named | `export function Button() {}` | `import { Button } from './Button.js';` | +| Mặc định | `export default function Button() {}` | `import Button from './Button.js';` | +| Có tên | `export function Button() {}` | `import { Button } from './Button.js';` | -When you write a _default_ import, you can put any name you want after `import`. For example, you could write `import Banana from './Button.js'` instead and it would still provide you with the same default export. In contrast, with named imports, the name has to match on both sides. That's why they are called _named_ imports! +Khi bạn viết một nhập _mặc định_, bạn có thể đặt bất kỳ tên nào bạn muốn sau `import`. Ví dụ: bạn có thể viết `import Banana from './Button.js'` và nó vẫn sẽ cung cấp cho bạn cùng một xuất mặc định. Ngược lại, với nhập có tên, tên phải khớp ở cả hai phía. Đó là lý do tại sao chúng được gọi là nhập _có tên_! -**People often use default exports if the file exports only one component, and use named exports if it exports multiple components and values.** Regardless of which coding style you prefer, always give meaningful names to your component functions and the files that contain them. Components without names, like `export default () => {}`, are discouraged because they make debugging harder. +**Mọi người thường sử dụng xuất mặc định nếu file chỉ xuất một component và sử dụng xuất có tên nếu nó xuất nhiều component và giá trị.** Bất kể bạn thích kiểu mã hóa nào, hãy luôn đặt tên có ý nghĩa cho các function component của bạn và các file chứa chúng. Các component không có tên, như `export default () => {}`, không được khuyến khích vì chúng gây khó khăn hơn cho việc gỡ lỗi. </DeepDive> -## Exporting and importing multiple components from the same file {/*exporting-and-importing-multiple-components-from-the-same-file*/} +## Xuất và nhập nhiều component từ cùng một file {/*exporting-and-importing-multiple-components-from-the-same-file*/} -What if you want to show just one `Profile` instead of a gallery? You can export the `Profile` component, too. But `Gallery.js` already has a *default* export, and you can't have _two_ default exports. You could create a new file with a default export, or you could add a *named* export for `Profile`. **A file can only have one default export, but it can have numerous named exports!** +Điều gì sẽ xảy ra nếu bạn muốn hiển thị chỉ một `Profile` thay vì một gallery? Bạn cũng có thể xuất component `Profile`. Nhưng `Gallery.js` đã có một xuất *mặc định* và bạn không thể có _hai_ xuất mặc định. Bạn có thể tạo một file mới với một xuất mặc định hoặc bạn có thể thêm một xuất *có tên* cho `Profile`. **Một file chỉ có thể có một xuất mặc định, nhưng nó có thể có nhiều xuất có tên!** <Note> -To reduce the potential confusion between default and named exports, some teams choose to only stick to one style (default or named), or avoid mixing them in a single file. Do what works best for you! +Để giảm sự nhầm lẫn tiềm ẩn giữa xuất mặc định và có tên, một số nhóm chọn chỉ tuân theo một kiểu (mặc định hoặc có tên) hoặc tránh trộn chúng trong một file. Hãy làm những gì phù hợp nhất với bạn! </Note> -First, **export** `Profile` from `Gallery.js` using a named export (no `default` keyword): +Đầu tiên, **xuất** `Profile` từ `Gallery.js` bằng cách sử dụng xuất có tên (không có từ khóa `default`): ```js export function Profile() { @@ -165,13 +165,13 @@ export function Profile() { } ``` -Then, **import** `Profile` from `Gallery.js` to `App.js` using a named import (with the curly braces): +Sau đó, **nhập** `Profile` từ `Gallery.js` vào `App.js` bằng cách sử dụng nhập có tên (với dấu ngoặc nhọn): ```js import { Profile } from './Gallery.js'; ``` -Finally, **render** `<Profile />` from the `App` component: +Cuối cùng, **hiển thị** `<Profile />` từ component `App`: ```js export default function App() { @@ -179,7 +179,7 @@ export default function App() { } ``` -Now `Gallery.js` contains two exports: a default `Gallery` export, and a named `Profile` export. `App.js` imports both of them. Try editing `<Profile />` to `<Gallery />` and back in this example: +Bây giờ `Gallery.js` chứa hai xuất: một xuất `Gallery` mặc định và một xuất `Profile` có tên. `App.js` nhập cả hai. Hãy thử chỉnh sửa `<Profile />` thành `<Gallery />` và ngược lại trong ví dụ này: <Sandpack> @@ -207,7 +207,7 @@ export function Profile() { export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -222,24 +222,24 @@ img { margin: 0 10px 10px 0; height: 90px; } </Sandpack> -Now you're using a mix of default and named exports: +Bây giờ bạn đang sử dụng kết hợp xuất mặc định và có tên: * `Gallery.js`: - - Exports the `Profile` component as a **named export called `Profile`.** - - Exports the `Gallery` component as a **default export.** + - Xuất component `Profile` dưới dạng **xuất có tên có tên là `Profile`.** + - Xuất component `Gallery` dưới dạng **xuất mặc định.** * `App.js`: - - Imports `Profile` as a **named import called `Profile`** from `Gallery.js`. - - Imports `Gallery` as a **default import** from `Gallery.js`. - - Exports the root `App` component as a **default export.** + - Nhập `Profile` dưới dạng **nhập có tên có tên là `Profile`** từ `Gallery.js`. + - Nhập `Gallery` dưới dạng **nhập mặc định** từ `Gallery.js`. + - Xuất component `App` gốc dưới dạng **xuất mặc định.** <Recap> -On this page you learned: +Trên trang này, bạn đã học: -* What a root component file is -* How to import and export a component -* When and how to use default and named imports and exports -* How to export multiple components from the same file +* File component gốc là gì +* Cách nhập và xuất một component +* Khi nào và cách sử dụng nhập và xuất mặc định và có tên +* Cách xuất nhiều component từ cùng một file </Recap> @@ -247,22 +247,22 @@ On this page you learned: <Challenges> -#### Split the components further {/*split-the-components-further*/} +#### Chia nhỏ các component hơn nữa {/*split-the-components-further*/} -Currently, `Gallery.js` exports both `Profile` and `Gallery`, which is a bit confusing. +Hiện tại, `Gallery.js` xuất cả `Profile` và `Gallery`, điều này hơi khó hiểu. -Move the `Profile` component to its own `Profile.js`, and then change the `App` component to render both `<Profile />` and `<Gallery />` one after another. +Di chuyển component `Profile` sang `Profile.js` riêng của nó, sau đó thay đổi component `App` để hiển thị cả `<Profile />` và `<Gallery />` liên tiếp. -You may use either a default or a named export for `Profile`, but make sure that you use the corresponding import syntax in both `App.js` and `Gallery.js`! You can refer to the table from the deep dive above: +Bạn có thể sử dụng xuất mặc định hoặc xuất có tên cho `Profile`, nhưng hãy đảm bảo rằng bạn sử dụng cú pháp nhập tương ứng trong cả `App.js` và `Gallery.js`! Bạn có thể tham khảo bảng từ phần tìm hiểu sâu ở trên: -| Syntax | Export statement | Import statement | +| Cú pháp | Câu lệnh xuất | Câu lệnh nhập | | ----------- | ----------- | ----------- | -| Default | `export default function Button() {}` | `import Button from './Button.js';` | -| Named | `export function Button() {}` | `import { Button } from './Button.js';` | +| Mặc định | `export default function Button() {}` | `import Button from './Button.js';` | +| Có tên | `export function Button() {}` | `import { Button } from './Button.js';` | <Hint> -Don't forget to import your components where they are called. Doesn't `Gallery` use `Profile`, too? +Đừng quên nhập các component của bạn nơi chúng được gọi. `Gallery` không sử dụng `Profile` sao? </Hint> @@ -295,7 +295,7 @@ export function Profile() { export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -313,11 +313,11 @@ img { margin: 0 10px 10px 0; height: 90px; } </Sandpack> -After you get it working with one kind of exports, make it work with the other kind. +Sau khi bạn làm cho nó hoạt động với một loại xuất, hãy làm cho nó hoạt động với loại còn lại. <Solution> -This is the solution with named exports: +Đây là giải pháp với xuất có tên: <Sandpack> @@ -341,7 +341,7 @@ import { Profile } from './Profile.js'; export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> @@ -367,7 +367,7 @@ img { margin: 0 10px 10px 0; height: 90px; } </Sandpack> -This is the solution with default exports: +Đây là giải pháp với xuất mặc định: <Sandpack> @@ -391,7 +391,7 @@ import Profile from './Profile.js'; export default function Gallery() { return ( <section> - <h1>Amazing scientists</h1> + <h1>Các nhà khoa học tuyệt vời</h1> <Profile /> <Profile /> <Profile /> diff --git a/src/content/learn/javascript-in-jsx-with-curly-braces.md b/src/content/learn/javascript-in-jsx-with-curly-braces.md index 736065b03..7746771e0 100644 --- a/src/content/learn/javascript-in-jsx-with-curly-braces.md +++ b/src/content/learn/javascript-in-jsx-with-curly-braces.md @@ -1,25 +1,25 @@ --- -title: JavaScript in JSX with Curly Braces +title: JavaScript trong JSX với Dấu ngoặc nhọn --- <Intro> -JSX lets you write HTML-like markup inside a JavaScript file, keeping rendering logic and content in the same place. Sometimes you will want to add a little JavaScript logic or reference a dynamic property inside that markup. In this situation, you can use curly braces in your JSX to open a window to JavaScript. +JSX cho phép bạn viết mã đánh dấu giống HTML bên trong tệp JavaScript, giữ logic hiển thị và nội dung ở cùng một nơi. Đôi khi bạn sẽ muốn thêm một chút logic JavaScript hoặc tham chiếu một thuộc tính động bên trong mã đánh dấu đó. Trong tình huống này, bạn có thể sử dụng dấu ngoặc nhọn trong JSX để mở ra một "cánh cửa" vào JavaScript. </Intro> <YouWillLearn> -* How to pass strings with quotes -* How to reference a JavaScript variable inside JSX with curly braces -* How to call a JavaScript function inside JSX with curly braces -* How to use a JavaScript object inside JSX with curly braces +* Cách truyền chuỗi với dấu ngoặc kép +* Cách tham chiếu một biến JavaScript bên trong JSX với dấu ngoặc nhọn +* Cách gọi một hàm JavaScript bên trong JSX với dấu ngoặc nhọn +* Cách sử dụng một đối tượng JavaScript bên trong JSX với dấu ngoặc nhọn </YouWillLearn> -## Passing strings with quotes {/*passing-strings-with-quotes*/} +## Truyền chuỗi với dấu ngoặc kép {/*passing-strings-with-quotes*/} -When you want to pass a string attribute to JSX, you put it in single or double quotes: +Khi bạn muốn truyền một thuộc tính chuỗi cho JSX, bạn đặt nó trong dấu ngoặc đơn hoặc dấu ngoặc kép: <Sandpack> @@ -41,9 +41,9 @@ export default function Avatar() { </Sandpack> -Here, `"https://i.imgur.com/7vQD0fPs.jpg"` and `"Gregorio Y. Zara"` are being passed as strings. +Ở đây, `"https://i.imgur.com/7vQD0fPs.jpg"` và `"Gregorio Y. Zara"` đang được truyền dưới dạng chuỗi. -But what if you want to dynamically specify the `src` or `alt` text? You could **use a value from JavaScript by replacing `"` and `"` with `{` and `}`**: +Nhưng điều gì sẽ xảy ra nếu bạn muốn chỉ định động văn bản `src` hoặc `alt`? Bạn có thể **sử dụng một giá trị từ JavaScript bằng cách thay thế `"` và `"` bằng `{` và `}`**: <Sandpack> @@ -67,11 +67,11 @@ export default function Avatar() { </Sandpack> -Notice the difference between `className="avatar"`, which specifies an `"avatar"` CSS class name that makes the image round, and `src={avatar}` that reads the value of the JavaScript variable called `avatar`. That's because curly braces let you work with JavaScript right there in your markup! +Lưu ý sự khác biệt giữa `className="avatar"`, chỉ định một tên lớp CSS `"avatar"` để làm cho hình ảnh tròn và `src={avatar}` đọc giá trị của biến JavaScript có tên là `avatar`. Đó là bởi vì dấu ngoặc nhọn cho phép bạn làm việc với JavaScript ngay tại đó trong mã đánh dấu của bạn! -## Using curly braces: A window into the JavaScript world {/*using-curly-braces-a-window-into-the-javascript-world*/} +## Sử dụng dấu ngoặc nhọn: Một "cánh cửa" vào thế giới JavaScript {/*using-curly-braces-a-window-into-the-javascript-world*/} -JSX is a special way of writing JavaScript. That means it’s possible to use JavaScript inside it—with curly braces `{ }`. The example below first declares a name for the scientist, `name`, then embeds it with curly braces inside the `<h1>`: +JSX là một cách viết JavaScript đặc biệt. Điều đó có nghĩa là có thể sử dụng JavaScript bên trong nó—với dấu ngoặc nhọn `{ }`. Ví dụ dưới đây trước tiên khai báo một tên cho nhà khoa học, `name`, sau đó nhúng nó với dấu ngoặc nhọn bên trong thẻ `<h1>`: <Sandpack> @@ -86,9 +86,9 @@ export default function TodoList() { </Sandpack> -Try changing the `name`'s value from `'Gregorio Y. Zara'` to `'Hedy Lamarr'`. See how the list title changes? +Hãy thử thay đổi giá trị của `name` từ `'Gregorio Y. Zara'` thành `'Hedy Lamarr'`. Bạn có thấy tiêu đề danh sách thay đổi không? -Any JavaScript expression will work between curly braces, including function calls like `formatDate()`: +Bất kỳ biểu thức JavaScript nào cũng sẽ hoạt động giữa dấu ngoặc nhọn, bao gồm cả các lệnh gọi hàm như `formatDate()`: <Sandpack> @@ -111,18 +111,18 @@ export default function TodoList() { </Sandpack> -### Where to use curly braces {/*where-to-use-curly-braces*/} +### Nơi sử dụng dấu ngoặc nhọn {/*where-to-use-curly-braces*/} -You can only use curly braces in two ways inside JSX: +Bạn chỉ có thể sử dụng dấu ngoặc nhọn theo hai cách bên trong JSX: -1. **As text** directly inside a JSX tag: `<h1>{name}'s To Do List</h1>` works, but `<{tag}>Gregorio Y. Zara's To Do List</{tag}>` will not. -2. **As attributes** immediately following the `=` sign: `src={avatar}` will read the `avatar` variable, but `src="{avatar}"` will pass the string `"{avatar}"`. +1. **Dưới dạng văn bản** trực tiếp bên trong một thẻ JSX: `<h1>{name}'s To Do List</h1>` hoạt động, nhưng `<{tag}>Gregorio Y. Zara's To Do List</{tag}>` sẽ không hoạt động. +2. **Dưới dạng thuộc tính** ngay sau dấu `=`: `src={avatar}` sẽ đọc biến `avatar`, nhưng `src="{avatar}"` sẽ truyền chuỗi `"{avatar}"`. -## Using "double curlies": CSS and other objects in JSX {/*using-double-curlies-css-and-other-objects-in-jsx*/} +## Sử dụng "dấu ngoặc kép": CSS và các đối tượng khác trong JSX {/*using-double-curlies-css-and-other-objects-in-jsx*/} -In addition to strings, numbers, and other JavaScript expressions, you can even pass objects in JSX. Objects are also denoted with curly braces, like `{ name: "Hedy Lamarr", inventions: 5 }`. Therefore, to pass a JS object in JSX, you must wrap the object in another pair of curly braces: `person={{ name: "Hedy Lamarr", inventions: 5 }}`. +Ngoài chuỗi, số và các biểu thức JavaScript khác, bạn thậm chí có thể truyền các đối tượng trong JSX. Các đối tượng cũng được biểu thị bằng dấu ngoặc nhọn, như `{ name: "Hedy Lamarr", inventions: 5 }`. Do đó, để truyền một đối tượng JS trong JSX, bạn phải bọc đối tượng trong một cặp dấu ngoặc nhọn khác: `person={{ name: "Hedy Lamarr", inventions: 5 }}`. -You may see this with inline CSS styles in JSX. React does not require you to use inline styles (CSS classes work great for most cases). But when you need an inline style, you pass an object to the `style` attribute: +Bạn có thể thấy điều này với các kiểu CSS nội tuyến trong JSX. React không yêu cầu bạn sử dụng các kiểu nội tuyến (các lớp CSS hoạt động tốt cho hầu hết các trường hợp). Nhưng khi bạn cần một kiểu nội tuyến, bạn sẽ truyền một đối tượng cho thuộc tính `style`: <Sandpack> @@ -148,9 +148,9 @@ ul { padding: 20px 20px 20px 40px; margin: 0; } </Sandpack> -Try changing the values of `backgroundColor` and `color`. +Hãy thử thay đổi các giá trị của `backgroundColor` và `color`. -You can really see the JavaScript object inside the curly braces when you write it like this: +Bạn thực sự có thể thấy đối tượng JavaScript bên trong dấu ngoặc nhọn khi bạn viết nó như thế này: ```js {2-5} <ul style={ @@ -161,17 +161,17 @@ You can really see the JavaScript object inside the curly braces when you write }> ``` -The next time you see `{{` and `}}` in JSX, know that it's nothing more than an object inside the JSX curlies! +Lần tới khi bạn thấy `{{` và `}}` trong JSX, hãy biết rằng nó không là gì khác ngoài một đối tượng bên trong dấu ngoặc nhọn JSX! <Pitfall> -Inline `style` properties are written in camelCase. For example, HTML `<ul style="background-color: black">` would be written as `<ul style={{ backgroundColor: 'black' }}>` in your component. +Các thuộc tính `style` nội tuyến được viết bằng camelCase. Ví dụ: HTML `<ul style="background-color: black">` sẽ được viết là `<ul style={{ backgroundColor: 'black' }}>` trong thành phần của bạn. </Pitfall> -## More fun with JavaScript objects and curly braces {/*more-fun-with-javascript-objects-and-curly-braces*/} +## Thêm niềm vui với các đối tượng JavaScript và dấu ngoặc nhọn {/*more-fun-with-javascript-objects-and-curly-braces*/} -You can move several expressions into one object, and reference them in your JSX inside curly braces: +Bạn có thể di chuyển một số biểu thức vào một đối tượng và tham chiếu chúng trong JSX của bạn bên trong dấu ngoặc nhọn: <Sandpack> @@ -211,7 +211,7 @@ body > div > div { padding: 20px; } </Sandpack> -In this example, the `person` JavaScript object contains a `name` string and a `theme` object: +Trong ví dụ này, đối tượng JavaScript `person` chứa một chuỗi `name` và một đối tượng `theme`: ```js const person = { @@ -223,31 +223,31 @@ const person = { }; ``` -The component can use these values from `person` like so: +Thành phần có thể sử dụng các giá trị này từ `person` như sau: ```js <div style={person.theme}> <h1>{person.name}'s Todos</h1> ``` -JSX is very minimal as a templating language because it lets you organize data and logic using JavaScript. +JSX rất tối giản như một ngôn ngữ tạo khuôn mẫu vì nó cho phép bạn tổ chức dữ liệu và logic bằng JavaScript. <Recap> -Now you know almost everything about JSX: +Bây giờ bạn đã biết gần như mọi thứ về JSX: -* JSX attributes inside quotes are passed as strings. -* Curly braces let you bring JavaScript logic and variables into your markup. -* They work inside the JSX tag content or immediately after `=` in attributes. -* `{{` and `}}` is not special syntax: it's a JavaScript object tucked inside JSX curly braces. +* Các thuộc tính JSX bên trong dấu ngoặc kép được truyền dưới dạng chuỗi. +* Dấu ngoặc nhọn cho phép bạn đưa logic và biến JavaScript vào mã đánh dấu của mình. +* Chúng hoạt động bên trong nội dung thẻ JSX hoặc ngay sau `=` trong các thuộc tính. +* `{{` và `}}` không phải là cú pháp đặc biệt: nó là một đối tượng JavaScript được giấu bên trong dấu ngoặc nhọn JSX. </Recap> <Challenges> -#### Fix the mistake {/*fix-the-mistake*/} +#### Sửa lỗi {/*fix-the-mistake*/} -This code crashes with an error saying `Objects are not valid as a React child`: +Đoạn mã này bị lỗi với thông báo `Objects are not valid as a React child`: <Sandpack> @@ -287,15 +287,15 @@ body > div > div { padding: 20px; } </Sandpack> -Can you find the problem? +Bạn có thể tìm ra vấn đề không? -<Hint>Look for what's inside the curly braces. Are we putting the right thing there?</Hint> +<Hint>Hãy xem những gì bên trong dấu ngoặc nhọn. Chúng ta có đang đặt đúng thứ ở đó không?</Hint> <Solution> -This is happening because this example renders *an object itself* into the markup rather than a string: `<h1>{person}'s Todos</h1>` is trying to render the entire `person` object! Including raw objects as text content throws an error because React doesn't know how you want to display them. +Điều này xảy ra vì ví dụ này hiển thị *bản thân đối tượng* vào mã đánh dấu thay vì một chuỗi: `<h1>{person}'s Todos</h1>` đang cố gắng hiển thị toàn bộ đối tượng `person`! Bao gồm các đối tượng thô làm nội dung văn bản sẽ gây ra lỗi vì React không biết bạn muốn hiển thị chúng như thế nào. -To fix it, replace `<h1>{person}'s Todos</h1>` with `<h1>{person.name}'s Todos</h1>`: +Để khắc phục, hãy thay thế `<h1>{person}'s Todos</h1>` bằng `<h1>{person.name}'s Todos</h1>`: <Sandpack> @@ -337,9 +337,9 @@ body > div > div { padding: 20px; } </Solution> -#### Extract information into an object {/*extract-information-into-an-object*/} +#### Trích xuất thông tin vào một đối tượng {/*extract-information-into-an-object*/} -Extract the image URL into the `person` object. +Trích xuất URL hình ảnh vào đối tượng `person`. <Sandpack> @@ -381,7 +381,7 @@ body > div > div { padding: 20px; } <Solution> -Move the image URL into a property called `person.imageUrl` and read it from the `<img>` tag using the curlies: +Di chuyển URL hình ảnh vào một thuộc tính có tên là `person.imageUrl` và đọc nó từ thẻ `<img>` bằng cách sử dụng dấu ngoặc nhọn: <Sandpack> @@ -424,13 +424,13 @@ body > div > div { padding: 20px; } </Solution> -#### Write an expression inside JSX curly braces {/*write-an-expression-inside-jsx-curly-braces*/} +#### Viết một biểu thức bên trong dấu ngoặc nhọn JSX {/*write-an-expression-inside-jsx-curly-braces*/} -In the object below, the full image URL is split into four parts: base URL, `imageId`, `imageSize`, and file extension. +Trong đối tượng bên dưới, URL hình ảnh đầy đủ được chia thành bốn phần: URL cơ sở, `imageId`, `imageSize` và phần mở rộng tệp. -We want the image URL to combine these attributes together: base URL (always `'https://i.imgur.com/'`), `imageId` (`'7vQD0fP'`), `imageSize` (`'s'`), and file extension (always `'.jpg'`). However, something is wrong with how the `<img>` tag specifies its `src`. +Chúng tôi muốn URL hình ảnh kết hợp các thuộc tính này với nhau: URL cơ sở (luôn là `'https://i.imgur.com/'`), `imageId` (`'7vQD0fP'`), `imageSize` (`'s'`) và phần mở rộng tệp (luôn là `'.jpg'`). Tuy nhiên, có điều gì đó không ổn với cách thẻ `<img>` chỉ định `src` của nó. -Can you fix it? +Bạn có thể sửa nó không? <Sandpack> @@ -474,15 +474,15 @@ body > div > div { padding: 20px; } </Sandpack> -To check that your fix worked, try changing the value of `imageSize` to `'b'`. The image should resize after your edit. +Để kiểm tra xem bản sửa lỗi của bạn có hoạt động không, hãy thử thay đổi giá trị của `imageSize` thành `'b'`. Hình ảnh sẽ thay đổi kích thước sau khi bạn chỉnh sửa. <Solution> -You can write it as `src={baseUrl + person.imageId + person.imageSize + '.jpg'}`. +Bạn có thể viết nó là `src={baseUrl + person.imageId + person.imageSize + '.jpg'}`. -1. `{` opens the JavaScript expression -2. `baseUrl + person.imageId + person.imageSize + '.jpg'` produces the correct URL string -3. `}` closes the JavaScript expression +1. `{` mở biểu thức JavaScript +2. `baseUrl + person.imageId + person.imageSize + '.jpg'` tạo ra chuỗi URL chính xác +3. `}` đóng biểu thức JavaScript <Sandpack> @@ -525,7 +525,7 @@ body > div > div { padding: 20px; } </Sandpack> -You can also move this expression into a separate function like `getImageUrl` below: +Bạn cũng có thể di chuyển biểu thức này vào một hàm riêng biệt như `getImageUrl` bên dưới: <Sandpack> @@ -580,7 +580,7 @@ body > div > div { padding: 20px; } </Sandpack> -Variables and functions can help you keep the markup simple! +Các biến và hàm có thể giúp bạn giữ cho mã đánh dấu đơn giản! </Solution> diff --git a/src/content/learn/keeping-components-pure.md b/src/content/learn/keeping-components-pure.md index 6d4f55763..3153d7386 100644 --- a/src/content/learn/keeping-components-pure.md +++ b/src/content/learn/keeping-components-pure.md @@ -1,41 +1,41 @@ --- -title: Keeping Components Pure +title: Giữ Các Component Thuần Khiết --- <Intro> -Some JavaScript functions are *pure.* Pure functions only perform a calculation and nothing more. By strictly only writing your components as pure functions, you can avoid an entire class of baffling bugs and unpredictable behavior as your codebase grows. To get these benefits, though, there are a few rules you must follow. +Một số hàm JavaScript là *thuần khiết.* Các hàm thuần khiết chỉ thực hiện một phép tính và không làm gì khác. Bằng cách chỉ viết các component của bạn như các hàm thuần khiết, bạn có thể tránh được cả một loạt các lỗi khó hiểu và hành vi không thể đoán trước khi codebase của bạn phát triển. Để có được những lợi ích này, bạn phải tuân theo một vài quy tắc. </Intro> <YouWillLearn> -* What purity is and how it helps you avoid bugs -* How to keep components pure by keeping changes out of the render phase -* How to use Strict Mode to find mistakes in your components +* Tính thuần khiết là gì và nó giúp bạn tránh lỗi như thế nào +* Làm thế nào để giữ cho các component thuần khiết bằng cách giữ các thay đổi bên ngoài giai đoạn render +* Cách sử dụng Strict Mode để tìm lỗi trong các component của bạn </YouWillLearn> -## Purity: Components as formulas {/*purity-components-as-formulas*/} +## Tính Thuần Khiết: Các Component Như Các Công Thức {/*purity-components-as-formulas*/} -In computer science (and especially the world of functional programming), [a pure function](https://wikipedia.org/wiki/Pure_function) is a function with the following characteristics: +Trong khoa học máy tính (và đặc biệt là thế giới của lập trình hàm), [một hàm thuần khiết](https://wikipedia.org/wiki/Pure_function) là một hàm có các đặc điểm sau: -* **It minds its own business.** It does not change any objects or variables that existed before it was called. -* **Same inputs, same output.** Given the same inputs, a pure function should always return the same result. +* **Nó chỉ lo việc của nó.** Nó không thay đổi bất kỳ đối tượng hoặc biến nào đã tồn tại trước khi nó được gọi. +* **Đầu vào giống nhau, đầu ra giống nhau.** Với cùng một đầu vào, một hàm thuần khiết phải luôn trả về cùng một kết quả. -You might already be familiar with one example of pure functions: formulas in math. +Bạn có thể đã quen thuộc với một ví dụ về các hàm thuần khiết: các công thức trong toán học. -Consider this math formula: <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math>. +Xem xét công thức toán học này: <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math>. -If <Math><MathI>x</MathI> = 2</Math> then <Math><MathI>y</MathI> = 4</Math>. Always. +Nếu <Math><MathI>x</MathI> = 2</Math> thì <Math><MathI>y</MathI> = 4</Math>. Luôn luôn. -If <Math><MathI>x</MathI> = 3</Math> then <Math><MathI>y</MathI> = 6</Math>. Always. +Nếu <Math><MathI>x</MathI> = 3</Math> thì <Math><MathI>y</MathI> = 6</Math>. Luôn luôn. -If <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> won't sometimes be <Math>9</Math> or <Math>–1</Math> or <Math>2.5</Math> depending on the time of day or the state of the stock market. +Nếu <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> sẽ không đôi khi là <Math>9</Math> hoặc <Math>–1</Math> hoặc <Math>2.5</Math> tùy thuộc vào thời gian trong ngày hoặc trạng thái của thị trường chứng khoán. -If <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> and <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> will _always_ be <Math>6</Math>. +Nếu <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> và <Math><MathI>x</MathI> = 3</Math>, <MathI>y</MathI> sẽ _luôn luôn_ là <Math>6</Math>. -If we made this into a JavaScript function, it would look like this: +Nếu chúng ta biến điều này thành một hàm JavaScript, nó sẽ trông như thế này: ```js function double(number) { @@ -43,9 +43,9 @@ function double(number) { } ``` -In the above example, `double` is a **pure function.** If you pass it `3`, it will return `6`. Always. +Trong ví dụ trên, `double` là một **hàm thuần khiết.** Nếu bạn truyền cho nó `3`, nó sẽ trả về `6`. Luôn luôn. -React is designed around this concept. **React assumes that every component you write is a pure function.** This means that React components you write must always return the same JSX given the same inputs: +React được thiết kế dựa trên khái niệm này. **React giả định rằng mọi component bạn viết là một hàm thuần khiết.** Điều này có nghĩa là các component React bạn viết phải luôn trả về cùng một JSX với cùng một đầu vào: <Sandpack> @@ -53,9 +53,9 @@ React is designed around this concept. **React assumes that every component you function Recipe({ drinkers }) { return ( <ol> - <li>Boil {drinkers} cups of water.</li> - <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li> - <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li> + <li>Đun sôi {drinkers} cốc nước.</li> + <li>Thêm {drinkers} thìa trà và {0.5 * drinkers} thìa gia vị.</li> + <li>Thêm {0.5 * drinkers} cốc sữa vào đun sôi và đường tùy khẩu vị.</li> </ol> ); } @@ -63,10 +63,10 @@ function Recipe({ drinkers }) { export default function App() { return ( <section> - <h1>Spiced Chai Recipe</h1> - <h2>For two</h2> + <h1>Công Thức Trà Chai Cay</h1> + <h2>Cho hai người</h2> <Recipe drinkers={2} /> - <h2>For a gathering</h2> + <h2>Cho một buổi tụ tập</h2> <Recipe drinkers={4} /> </section> ); @@ -75,21 +75,21 @@ export default function App() { </Sandpack> -When you pass `drinkers={2}` to `Recipe`, it will return JSX containing `2 cups of water`. Always. +Khi bạn truyền `drinkers={2}` cho `Recipe`, nó sẽ trả về JSX chứa `2 cốc nước`. Luôn luôn. -If you pass `drinkers={4}`, it will return JSX containing `4 cups of water`. Always. +Nếu bạn truyền `drinkers={4}`, nó sẽ trả về JSX chứa `4 cốc nước`. Luôn luôn. -Just like a math formula. +Giống như một công thức toán học. -You could think of your components as recipes: if you follow them and don't introduce new ingredients during the cooking process, you will get the same dish every time. That "dish" is the JSX that the component serves to React to [render.](/learn/render-and-commit) +Bạn có thể nghĩ về các component của mình như các công thức nấu ăn: nếu bạn làm theo chúng và không đưa thêm nguyên liệu mới trong quá trình nấu, bạn sẽ nhận được món ăn giống nhau mỗi lần. "Món ăn" đó là JSX mà component phục vụ cho React để [render.](/learn/render-and-commit) -<Illustration src="/images/docs/illustrations/i_puritea-recipe.png" alt="A tea recipe for x people: take x cups of water, add x spoons of tea and 0.5x spoons of spices, and 0.5x cups of milk" /> +<Illustration src="/images/docs/illustrations/i_puritea-recipe.png" alt="Một công thức trà cho x người: lấy x cốc nước, thêm x thìa trà và 0.5x thìa gia vị, và 0.5x cốc sữa" /> -## Side Effects: (un)intended consequences {/*side-effects-unintended-consequences*/} +## Tác Dụng Phụ: Hậu Quả (Không) Mong Muốn {/*side-effects-unintended-consequences*/} -React's rendering process must always be pure. Components should only *return* their JSX, and not *change* any objects or variables that existed before rendering—that would make them impure! +Quá trình render của React phải luôn thuần khiết. Các component chỉ nên *trả về* JSX của chúng, và không *thay đổi* bất kỳ đối tượng hoặc biến nào đã tồn tại trước khi render—điều đó sẽ làm cho chúng không thuần khiết! -Here is a component that breaks this rule: +Đây là một component vi phạm quy tắc này: <Sandpack> @@ -97,9 +97,9 @@ Here is a component that breaks this rule: let guest = 0; function Cup() { - // Bad: changing a preexisting variable! + // Sai: thay đổi một biến đã tồn tại! guest = guest + 1; - return <h2>Tea cup for guest #{guest}</h2>; + return <h2>Cốc trà cho khách #{guest}</h2>; } export default function TeaSet() { @@ -115,17 +115,17 @@ export default function TeaSet() { </Sandpack> -This component is reading and writing a `guest` variable declared outside of it. This means that **calling this component multiple times will produce different JSX!** And what's more, if _other_ components read `guest`, they will produce different JSX, too, depending on when they were rendered! That's not predictable. +Component này đang đọc và ghi một biến `guest` được khai báo bên ngoài nó. Điều này có nghĩa là **gọi component này nhiều lần sẽ tạo ra JSX khác nhau!** Và hơn thế nữa, nếu các component _khác_ đọc `guest`, chúng cũng sẽ tạo ra JSX khác nhau, tùy thuộc vào thời điểm chúng được render! Điều đó không thể đoán trước được. -Going back to our formula <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math>, now even if <Math><MathI>x</MathI> = 2</Math>, we cannot trust that <Math><MathI>y</MathI> = 4</Math>. Our tests could fail, our users would be baffled, planes would fall out of the sky—you can see how this would lead to confusing bugs! +Quay trở lại công thức của chúng ta <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math>, bây giờ ngay cả khi <Math><MathI>x</MathI> = 2</Math>, chúng ta không thể tin rằng <Math><MathI>y</MathI> = 4</Math>. Các bài kiểm tra của chúng ta có thể thất bại, người dùng của chúng ta sẽ bối rối, máy bay sẽ rơi khỏi bầu trời—bạn có thể thấy điều này sẽ dẫn đến những lỗi khó hiểu như thế nào! -You can fix this component by [passing `guest` as a prop instead](/learn/passing-props-to-a-component): +Bạn có thể sửa component này bằng cách [truyền `guest` như một prop](/learn/passing-props-to-a-component): <Sandpack> ```js function Cup({ guest }) { - return <h2>Tea cup for guest #{guest}</h2>; + return <h2>Cốc trà cho khách #{guest}</h2>; } export default function TeaSet() { @@ -141,37 +141,37 @@ export default function TeaSet() { </Sandpack> -Now your component is pure, as the JSX it returns only depends on the `guest` prop. +Bây giờ component của bạn là thuần khiết, vì JSX mà nó trả về chỉ phụ thuộc vào prop `guest`. -In general, you should not expect your components to be rendered in any particular order. It doesn't matter if you call <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> before or after <Math><MathI>y</MathI> = 5<MathI>x</MathI></Math>: both formulas will resolve independently of each other. In the same way, each component should only "think for itself", and not attempt to coordinate with or depend upon others during rendering. Rendering is like a school exam: each component should calculate JSX on their own! +Nói chung, bạn không nên mong đợi các component của mình được render theo bất kỳ thứ tự cụ thể nào. Không quan trọng nếu bạn gọi <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> trước hay sau <Math><MathI>y</MathI> = 5<MathI>x</MathI></Math>: cả hai công thức sẽ giải quyết độc lập với nhau. Tương tự, mỗi component chỉ nên "tự suy nghĩ cho bản thân", và không cố gắng phối hợp với hoặc phụ thuộc vào những người khác trong quá trình render. Render giống như một bài kiểm tra ở trường: mỗi component nên tự tính toán JSX! <DeepDive> -#### Detecting impure calculations with StrictMode {/*detecting-impure-calculations-with-strict-mode*/} +#### Phát hiện các phép tính không thuần khiết với StrictMode {/*detecting-impure-calculations-with-strict-mode*/} -Although you might not have used them all yet, in React there are three kinds of inputs that you can read while rendering: [props](/learn/passing-props-to-a-component), [state](/learn/state-a-components-memory), and [context.](/learn/passing-data-deeply-with-context) You should always treat these inputs as read-only. +Mặc dù bạn có thể chưa sử dụng tất cả chúng, nhưng trong React có ba loại đầu vào mà bạn có thể đọc trong khi render: [props](/learn/passing-props-to-a-component), [state](/learn/state-a-components-memory), và [context.](/learn/passing-data-deeply-with-context) Bạn nên luôn coi các đầu vào này là chỉ đọc. -When you want to *change* something in response to user input, you should [set state](/learn/state-a-components-memory) instead of writing to a variable. You should never change preexisting variables or objects while your component is rendering. +Khi bạn muốn *thay đổi* một cái gì đó để đáp ứng với đầu vào của người dùng, bạn nên [set state](/learn/state-a-components-memory) thay vì ghi vào một biến. Bạn không bao giờ nên thay đổi các biến hoặc đối tượng đã tồn tại trong khi component của bạn đang render. -React offers a "Strict Mode" in which it calls each component's function twice during development. **By calling the component functions twice, Strict Mode helps find components that break these rules.** +React cung cấp một "Strict Mode" trong đó nó gọi hàm của mỗi component hai lần trong quá trình phát triển. **Bằng cách gọi các hàm component hai lần, Strict Mode giúp tìm các component vi phạm các quy tắc này.** -Notice how the original example displayed "Guest #2", "Guest #4", and "Guest #6" instead of "Guest #1", "Guest #2", and "Guest #3". The original function was impure, so calling it twice broke it. But the fixed pure version works even if the function is called twice every time. **Pure functions only calculate, so calling them twice won't change anything**--just like calling `double(2)` twice doesn't change what's returned, and solving <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> twice doesn't change what <MathI>y</MathI> is. Same inputs, same outputs. Always. +Lưu ý cách ví dụ ban đầu hiển thị "Khách #2", "Khách #4" và "Khách #6" thay vì "Khách #1", "Khách #2" và "Khách #3". Hàm ban đầu không thuần khiết, vì vậy gọi nó hai lần đã làm hỏng nó. Nhưng phiên bản thuần khiết đã sửa hoạt động ngay cả khi hàm được gọi hai lần mỗi lần. **Các hàm thuần khiết chỉ tính toán, vì vậy gọi chúng hai lần sẽ không thay đổi bất cứ điều gì**--giống như gọi `double(2)` hai lần không thay đổi những gì được trả về, và giải <Math><MathI>y</MathI> = 2<MathI>x</MathI></Math> hai lần không thay đổi <MathI>y</MathI> là gì. Đầu vào giống nhau, đầu ra giống nhau. Luôn luôn. -Strict Mode has no effect in production, so it won't slow down the app for your users. To opt into Strict Mode, you can wrap your root component into `<React.StrictMode>`. Some frameworks do this by default. +Strict Mode không có hiệu lực trong sản xuất, vì vậy nó sẽ không làm chậm ứng dụng cho người dùng của bạn. Để chọn tham gia Strict Mode, bạn có thể bọc component gốc của mình trong `<React.StrictMode>`. Một số framework thực hiện điều này theo mặc định. </DeepDive> -### Local mutation: Your component's little secret {/*local-mutation-your-components-little-secret*/} +### Thay Đổi Cục Bộ: Bí Mật Nhỏ Của Component Của Bạn {/*local-mutation-your-components-little-secret*/} -In the above example, the problem was that the component changed a *preexisting* variable while rendering. This is often called a **"mutation"** to make it sound a bit scarier. Pure functions don't mutate variables outside of the function's scope or objects that were created before the call—that makes them impure! +Trong ví dụ trên, vấn đề là component đã thay đổi một biến *đã tồn tại* trong khi render. Điều này thường được gọi là **"mutation"** để làm cho nó nghe có vẻ đáng sợ hơn một chút. Các hàm thuần khiết không mutate các biến bên ngoài phạm vi của hàm hoặc các đối tượng được tạo trước khi gọi—điều đó làm cho chúng không thuần khiết! -However, **it's completely fine to change variables and objects that you've *just* created while rendering.** In this example, you create an `[]` array, assign it to a `cups` variable, and then `push` a dozen cups into it: +Tuy nhiên, **hoàn toàn ổn khi thay đổi các biến và đối tượng mà bạn *vừa* tạo trong khi render.** Trong ví dụ này, bạn tạo một mảng `[]`, gán nó cho một biến `cups`, và sau đó `push` một tá cốc vào nó: <Sandpack> ```js function Cup({ guest }) { - return <h2>Tea cup for guest #{guest}</h2>; + return <h2>Cốc trà cho khách #{guest}</h2>; } export default function TeaGathering() { @@ -185,59 +185,57 @@ export default function TeaGathering() { </Sandpack> -If the `cups` variable or the `[]` array were created outside the `TeaGathering` function, this would be a huge problem! You would be changing a *preexisting* object by pushing items into that array. +Nếu biến `cups` hoặc mảng `[]` được tạo bên ngoài hàm `TeaGathering`, đây sẽ là một vấn đề lớn! Bạn sẽ thay đổi một đối tượng *đã tồn tại* bằng cách đẩy các mục vào mảng đó. -However, it's fine because you've created them *during the same render*, inside `TeaGathering`. No code outside of `TeaGathering` will ever know that this happened. This is called **"local mutation"**—it's like your component's little secret. +Tuy nhiên, điều đó ổn vì bạn đã tạo chúng *trong cùng một lần render*, bên trong `TeaGathering`. Không có mã nào bên ngoài `TeaGathering` sẽ biết rằng điều này đã xảy ra. Điều này được gọi là **"local mutation"**—nó giống như bí mật nhỏ của component của bạn. -## Where you _can_ cause side effects {/*where-you-_can_-cause-side-effects*/} +## Nơi Bạn _Có Thể_ Gây Ra Tác Dụng Phụ {/*where-you-_can_-cause-side-effects*/} -While functional programming relies heavily on purity, at some point, somewhere, _something_ has to change. That's kind of the point of programming! These changes—updating the screen, starting an animation, changing the data—are called **side effects.** They're things that happen _"on the side"_, not during rendering. +Mặc dù lập trình hàm dựa nhiều vào tính thuần khiết, nhưng tại một thời điểm nào đó, ở đâu đó, _một cái gì đó_ phải thay đổi. Đó là mục đích của lập trình! Những thay đổi này—cập nhật màn hình, bắt đầu hoạt ảnh, thay đổi dữ liệu—được gọi là **tác dụng phụ.** Chúng là những thứ xảy ra _"ở bên cạnh"_, không phải trong quá trình render. -In React, **side effects usually belong inside [event handlers.](/learn/responding-to-events)** Event handlers are functions that React runs when you perform some action—for example, when you click a button. Even though event handlers are defined *inside* your component, they don't run *during* rendering! **So event handlers don't need to be pure.** +Trong React, **tác dụng phụ thường thuộc về bên trong [trình xử lý sự kiện.](/learn/responding-to-events)** Trình xử lý sự kiện là các hàm mà React chạy khi bạn thực hiện một số hành động—ví dụ: khi bạn nhấp vào một nút. Mặc dù trình xử lý sự kiện được xác định *bên trong* component của bạn, nhưng chúng không chạy *trong* quá trình render! **Vì vậy, trình xử lý sự kiện không cần phải thuần khiết.** -If you've exhausted all other options and can't find the right event handler for your side effect, you can still attach it to your returned JSX with a [`useEffect`](/reference/react/useEffect) call in your component. This tells React to execute it later, after rendering, when side effects are allowed. **However, this approach should be your last resort.** +Nếu bạn đã cạn kiệt tất cả các tùy chọn khác và không thể tìm thấy trình xử lý sự kiện phù hợp cho tác dụng phụ của mình, bạn vẫn có thể đính kèm nó vào JSX được trả về của mình bằng một lệnh gọi [`useEffect`](/reference/react/useEffect) trong component của bạn. Điều này cho React biết để thực thi nó sau đó, sau khi render, khi các tác dụng phụ được cho phép. **Tuy nhiên, cách tiếp cận này nên là phương sách cuối cùng của bạn.** -When possible, try to express your logic with rendering alone. You'll be surprised how far this can take you! +Khi có thể, hãy cố gắng thể hiện logic của bạn chỉ bằng cách render. Bạn sẽ ngạc nhiên về mức độ bạn có thể đạt được! <DeepDive> -#### Why does React care about purity? {/*why-does-react-care-about-purity*/} +#### Tại Sao React Quan Tâm Đến Tính Thuần Khiết? {/*why-does-react-care-about-purity*/} -Writing pure functions takes some habit and discipline. But it also unlocks marvelous opportunities: +Viết các hàm thuần khiết cần một số thói quen và kỷ luật. Nhưng nó cũng mở ra những cơ hội tuyệt vời: -* Your components could run in a different environment—for example, on the server! Since they return the same result for the same inputs, one component can serve many user requests. -* You can improve performance by [skipping rendering](/reference/react/memo) components whose inputs have not changed. This is safe because pure functions always return the same results, so they are safe to cache. -* If some data changes in the middle of rendering a deep component tree, React can restart rendering without wasting time to finish the outdated render. Purity makes it safe to stop calculating at any time. +* Các component của bạn có thể chạy trong một môi trường khác—ví dụ: trên máy chủ! Vì chúng trả về cùng một kết quả cho cùng một đầu vào, một component có thể phục vụ nhiều yêu cầu của người dùng. +* Bạn có thể cải thiện hiệu suất bằng cách [bỏ qua việc render](/reference/react/memo) các component có đầu vào không thay đổi. Điều này an toàn vì các hàm thuần khiết luôn trả về cùng một kết quả, vì vậy chúng an toàn để lưu vào bộ nhớ cache. +* Nếu một số dữ liệu thay đổi ở giữa quá trình render một cây component sâu, React có thể khởi động lại quá trình render mà không lãng phí thời gian để hoàn thành quá trình render đã lỗi thời. Tính thuần khiết giúp bạn an toàn khi dừng tính toán bất cứ lúc nào. -Every new React feature we're building takes advantage of purity. From data fetching to animations to performance, keeping components pure unlocks the power of the React paradigm. +Mọi tính năng React mới mà chúng tôi đang xây dựng đều tận dụng tính thuần khiết. Từ tìm nạp dữ liệu đến hoạt ảnh đến hiệu suất, việc giữ cho các component thuần khiết sẽ mở ra sức mạnh của mô hình React. </DeepDive> <Recap> -* A component must be pure, meaning: - * **It minds its own business.** It should not change any objects or variables that existed before rendering. - * **Same inputs, same output.** Given the same inputs, a component should always return the same JSX. -* Rendering can happen at any time, so components should not depend on each others' rendering sequence. -* You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, ["set" state](/learn/state-a-components-memory) instead of mutating preexisting objects. -* Strive to express your component's logic in the JSX you return. When you need to "change things", you'll usually want to do it in an event handler. As a last resort, you can `useEffect`. -* Writing pure functions takes a bit of practice, but it unlocks the power of React's paradigm. +* Một component phải thuần khiết, có nghĩa là: + * **Nó chỉ lo việc của nó.** Nó không nên thay đổi bất kỳ đối tượng hoặc biến nào đã tồn tại trước khi render. + * **Đầu vào giống nhau, đầu ra giống nhau.** Với cùng một đầu vào, một component phải luôn trả về cùng một JSX. +* Quá trình render có thể xảy ra bất cứ lúc nào, vì vậy các component không nên phụ thuộc vào trình tự render của nhau. +* Bạn không nên mutate bất kỳ đầu vào nào mà các component của bạn sử dụng để render. Điều đó bao gồm props, state và context. Để cập nhật màn hình, hãy ["set" state](/learn/state-a-components-memory) thay vì mutate các đối tượng đã tồn tại. +* Cố gắng thể hiện logic của component của bạn trong JSX mà bạn trả về. Khi bạn cần "thay đổi mọi thứ", bạn thường sẽ muốn thực hiện nó trong một trình xử lý sự kiện. Là phương sách cuối cùng, bạn có thể sử dụng `useEffect`. +* Viết các hàm thuần khiết cần một chút luyện tập, nhưng nó sẽ mở ra sức mạnh của mô hình React. </Recap> - - <Challenges> -#### Fix a broken clock {/*fix-a-broken-clock*/} +#### Sửa Một Chiếc Đồng Hồ Bị Hỏng {/*fix-a-broken-clock*/} -This component tries to set the `<h1>`'s CSS class to `"night"` during the time from midnight to six hours in the morning, and `"day"` at all other times. However, it doesn't work. Can you fix this component? +Component này cố gắng đặt CSS class của `<h1>` thành `"night"` trong khoảng thời gian từ nửa đêm đến sáu giờ sáng, và `"day"` vào tất cả các thời điểm khác. Tuy nhiên, nó không hoạt động. Bạn có thể sửa component này không? -You can verify whether your solution works by temporarily changing the computer's timezone. When the current time is between midnight and six in the morning, the clock should have inverted colors! +Bạn có thể xác minh xem giải pháp của bạn có hoạt động hay không bằng cách tạm thời thay đổi múi giờ của máy tính. Khi thời gian hiện tại là từ nửa đêm đến sáu giờ sáng, đồng hồ sẽ có màu đảo ngược! <Hint> -Rendering is a *calculation*, it shouldn't try to "do" things. Can you express the same idea differently? +Render là một *phép tính*, nó không nên cố gắng "làm" mọi thứ. Bạn có thể thể hiện cùng một ý tưởng một cách khác không? </Hint> @@ -301,7 +299,7 @@ body > * { <Solution> -You can fix this component by calculating the `className` and including it in the render output: +Bạn có thể sửa component này bằng cách tính toán `className` và bao gồm nó trong đầu ra render: <Sandpack> @@ -362,19 +360,19 @@ body > * { </Sandpack> -In this example, the side effect (modifying the DOM) was not necessary at all. You only needed to return JSX. +Trong ví dụ này, tác dụng phụ (sửa đổi DOM) là không cần thiết. Bạn chỉ cần trả về JSX. </Solution> -#### Fix a broken profile {/*fix-a-broken-profile*/} +#### Sửa Một Hồ Sơ Bị Hỏng {/*fix-a-broken-profile*/} -Two `Profile` components are rendered side by side with different data. Press "Collapse" on the first profile, and then "Expand" it. You'll notice that both profiles now show the same person. This is a bug. +Hai component `Profile` được render cạnh nhau với dữ liệu khác nhau. Nhấn "Collapse" trên hồ sơ đầu tiên, và sau đó "Expand" nó. Bạn sẽ nhận thấy rằng cả hai hồ sơ bây giờ hiển thị cùng một người. Đây là một lỗi. -Find the cause of the bug and fix it. +Tìm nguyên nhân gây ra lỗi và sửa nó. <Hint> -The buggy code is in `Profile.js`. Make sure you read it all from top to bottom! +Mã bị lỗi nằm trong `Profile.js`. Hãy chắc chắn rằng bạn đọc tất cả từ trên xuống dưới! </Hint> @@ -475,9 +473,9 @@ h1 { margin: 5px; font-size: 18px; } <Solution> -The problem is that the `Profile` component writes to a preexisting variable called `currentPerson`, and the `Header` and `Avatar` components read from it. This makes *all three of them* impure and difficult to predict. +Vấn đề là component `Profile` ghi vào một biến đã tồn tại có tên là `currentPerson`, và các component `Header` và `Avatar` đọc từ nó. Điều này làm cho *cả ba* không thuần khiết và khó dự đoán. -To fix the bug, remove the `currentPerson` variable. Instead, pass all information from `Profile` to `Header` and `Avatar` via props. You'll need to add a `person` prop to both components and pass it all the way down. +Để sửa lỗi, hãy xóa biến `currentPerson`. Thay vào đó, hãy truyền tất cả thông tin từ `Profile` đến `Header` và `Avatar` thông qua props. Bạn sẽ cần thêm một prop `person` cho cả hai component và truyền nó xuống. <Sandpack> @@ -571,15 +569,15 @@ h1 { margin: 5px; font-size: 18px; } </Sandpack> -Remember that React does not guarantee that component functions will execute in any particular order, so you can't communicate between them by setting variables. All communication must happen through props. +Hãy nhớ rằng React không đảm bảo rằng các hàm component sẽ thực thi theo bất kỳ thứ tự cụ thể nào, vì vậy bạn không thể giao tiếp giữa chúng bằng cách đặt các biến. Tất cả giao tiếp phải diễn ra thông qua props. </Solution> -#### Fix a broken story tray {/*fix-a-broken-story-tray*/} +#### Sửa Một Khay Story Bị Hỏng {/*fix-a-broken-story-tray*/} -The CEO of your company is asking you to add "stories" to your online clock app, and you can't say no. You've written a `StoryTray` component that accepts a list of `stories`, followed by a "Create Story" placeholder. +CEO của công ty bạn đang yêu cầu bạn thêm "stories" vào ứng dụng đồng hồ trực tuyến của bạn, và bạn không thể từ chối. Bạn đã viết một component `StoryTray` chấp nhận một danh sách `stories`, theo sau là một trình giữ chỗ "Create Story". -You implemented the "Create Story" placeholder by pushing one more fake story at the end of the `stories` array that you receive as a prop. But for some reason, "Create Story" appears more than once. Fix the issue. +Bạn đã triển khai trình giữ chỗ "Create Story" bằng cách đẩy thêm một story giả vào cuối mảng `stories` mà bạn nhận được dưới dạng prop. Nhưng vì một số lý do, "Create Story" xuất hiện nhiều lần. Sửa vấn đề. <Sandpack> @@ -675,11 +673,11 @@ li { <Solution> -Notice how whenever the clock updates, "Create Story" is added *twice*. This serves as a hint that we have a mutation during rendering--Strict Mode calls components twice to make these issues more noticeable. +Lưu ý rằng bất cứ khi nào đồng hồ cập nhật, "Create Story" được thêm vào *hai lần*. Điều này đóng vai trò là một gợi ý rằng chúng ta có một mutation trong quá trình render--Strict Mode gọi các component hai lần để làm cho những vấn đề này dễ nhận thấy hơn. -`StoryTray` function is not pure. By calling `push` on the received `stories` array (a prop!), it is mutating an object that was created *before* `StoryTray` started rendering. This makes it buggy and very difficult to predict. +Hàm `StoryTray` không thuần khiết. Bằng cách gọi `push` trên mảng `stories` đã nhận (một prop!), nó đang mutate một đối tượng được tạo *trước khi* `StoryTray` bắt đầu render. Điều này làm cho nó bị lỗi và rất khó dự đoán. -The simplest fix is to not touch the array at all, and render "Create Story" separately: +Cách sửa đơn giản nhất là không chạm vào mảng và render "Create Story" riêng biệt: <Sandpack> @@ -763,16 +761,16 @@ li { </Sandpack> -Alternatively, you could create a _new_ array (by copying the existing one) before you push an item into it: +Ngoài ra, bạn có thể tạo một mảng _mới_ (bằng cách sao chép mảng hiện có) trước khi bạn đẩy một mục vào nó: <Sandpack> ```js src/StoryTray.js active export default function StoryTray({ stories }) { - // Copy the array! + // Sao chép mảng! let storiesToDisplay = stories.slice(); - // Does not affect the original array: + // Không ảnh hưởng đến mảng ban đầu: storiesToDisplay.push({ id: 'create', label: 'Create Story' @@ -855,9 +853,9 @@ li { </Sandpack> -This keeps your mutation local and your rendering function pure. However, you still need to be careful: for example, if you tried to change any of the array's existing items, you'd have to clone those items too. +Điều này giữ cho mutation của bạn cục bộ và hàm render của bạn thuần khiết. Tuy nhiên, bạn vẫn cần phải cẩn thận: ví dụ: nếu bạn cố gắng thay đổi bất kỳ mục hiện có nào của mảng, bạn cũng sẽ phải clone những mục đó. -It is useful to remember which operations on arrays mutate them, and which don't. For example, `push`, `pop`, `reverse`, and `sort` will mutate the original array, but `slice`, `filter`, and `map` will create a new one. +Điều hữu ích là ghi nhớ những thao tác nào trên mảng mutate chúng và những thao tác nào không. Ví dụ: `push`, `pop`, `reverse` và `sort` sẽ mutate mảng ban đầu, nhưng `slice`, `filter` và `map` sẽ tạo một mảng mới. </Solution> diff --git a/src/content/learn/managing-state.md b/src/content/learn/managing-state.md index 93bcc10fd..907c3519c 100644 --- a/src/content/learn/managing-state.md +++ b/src/content/learn/managing-state.md @@ -1,30 +1,30 @@ --- -title: Managing State +title: Quản Lý State --- <Intro> -As your application grows, it helps to be more intentional about how your state is organized and how the data flows between your components. Redundant or duplicate state is a common source of bugs. In this chapter, you'll learn how to structure your state well, how to keep your state update logic maintainable, and how to share state between distant components. +Khi ứng dụng của bạn phát triển, việc có kế hoạch hơn về cách tổ chức state và cách dữ liệu luân chuyển giữa các component sẽ rất hữu ích. State dư thừa hoặc trùng lặp là một nguồn gốc phổ biến của lỗi. Trong chương này, bạn sẽ học cách cấu trúc state một cách tốt, làm thế nào để giữ cho logic cập nhật state của bạn dễ bảo trì và làm thế nào để chia sẻ state giữa các component ở xa. </Intro> <YouWillLearn isChapter={true}> -* [How to think about UI changes as state changes](/learn/reacting-to-input-with-state) -* [How to structure state well](/learn/choosing-the-state-structure) -* [How to "lift state up" to share it between components](/learn/sharing-state-between-components) -* [How to control whether the state gets preserved or reset](/learn/preserving-and-resetting-state) -* [How to consolidate complex state logic in a function](/learn/extracting-state-logic-into-a-reducer) -* [How to pass information without "prop drilling"](/learn/passing-data-deeply-with-context) -* [How to scale state management as your app grows](/learn/scaling-up-with-reducer-and-context) +* [Cách suy nghĩ về các thay đổi UI như là các thay đổi state](/learn/reacting-to-input-with-state) +* [Cách cấu trúc state một cách tốt](/learn/choosing-the-state-structure) +* [Cách "nâng state lên" để chia sẻ nó giữa các component](/learn/sharing-state-between-components) +* [Cách kiểm soát việc state được giữ lại hay đặt lại](/learn/preserving-and-resetting-state) +* [Cách hợp nhất logic state phức tạp vào một hàm](/learn/extracting-state-logic-into-a-reducer) +* [Cách truyền thông tin mà không cần "truyền prop"](/learn/passing-data-deeply-with-context) +* [Cách mở rộng quản lý state khi ứng dụng của bạn phát triển](/learn/scaling-up-with-reducer-and-context) </YouWillLearn> -## Reacting to input with state {/*reacting-to-input-with-state*/} +## Phản hồi lại input bằng state {/*reacting-to-input-with-state*/} -With React, you won't modify the UI from code directly. For example, you won't write commands like "disable the button", "enable the button", "show the success message", etc. Instead, you will describe the UI you want to see for the different visual states of your component ("initial state", "typing state", "success state"), and then trigger the state changes in response to user input. This is similar to how designers think about UI. +Với React, bạn sẽ không sửa đổi UI trực tiếp từ code. Ví dụ: bạn sẽ không viết các lệnh như "vô hiệu hóa nút", "kích hoạt nút", "hiển thị thông báo thành công", v.v. Thay vào đó, bạn sẽ mô tả UI bạn muốn thấy cho các trạng thái hiển thị khác nhau của component của bạn ("trạng thái ban đầu", "trạng thái đang nhập", "trạng thái thành công"), và sau đó kích hoạt các thay đổi state để đáp ứng lại input của người dùng. Điều này tương tự như cách các nhà thiết kế nghĩ về UI. -Here is a quiz form built using React. Note how it uses the `status` state variable to determine whether to enable or disable the submit button, and whether to show the success message instead. +Đây là một biểu mẫu trắc nghiệm được xây dựng bằng React. Lưu ý cách nó sử dụng biến state `status` để xác định xem có nên kích hoạt hoặc vô hiệu hóa nút gửi hay không, và có nên hiển thị thông báo thành công hay không. <Sandpack> @@ -108,15 +108,15 @@ function submitForm(answer) { <LearnMore path="/learn/reacting-to-input-with-state"> -Read **[Reacting to Input with State](/learn/reacting-to-input-with-state)** to learn how to approach interactions with a state-driven mindset. +Đọc **[Phản hồi lại Input bằng State](/learn/reacting-to-input-with-state)** để tìm hiểu cách tiếp cận các tương tác với tư duy hướng đến state. </LearnMore> -## Choosing the state structure {/*choosing-the-state-structure*/} +## Lựa chọn cấu trúc state {/*choosing-the-state-structure*/} -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. The most important principle is that state shouldn't contain redundant or duplicated information. If there's unnecessary state, it's easy to forget to update it, and introduce bugs! +Cấu trúc state tốt có thể tạo ra sự khác biệt giữa một component dễ sửa đổi và gỡ lỗi, và một component là nguồn gốc liên tục của lỗi. Nguyên tắc quan trọng nhất là state không nên chứa thông tin dư thừa hoặc trùng lặp. Nếu có state không cần thiết, bạn rất dễ quên cập nhật nó và gây ra lỗi! -For example, this form has a **redundant** `fullName` state variable: +Ví dụ: biểu mẫu này có một biến state `fullName` **dư thừa**: <Sandpack> @@ -169,7 +169,7 @@ label { display: block; margin-bottom: 5px; } </Sandpack> -You can remove it and simplify the code by calculating `fullName` while the component is rendering: +Bạn có thể xóa nó và đơn giản hóa code bằng cách tính toán `fullName` trong khi component đang render: <Sandpack> @@ -221,19 +221,19 @@ label { display: block; margin-bottom: 5px; } </Sandpack> -This might seem like a small change, but many bugs in React apps are fixed this way. +Điều này có vẻ như là một thay đổi nhỏ, nhưng nhiều lỗi trong các ứng dụng React được sửa bằng cách này. <LearnMore path="/learn/choosing-the-state-structure"> -Read **[Choosing the State Structure](/learn/choosing-the-state-structure)** to learn how to design the state shape to avoid bugs. +Đọc **[Lựa Chọn Cấu Trúc State](/learn/choosing-the-state-structure)** để tìm hiểu cách thiết kế hình dạng state để tránh lỗi. </LearnMore> -## Sharing state between components {/*sharing-state-between-components*/} +## Chia sẻ state giữa các component {/*sharing-state-between-components*/} -Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as "lifting state up", and it's one of the most common things you will do writing React code. +Đôi khi, bạn muốn state của hai component luôn thay đổi cùng nhau. Để làm điều đó, hãy xóa state khỏi cả hai, di chuyển nó đến parent chung gần nhất của chúng, và sau đó truyền nó xuống cho chúng thông qua props. Điều này được gọi là "nâng state lên", và đó là một trong những điều phổ biến nhất bạn sẽ làm khi viết code React. -In this example, only one panel should be active at a time. To achieve this, instead of keeping the active state inside each individual panel, the parent component holds the state and specifies the props for its children. +Trong ví dụ này, chỉ một panel được kích hoạt tại một thời điểm. Để đạt được điều này, thay vì giữ state hoạt động bên trong mỗi panel riêng lẻ, component parent giữ state và chỉ định các props cho các component con của nó. <Sandpack> @@ -296,15 +296,15 @@ h3, p { margin: 5px 0px; } <LearnMore path="/learn/sharing-state-between-components"> -Read **[Sharing State Between Components](/learn/sharing-state-between-components)** to learn how to lift state up and keep components in sync. +Đọc **[Chia Sẻ State Giữa Các Component](/learn/sharing-state-between-components)** để tìm hiểu cách nâng state lên và giữ cho các component đồng bộ. </LearnMore> -## Preserving and resetting state {/*preserving-and-resetting-state*/} +## Giữ và đặt lại state {/*preserving-and-resetting-state*/} -When you re-render a component, React needs to decide which parts of the tree to keep (and update), and which parts to discard or re-create from scratch. In most cases, React's automatic behavior works well enough. By default, React preserves the parts of the tree that "match up" with the previously rendered component tree. +Khi bạn render lại một component, React cần quyết định phần nào của cây giữ lại (và cập nhật), và phần nào loại bỏ hoặc tạo lại từ đầu. Trong hầu hết các trường hợp, hành vi tự động của React hoạt động đủ tốt. Theo mặc định, React giữ lại các phần của cây "khớp" với cây component đã render trước đó. -However, sometimes this is not what you want. In this chat app, typing a message and then switching the recipient does not reset the input. This can make the user accidentally send a message to the wrong person: +Tuy nhiên, đôi khi đây không phải là điều bạn muốn. Trong ứng dụng trò chuyện này, việc nhập tin nhắn và sau đó chuyển đổi người nhận không đặt lại input. Điều này có thể khiến người dùng vô tình gửi tin nhắn cho nhầm người: <Sandpack> @@ -399,7 +399,7 @@ textarea { </Sandpack> -React lets you override the default behavior, and *force* a component to reset its state by passing it a different `key`, like `<Chat key={email} />`. This tells React that if the recipient is different, it should be considered a *different* `Chat` component that needs to be re-created from scratch with the new data (and UI like inputs). Now switching between the recipients resets the input field--even though you render the same component. +React cho phép bạn ghi đè hành vi mặc định và *buộc* một component đặt lại state của nó bằng cách truyền cho nó một `key` khác, như `<Chat key={email} />`. Điều này cho React biết rằng nếu người nhận khác, nó nên được coi là một component `Chat` *khác* cần được tạo lại từ đầu với dữ liệu mới (và UI như input). Bây giờ, việc chuyển đổi giữa những người nhận sẽ đặt lại trường input--ngay cả khi bạn render cùng một component. <Sandpack> @@ -496,13 +496,13 @@ textarea { <LearnMore path="/learn/preserving-and-resetting-state"> -Read **[Preserving and Resetting State](/learn/preserving-and-resetting-state)** to learn the lifetime of state and how to control it. +Đọc **[Giữ và Đặt Lại State](/learn/preserving-and-resetting-state)** để tìm hiểu vòng đời của state và cách kiểm soát nó. </LearnMore> -## Extracting state logic into a reducer {/*extracting-state-logic-into-a-reducer*/} +## Trích xuất logic state vào một reducer {/*extracting-state-logic-into-a-reducer*/} -Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called "reducer". Your event handlers become concise because they only specify the user "actions". At the bottom of the file, the reducer function specifies how the state should update in response to each action! +Các component có nhiều cập nhật state trải rộng trên nhiều trình xử lý sự kiện có thể trở nên quá tải. Đối với những trường hợp này, bạn có thể hợp nhất tất cả logic cập nhật state bên ngoài component của bạn trong một hàm duy nhất, được gọi là "reducer". Các trình xử lý sự kiện của bạn trở nên ngắn gọn vì chúng chỉ định các "hành động" của người dùng. Ở cuối file, hàm reducer chỉ định cách state sẽ cập nhật để đáp ứng với mỗi hành động! <Sandpack> @@ -693,15 +693,15 @@ ul, li { margin: 0; padding: 0; } <LearnMore path="/learn/extracting-state-logic-into-a-reducer"> -Read **[Extracting State Logic into a Reducer](/learn/extracting-state-logic-into-a-reducer)** to learn how to consolidate logic in the reducer function. +Đọc **[Trích Xuất Logic State vào Một Reducer](/learn/extracting-state-logic-into-a-reducer)** để tìm hiểu cách hợp nhất logic trong hàm reducer. </LearnMore> -## Passing data deeply with context {/*passing-data-deeply-with-context*/} +## Truyền dữ liệu sâu với context {/*passing-data-deeply-with-context*/} -Usually, you will pass information from a parent component to a child component via props. But passing props can become inconvenient if you need to pass some prop through many components, or if many components need the same information. Context lets the parent component make some information available to any component in the tree below it—no matter how deep it is—without passing it explicitly through props. +Thông thường, bạn sẽ truyền thông tin từ một component parent đến một component con thông qua props. Nhưng việc truyền props có thể trở nên bất tiện nếu bạn cần truyền một số prop qua nhiều component, hoặc nếu nhiều component cần cùng một thông tin. Context cho phép component parent cung cấp một số thông tin cho bất kỳ component nào trong cây bên dưới nó—bất kể nó sâu đến đâu—mà không cần truyền nó một cách rõ ràng thông qua props. -Here, the `Heading` component determines its heading level by "asking" the closest `Section` for its level. Each `Section` tracks its own level by asking the parent `Section` and adding one to it. Every `Section` provides information to all components below it without passing props--it does that through context. +Ở đây, component `Heading` xác định cấp độ heading của nó bằng cách "hỏi" `Section` gần nhất về cấp độ của nó. Mỗi `Section` theo dõi cấp độ của riêng nó bằng cách hỏi `Section` parent và thêm một vào nó. Mọi `Section` cung cấp thông tin cho tất cả các component bên dưới nó mà không cần truyền props--nó thực hiện điều đó thông qua context. <Sandpack> @@ -795,15 +795,15 @@ export const LevelContext = createContext(0); <LearnMore path="/learn/passing-data-deeply-with-context"> -Read **[Passing Data Deeply with Context](/learn/passing-data-deeply-with-context)** to learn about using context as an alternative to passing props. +Đọc **[Truyền Dữ Liệu Sâu với Context](/learn/passing-data-deeply-with-context)** để tìm hiểu về cách sử dụng context như một giải pháp thay thế cho việc truyền props. </LearnMore> -## Scaling up with reducer and context {/*scaling-up-with-reducer-and-context*/} +## Mở rộng với reducer và context {/*scaling-up-with-reducer-and-context*/} -Reducers let you consolidate a component’s state update logic. Context lets you pass information deep down to other components. You can combine reducers and context together to manage state of a complex screen. +Reducers cho phép bạn hợp nhất logic cập nhật state của một component. Context cho phép bạn truyền thông tin sâu xuống các component khác. Bạn có thể kết hợp reducers và context với nhau để quản lý state của một màn hình phức tạp. -With this approach, a parent component with complex state manages it with a reducer. Other components anywhere deep in the tree can read its state via context. They can also dispatch actions to update that state. +Với phương pháp này, một component parent có state phức tạp quản lý nó bằng một reducer. Các component khác ở bất kỳ đâu sâu trong cây có thể đọc state của nó thông qua context. Họ cũng có thể dispatch các hành động để cập nhật state đó. <Sandpack> @@ -1006,12 +1006,12 @@ ul, li { margin: 0; padding: 0; } <LearnMore path="/learn/scaling-up-with-reducer-and-context"> -Read **[Scaling Up with Reducer and Context](/learn/scaling-up-with-reducer-and-context)** to learn how state management scales in a growing app. +Đọc **[Mở Rộng với Reducer và Context](/learn/scaling-up-with-reducer-and-context)** để tìm hiểu cách quản lý state mở rộng trong một ứng dụng đang phát triển. </LearnMore> -## What's next? {/*whats-next*/} +## Tiếp theo là gì? {/*whats-next*/} -Head over to [Reacting to Input with State](/learn/reacting-to-input-with-state) to start reading this chapter page by page! +Đi tới [Phản hồi lại Input bằng State](/learn/reacting-to-input-with-state) để bắt đầu đọc trang chương này từng trang một! -Or, if you're already familiar with these topics, why not read about [Escape Hatches](/learn/escape-hatches)? +Hoặc, nếu bạn đã quen thuộc với các chủ đề này, tại sao không đọc về [Các Lối Thoát Hiểm](/learn/escape-hatches)? diff --git a/src/content/learn/passing-props-to-a-component.md b/src/content/learn/passing-props-to-a-component.md index aae682d14..3a7d12232 100644 --- a/src/content/learn/passing-props-to-a-component.md +++ b/src/content/learn/passing-props-to-a-component.md @@ -1,26 +1,25 @@ --- -title: Passing Props to a Component +title: Truyền props vào 1 Component --- <Intro> - -React components use *props* to communicate with each other. Every parent component can pass some information to its child components by giving them props. Props might remind you of HTML attributes, but you can pass any JavaScript value through them, including objects, arrays, and functions. +Các component React sử dụng *props* để giao tiếp với nhau. Mỗi component cha có thể truyền một số thông tin cho các component con của nó bằng cách cung cấp cho chúng các props. Props có thể làm bạn nhớ đến các thuộc tính HTML, nhưng bạn có thể truyền bất kỳ giá trị JavaScript nào thông qua chúng, bao gồm cả đối tượng, mảng và hàm. </Intro> <YouWillLearn> -* How to pass props to a component -* How to read props from a component -* How to specify default values for props -* How to pass some JSX to a component -* How props change over time +* Cách truyền props cho một component +* Cách đọc props từ một component +* Cách chỉ định các giá trị mặc định cho props +* Cách truyền một số JSX cho một component +* Cách props thay đổi theo thời gian </YouWillLearn> -## Familiar props {/*familiar-props*/} +## Các props quen thuộc {/*familiar-props*/} -Props are the information that you pass to a JSX tag. For example, `className`, `src`, `alt`, `width`, and `height` are some of the props you can pass to an `<img>`: +Props là thông tin mà bạn truyền cho một thẻ JSX. Ví dụ: `className`, `src`, `alt`, `width` và `height` là một số props bạn có thể truyền cho một `<img>`: <Sandpack> @@ -51,11 +50,11 @@ body { min-height: 120px; } </Sandpack> -The props you can pass to an `<img>` tag are predefined (ReactDOM conforms to [the HTML standard](https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element)). But you can pass any props to *your own* components, such as `<Avatar>`, to customize them. Here's how! +Các props bạn có thể truyền cho một thẻ `<img>` được xác định trước (ReactDOM tuân theo [tiêu chuẩn HTML](https://www.w3.org/TR/html52/semantics-embedded-content.html#the-img-element)). Nhưng bạn có thể truyền bất kỳ props nào cho các component *của riêng bạn*, chẳng hạn như `<Avatar>`, để tùy chỉnh chúng. Đây là cách thực hiện! -## Passing props to a component {/*passing-props-to-a-component*/} +## Truyền props cho một component {/*passing-props-to-a-component*/} -In this code, the `Profile` component isn't passing any props to its child component, `Avatar`: +Trong đoạn code này, component `Profile` không truyền bất kỳ props nào cho component con của nó, `Avatar`: ```js export default function Profile() { @@ -65,11 +64,11 @@ export default function Profile() { } ``` -You can give `Avatar` some props in two steps. +Bạn có thể cung cấp cho `Avatar` một số props trong hai bước. -### Step 1: Pass props to the child component {/*step-1-pass-props-to-the-child-component*/} +### Bước 1: Truyền props cho component con {/*step-1-pass-props-to-the-child-component*/} -First, pass some props to `Avatar`. For example, let's pass two props: `person` (an object), and `size` (a number): +Đầu tiên, hãy truyền một số props cho `Avatar`. Ví dụ: hãy truyền hai props: `person` (một đối tượng) và `size` (một số): ```js export default function Profile() { @@ -84,25 +83,25 @@ export default function Profile() { <Note> -If double curly braces after `person=` confuse you, recall [they're merely an object](/learn/javascript-in-jsx-with-curly-braces#using-double-curlies-css-and-other-objects-in-jsx) inside the JSX curlies. +Nếu dấu ngoặc nhọn kép sau `person=` làm bạn bối rối, hãy nhớ lại [chúng chỉ là một đối tượng](/learn/javascript-in-jsx-with-curly-braces#using-double-curlies-css-and-other-objects-in-jsx) bên trong dấu ngoặc nhọn JSX. </Note> -Now you can read these props inside the `Avatar` component. +Bây giờ bạn có thể đọc các props này bên trong component `Avatar`. -### Step 2: Read props inside the child component {/*step-2-read-props-inside-the-child-component*/} +### Bước 2: Đọc props bên trong component con {/*step-2-read-props-inside-the-child-component*/} -You can read these props by listing their names `person, size` separated by the commas inside `({` and `})` directly after `function Avatar`. This lets you use them inside the `Avatar` code, like you would with a variable. +Bạn có thể đọc các props này bằng cách liệt kê tên của chúng `person, size` được phân tách bằng dấu phẩy bên trong `({` và `})` ngay sau `function Avatar`. Điều này cho phép bạn sử dụng chúng bên trong code `Avatar`, giống như bạn làm với một biến. ```js function Avatar({ person, size }) { - // person and size are available here + // person và size có sẵn ở đây } ``` -Add some logic to `Avatar` that uses the `person` and `size` props for rendering, and you're done. +Thêm một số logic vào `Avatar` sử dụng các props `person` và `size` để hiển thị, và bạn đã hoàn thành. -Now you can configure `Avatar` to render in many different ways with different props. Try tweaking the values! +Bây giờ bạn có thể định cấu hình `Avatar` để hiển thị theo nhiều cách khác nhau với các props khác nhau. Hãy thử điều chỉnh các giá trị! <Sandpack> @@ -168,9 +167,9 @@ body { min-height: 120px; } </Sandpack> -Props let you think about parent and child components independently. For example, you can change the `person` or the `size` props inside `Profile` without having to think about how `Avatar` uses them. Similarly, you can change how the `Avatar` uses these props, without looking at the `Profile`. +Props cho phép bạn suy nghĩ về các component cha và con một cách độc lập. Ví dụ: bạn có thể thay đổi các props `person` hoặc `size` bên trong `Profile` mà không cần phải suy nghĩ về cách `Avatar` sử dụng chúng. Tương tự, bạn có thể thay đổi cách `Avatar` sử dụng các props này mà không cần nhìn vào `Profile`. -You can think of props like "knobs" that you can adjust. They serve the same role as arguments serve for functions—in fact, props _are_ the only argument to your component! React component functions accept a single argument, a `props` object: +Bạn có thể coi props như "các núm" mà bạn có thể điều chỉnh. Chúng đóng vai trò tương tự như các đối số cho các hàm—thực tế, props _là_ đối số duy nhất cho component của bạn! Các hàm component React chấp nhận một đối số duy nhất, một đối tượng `props`: ```js function Avatar(props) { @@ -180,11 +179,11 @@ function Avatar(props) { } ``` -Usually you don't need the whole `props` object itself, so you destructure it into individual props. +Thông thường, bạn không cần toàn bộ đối tượng `props` mà chỉ cần tách nó thành các props riêng lẻ. <Pitfall> -**Don't miss the pair of `{` and `}` curlies** inside of `(` and `)` when declaring props: +**Đừng bỏ lỡ cặp dấu ngoặc nhọn `{` và `}`** bên trong `(` và `)` khi khai báo props: ```js function Avatar({ person, size }) { @@ -192,7 +191,7 @@ function Avatar({ person, size }) { } ``` -This syntax is called ["destructuring"](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_a_function_parameter) and is equivalent to reading properties from a function parameter: +Cú pháp này được gọi là ["destructuring"](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Unpacking_fields_from_objects_passed_as_a_function_parameter) và tương đương với việc đọc các thuộc tính từ một tham số hàm: ```js function Avatar(props) { @@ -204,9 +203,9 @@ function Avatar(props) { </Pitfall> -## Specifying a default value for a prop {/*specifying-a-default-value-for-a-prop*/} +## Chỉ định một giá trị mặc định cho một prop {/*specifying-a-default-value-for-a-prop*/} -If you want to give a prop a default value to fall back on when no value is specified, you can do it with the destructuring by putting `=` and the default value right after the parameter: +Nếu bạn muốn cung cấp cho một prop một giá trị mặc định để dự phòng khi không có giá trị nào được chỉ định, bạn có thể thực hiện việc đó bằng cách destructuring bằng cách đặt `=` và giá trị mặc định ngay sau tham số: ```js function Avatar({ person, size = 100 }) { @@ -214,13 +213,13 @@ function Avatar({ person, size = 100 }) { } ``` -Now, if `<Avatar person={...} />` is rendered with no `size` prop, the `size` will be set to `100`. +Bây giờ, nếu `<Avatar person={...} />` được hiển thị mà không có prop `size`, thì `size` sẽ được đặt thành `100`. -The default value is only used if the `size` prop is missing or if you pass `size={undefined}`. But if you pass `size={null}` or `size={0}`, the default value will **not** be used. +Giá trị mặc định chỉ được sử dụng nếu prop `size` bị thiếu hoặc nếu bạn truyền `size={undefined}`. Nhưng nếu bạn truyền `size={null}` hoặc `size={0}`, giá trị mặc định sẽ **không** được sử dụng. -## Forwarding props with the JSX spread syntax {/*forwarding-props-with-the-jsx-spread-syntax*/} +## Chuyển tiếp props với cú pháp spread JSX {/*forwarding-props-with-the-jsx-spread-syntax*/} -Sometimes, passing props gets very repetitive: +Đôi khi, việc truyền props trở nên rất lặp đi lặp lại: ```js function Profile({ person, size, isSepia, thickBorder }) { @@ -237,7 +236,7 @@ function Profile({ person, size, isSepia, thickBorder }) { } ``` -There's nothing wrong with repetitive code—it can be more legible. But at times you may value conciseness. Some components forward all of their props to their children, like how this `Profile` does with `Avatar`. Because they don't use any of their props directly, it can make sense to use a more concise "spread" syntax: +Không có gì sai với code lặp đi lặp lại—nó có thể dễ đọc hơn. Nhưng đôi khi bạn có thể coi trọng sự ngắn gọn. Một số component chuyển tiếp tất cả các props của chúng cho các component con của chúng, giống như cách `Profile` này làm với `Avatar`. Vì chúng không sử dụng bất kỳ props nào của chúng trực tiếp, nên có thể hợp lý khi sử dụng cú pháp "spread" ngắn gọn hơn: ```js function Profile(props) { @@ -249,13 +248,13 @@ function Profile(props) { } ``` -This forwards all of `Profile`'s props to the `Avatar` without listing each of their names. +Điều này chuyển tiếp tất cả các props của `Profile` cho `Avatar` mà không cần liệt kê từng tên của chúng. -**Use spread syntax with restraint.** If you're using it in every other component, something is wrong. Often, it indicates that you should split your components and pass children as JSX. More on that next! +**Sử dụng cú pháp spread một cách hạn chế.** Nếu bạn đang sử dụng nó trong mọi component khác, thì có điều gì đó không ổn. Thông thường, nó chỉ ra rằng bạn nên chia các component của mình và truyền các children dưới dạng JSX. Thêm về điều đó tiếp theo! -## Passing JSX as children {/*passing-jsx-as-children*/} +## Truyền JSX dưới dạng children {/*passing-jsx-as-children*/} -It is common to nest built-in browser tags: +Việc lồng các thẻ trình duyệt tích hợp sẵn là điều phổ biến: ```js <div> @@ -263,7 +262,7 @@ It is common to nest built-in browser tags: </div> ``` -Sometimes you'll want to nest your own components the same way: +Đôi khi bạn sẽ muốn lồng các component của riêng bạn theo cùng một cách: ```js <Card> @@ -271,7 +270,7 @@ Sometimes you'll want to nest your own components the same way: </Card> ``` -When you nest content inside a JSX tag, the parent component will receive that content in a prop called `children`. For example, the `Card` component below will receive a `children` prop set to `<Avatar />` and render it in a wrapper div: +Khi bạn lồng nội dung bên trong một thẻ JSX, component cha sẽ nhận nội dung đó trong một prop có tên là `children`. Ví dụ: component `Card` bên dưới sẽ nhận một prop `children` được đặt thành `<Avatar />` và hiển thị nó trong một div bao bọc: <Sandpack> @@ -347,17 +346,17 @@ export function getImageUrl(person, size = 's') { </Sandpack> -Try replacing the `<Avatar>` inside `<Card>` with some text to see how the `Card` component can wrap any nested content. It doesn't need to "know" what's being rendered inside of it. You will see this flexible pattern in many places. +Hãy thử thay thế `<Avatar>` bên trong `<Card>` bằng một số văn bản để xem component `Card` có thể bao bọc bất kỳ nội dung lồng nhau nào. Nó không cần phải "biết" những gì đang được hiển thị bên trong nó. Bạn sẽ thấy mẫu linh hoạt này ở nhiều nơi. -You can think of a component with a `children` prop as having a "hole" that can be "filled in" by its parent components with arbitrary JSX. You will often use the `children` prop for visual wrappers: panels, grids, etc. +Bạn có thể coi một component có prop `children` như có một "lỗ" có thể được "lấp đầy" bởi các component cha của nó bằng JSX tùy ý. Bạn sẽ thường sử dụng prop `children` cho các trình bao bọc trực quan: bảng điều khiển, lưới, v.v. -<Illustration src="/images/docs/illustrations/i_children-prop.png" alt='A puzzle-like Card tile with a slot for "children" pieces like text and Avatar' /> +<Illustration src="/images/docs/illustrations/i_children-prop.png" alt='Một ô Card giống như một câu đố với một khe cắm cho các mảnh "children" như văn bản và Avatar' /> -## How props change over time {/*how-props-change-over-time*/} +## Cách props thay đổi theo thời gian {/*how-props-change-over-time*/} -The `Clock` component below receives two props from its parent component: `color` and `time`. (The parent component's code is omitted because it uses [state](/learn/state-a-components-memory), which we won't dive into just yet.) +Component `Clock` bên dưới nhận hai props từ component cha của nó: `color` và `time`. (Code của component cha bị bỏ qua vì nó sử dụng [state](/learn/state-a-components-memory), mà chúng ta sẽ không đi sâu vào ngay bây giờ.) -Try changing the color in the select box below: +Hãy thử thay đổi màu trong hộp chọn bên dưới: <Sandpack> @@ -392,7 +391,7 @@ export default function App() { return ( <div> <p> - Pick a color:{' '} + Chọn một màu:{' '} <select value={color} onChange={e => setColor(e.target.value)}> <option value="lightcoral">lightcoral</option> <option value="midnightblue">midnightblue</option> @@ -407,31 +406,29 @@ export default function App() { </Sandpack> -This example illustrates that **a component may receive different props over time.** Props are not always static! Here, the `time` prop changes every second, and the `color` prop changes when you select another color. Props reflect a component's data at any point in time, rather than only in the beginning. +Ví dụ này minh họa rằng **một component có thể nhận các props khác nhau theo thời gian.** Props không phải lúc nào cũng tĩnh! Ở đây, prop `time` thay đổi mỗi giây và prop `color` thay đổi khi bạn chọn một màu khác. Props phản ánh dữ liệu của một component tại bất kỳ thời điểm nào, thay vì chỉ trong lúc ban đầu. -However, props are [immutable](https://en.wikipedia.org/wiki/Immutable_object)—a term from computer science meaning "unchangeable". When a component needs to change its props (for example, in response to a user interaction or new data), it will have to "ask" its parent component to pass it _different props_—a new object! Its old props will then be cast aside, and eventually the JavaScript engine will reclaim the memory taken by them. +Tuy nhiên, props là [bất biến](https://en.wikipedia.org/wiki/Immutable_object)—một thuật ngữ từ khoa học máy tính có nghĩa là "không thể thay đổi". Khi một component cần thay đổi các props của nó (ví dụ: để đáp ứng tương tác của người dùng hoặc dữ liệu mới), nó sẽ phải "yêu cầu" component cha của nó truyền cho nó _các props khác nhau_—một đối tượng mới! Các props cũ của nó sau đó sẽ bị loại bỏ và cuối cùng công cụ JavaScript sẽ thu hồi bộ nhớ mà chúng chiếm giữ. -**Don't try to "change props".** When you need to respond to the user input (like changing the selected color), you will need to "set state", which you can learn about in [State: A Component's Memory.](/learn/state-a-components-memory) +**Đừng cố gắng "thay đổi props".** Khi bạn cần phản hồi đầu vào của người dùng (như thay đổi màu đã chọn), bạn sẽ cần "đặt state", bạn có thể tìm hiểu về điều đó trong [State: Bộ nhớ của Component.](/learn/state-a-components-memory) <Recap> -* To pass props, add them to the JSX, just like you would with HTML attributes. -* To read props, use the `function Avatar({ person, size })` destructuring syntax. -* You can specify a default value like `size = 100`, which is used for missing and `undefined` props. -* You can forward all props with `<Avatar {...props} />` JSX spread syntax, but don't overuse it! -* Nested JSX like `<Card><Avatar /></Card>` will appear as `Card` component's `children` prop. -* Props are read-only snapshots in time: every render receives a new version of props. -* You can't change props. When you need interactivity, you'll need to set state. +* Để truyền props, hãy thêm chúng vào JSX, giống như bạn làm với các thuộc tính HTML. +* Để đọc props, hãy sử dụng cú pháp destructuring `function Avatar({ person, size })`. +* Bạn có thể chỉ định một giá trị mặc định như `size = 100`, được sử dụng cho các props bị thiếu và `undefined`. +* Bạn có thể chuyển tiếp tất cả các props bằng cú pháp spread JSX `<Avatar {...props} />`, nhưng đừng lạm dụng nó! +* JSX lồng nhau như `<Card><Avatar /></Card>` sẽ xuất hiện dưới dạng prop `children` của component `Card`. +* Props là các ảnh chụp chỉ đọc tại một thời điểm: mỗi lần hiển thị nhận được một phiên bản props mới. +* Bạn không thể thay đổi props. Khi bạn cần tương tác, bạn sẽ cần đặt state. </Recap> - - <Challenges> -#### Extract a component {/*extract-a-component*/} +#### Trích xuất một component {/*extract-a-component*/} -This `Gallery` component contains some very similar markup for two profiles. Extract a `Profile` component out of it to reduce the duplication. You'll need to choose what props to pass to it. +Component `Gallery` này chứa một số đánh dấu rất giống nhau cho hai profile. Hãy trích xuất một component `Profile` ra khỏi nó để giảm sự trùng lặp. Bạn sẽ cần chọn những props nào để truyền cho nó. <Sandpack> @@ -523,16 +520,15 @@ li { margin: 5px; } </Sandpack> <Hint> - -Start by extracting the markup for one of the scientists. Then find the pieces that don't match it in the second example, and make them configurable by props. +Bắt đầu bằng cách trích xuất đánh dấu cho một trong những nhà khoa học. Sau đó, tìm những phần không khớp với nó trong ví dụ thứ hai và làm cho chúng có thể định cấu hình bằng các đạo cụ. </Hint> <Solution> -In this solution, the `Profile` component accepts multiple props: `imageId` (a string), `name` (a string), `profession` (a string), `awards` (an array of strings), `discovery` (a string), and `imageSize` (a number). +Trong giải pháp này, thành phần `Profile` chấp nhận nhiều đạo cụ: `imageId` (một chuỗi), `name` (một chuỗi), `profession` (một chuỗi), `awards` (một mảng các chuỗi), `discovery` (một chuỗi) và `imageSize` (một số). -Note that the `imageSize` prop has a default value, which is why we don't pass it to the component. +Lưu ý rằng đạo cụ `imageSize` có một giá trị mặc định, đó là lý do tại sao chúng ta không chuyển nó cho thành phần. <Sandpack> @@ -630,9 +626,9 @@ li { margin: 5px; } </Sandpack> -Note how you don't need a separate `awardCount` prop if `awards` is an array. Then you can use `awards.length` to count the number of awards. Remember that props can take any values, and that includes arrays too! +Lưu ý rằng bạn không cần một đạo cụ `awardCount` riêng biệt nếu `awards` là một mảng. Sau đó, bạn có thể sử dụng `awards.length` để đếm số lượng giải thưởng. Hãy nhớ rằng các đạo cụ có thể nhận bất kỳ giá trị nào và điều đó bao gồm cả mảng! -Another solution, which is more similar to the earlier examples on this page, is to group all information about a person in a single object, and pass that object as one prop: +Một giải pháp khác, tương tự hơn với các ví dụ trước đó trên trang này, là nhóm tất cả thông tin về một người trong một đối tượng duy nhất và chuyển đối tượng đó làm một đạo cụ: <Sandpack> @@ -727,15 +723,15 @@ li { margin: 5px; } </Sandpack> -Although the syntax looks slightly different because you're describing properties of a JavaScript object rather than a collection of JSX attributes, these examples are mostly equivalent, and you can pick either approach. +Mặc dù cú pháp trông hơi khác một chút vì bạn đang mô tả các thuộc tính của một đối tượng JavaScript hơn là một tập hợp các thuộc tính JSX, nhưng các ví dụ này hầu như tương đương nhau và bạn có thể chọn một trong hai cách tiếp cận. </Solution> -#### Adjust the image size based on a prop {/*adjust-the-image-size-based-on-a-prop*/} +#### Điều chỉnh kích thước hình ảnh dựa trên một đạo cụ {/*adjust-the-image-size-based-on-a-prop*/} -In this example, `Avatar` receives a numeric `size` prop which determines the `<img>` width and height. The `size` prop is set to `40` in this example. However, if you open the image in a new tab, you'll notice that the image itself is larger (`160` pixels). The real image size is determined by which thumbnail size you're requesting. +Trong ví dụ này, `Avatar` nhận một đạo cụ số `size` xác định chiều rộng và chiều cao của `<img>`. Đạo cụ `size` được đặt thành `40` trong ví dụ này. Tuy nhiên, nếu bạn mở hình ảnh trong một tab mới, bạn sẽ nhận thấy rằng bản thân hình ảnh lớn hơn (`160` pixel). Kích thước hình ảnh thực tế được xác định bởi kích thước hình thu nhỏ bạn đang yêu cầu. -Change the `Avatar` component to request the closest image size based on the `size` prop. Specifically, if the `size` is less than `90`, pass `'s'` ("small") rather than `'b'` ("big") to the `getImageUrl` function. Verify that your changes work by rendering avatars with different values of the `size` prop and opening images in a new tab. +Thay đổi thành phần `Avatar` để yêu cầu kích thước hình ảnh gần nhất dựa trên đạo cụ `size`. Cụ thể, nếu `size` nhỏ hơn `90`, hãy chuyển `'s'` ("nhỏ") thay vì `'b'` ("lớn") cho hàm `getImageUrl`. Xác minh rằng các thay đổi của bạn hoạt động bằng cách hiển thị hình đại diện với các giá trị khác nhau của đạo cụ `size` và mở hình ảnh trong một tab mới. <Sandpack> @@ -786,7 +782,7 @@ export function getImageUrl(person, size) { <Solution> -Here is how you could go about it: +Đây là cách bạn có thể thực hiện: <Sandpack> @@ -848,7 +844,7 @@ export function getImageUrl(person, size) { </Sandpack> -You could also show a sharper image for high DPI screens by taking [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) into account: +Bạn cũng có thể hiển thị hình ảnh sắc nét hơn cho màn hình DPI cao bằng cách tính đến [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio): <Sandpack> @@ -919,13 +915,13 @@ export function getImageUrl(person, size) { </Sandpack> -Props let you encapsulate logic like this inside the `Avatar` component (and change it later if needed) so that everyone can use the `<Avatar>` component without thinking about how the images are requested and resized. +Đạo cụ cho phép bạn đóng gói logic như thế này bên trong thành phần `Avatar` (và thay đổi nó sau này nếu cần) để mọi người có thể sử dụng thành phần `<Avatar>` mà không cần suy nghĩ về cách hình ảnh được yêu cầu và thay đổi kích thước. </Solution> -#### Passing JSX in a `children` prop {/*passing-jsx-in-a-children-prop*/} +#### Chuyển JSX trong một đạo cụ `children` {/*passing-jsx-in-a-children-prop*/} -Extract a `Card` component from the markup below, and use the `children` prop to pass different JSX to it: +Trích xuất một thành phần `Card` từ đánh dấu bên dưới và sử dụng đạo cụ `children` để chuyển các JSX khác nhau cho nó: <Sandpack> @@ -983,13 +979,13 @@ h1 { <Hint> -Any JSX you put inside of a component's tag will be passed as the `children` prop to that component. +Bất kỳ JSX nào bạn đặt bên trong thẻ của một thành phần sẽ được chuyển dưới dạng đạo cụ `children` cho thành phần đó. </Hint> <Solution> -This is how you can use the `Card` component in both places: +Đây là cách bạn có thể sử dụng thành phần `Card` ở cả hai nơi: <Sandpack> @@ -1051,7 +1047,7 @@ h1 { </Sandpack> -You can also make `title` a separate prop if you want every `Card` to always have a title: +Bạn cũng có thể tạo `title` một đạo cụ riêng biệt nếu bạn muốn mọi `Card` luôn có tiêu đề: <Sandpack> diff --git a/src/content/learn/react-compiler.md b/src/content/learn/react-compiler.md index 7c46673e7..58c3cbe73 100644 --- a/src/content/learn/react-compiler.md +++ b/src/content/learn/react-compiler.md @@ -3,201 +3,201 @@ title: React Compiler --- <Intro> -This page will give you an introduction to React Compiler and how to try it out successfully. +Trang này sẽ cung cấp cho bạn phần giới thiệu về React Compiler và cách dùng thử thành công. </Intro> <Wip> -These docs are still a work in progress. More documentation is available in the [React Compiler Working Group repo](https://github.com/reactwg/react-compiler/discussions), and will be upstreamed into these docs when they are more stable. +Tài liệu này vẫn đang trong quá trình hoàn thiện. Bạn có thể tìm thêm tài liệu trong [React Compiler Working Group repo](https://github.com/reactwg/react-compiler/discussions) và sẽ được đưa vào tài liệu này khi chúng ổn định hơn. </Wip> <YouWillLearn> -* Getting started with the compiler -* Installing the compiler and ESLint plugin -* Troubleshooting +* Bắt đầu với trình biên dịch +* Cài đặt trình biên dịch và plugin ESLint +* Khắc phục sự cố </YouWillLearn> <Note> -React Compiler is a new compiler currently in Beta, that we've open sourced to get early feedback from the community. While it has been used in production at companies like Meta, rolling out the compiler to production for your app will depend on the health of your codebase and how well you’ve followed the [Rules of React](/reference/rules). +React Compiler là một trình biên dịch mới hiện đang ở giai đoạn Beta, chúng tôi đã mở mã nguồn để nhận phản hồi sớm từ cộng đồng. Mặc dù nó đã được sử dụng trong sản xuất tại các công ty như Meta, nhưng việc triển khai trình biên dịch vào sản xuất cho ứng dụng của bạn sẽ phụ thuộc vào tình trạng của codebase và mức độ tuân thủ [Rules of React](/reference/rules). -The latest Beta release can be found with the `@beta` tag, and daily experimental releases with `@experimental`. +Bạn có thể tìm thấy bản phát hành Beta mới nhất với tag `@beta` và các bản phát hành thử nghiệm hàng ngày với `@experimental`. </Note> -React Compiler is a new compiler that we've open sourced to get early feedback from the community. It is a build-time only tool that automatically optimizes your React app. It works with plain JavaScript, and understands the [Rules of React](/reference/rules), so you don't need to rewrite any code to use it. +React Compiler là một trình biên dịch mới mà chúng tôi đã mở mã nguồn để nhận phản hồi sớm từ cộng đồng. Nó là một công cụ chỉ chạy trong thời gian build và tự động tối ưu hóa ứng dụng React của bạn. Nó hoạt động với JavaScript thuần túy và hiểu [Rules of React](/reference/rules), vì vậy bạn không cần phải viết lại bất kỳ mã nào để sử dụng nó. -The compiler also includes an [ESLint plugin](#installing-eslint-plugin-react-compiler) that surfaces the analysis from the compiler right in your editor. **We strongly recommend everyone use the linter today.** The linter does not require that you have the compiler installed, so you can use it even if you are not ready to try out the compiler. +Trình biên dịch cũng bao gồm một [ESLint plugin](#installing-eslint-plugin-react-compiler) hiển thị các phân tích từ trình biên dịch ngay trong trình soạn thảo của bạn. **Chúng tôi đặc biệt khuyên mọi người nên sử dụng linter ngay hôm nay.** Linter không yêu cầu bạn phải cài đặt trình biên dịch, vì vậy bạn có thể sử dụng nó ngay cả khi bạn chưa sẵn sàng dùng thử trình biên dịch. -The compiler is currently released as `beta`, and is available to try out on React 17+ apps and libraries. To install the Beta: +Trình biên dịch hiện được phát hành dưới dạng `beta` và có sẵn để dùng thử trên các ứng dụng và thư viện React 17+. Để cài đặt Beta: <TerminalBlock> npm install -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta </TerminalBlock> -Or, if you're using Yarn: +Hoặc, nếu bạn đang sử dụng Yarn: <TerminalBlock> yarn add -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta </TerminalBlock> -If you are not using React 19 yet, please see [the section below](#using-react-compiler-with-react-17-or-18) for further instructions. +Nếu bạn chưa sử dụng React 19, vui lòng xem [phần bên dưới](#using-react-compiler-with-react-17-or-18) để biết thêm hướng dẫn. -### What does the compiler do? {/*what-does-the-compiler-do*/} +### Trình biên dịch làm gì? {/*what-does-the-compiler-do*/} -In order to optimize applications, React Compiler automatically memoizes your code. You may be familiar today with memoization through APIs such as `useMemo`, `useCallback`, and `React.memo`. With these APIs you can tell React that certain parts of your application don't need to recompute if their inputs haven't changed, reducing work on updates. While powerful, it's easy to forget to apply memoization or apply them incorrectly. This can lead to inefficient updates as React has to check parts of your UI that don't have any _meaningful_ changes. +Để tối ưu hóa ứng dụng, React Compiler tự động memoize code của bạn. Bạn có thể đã quen thuộc với memoization thông qua các API như `useMemo`, `useCallback` và `React.memo`. Với các API này, bạn có thể cho React biết rằng một số phần nhất định của ứng dụng không cần tính toán lại nếu đầu vào của chúng không thay đổi, giảm tải công việc khi cập nhật. Mặc dù mạnh mẽ, nhưng rất dễ quên áp dụng memoization hoặc áp dụng chúng không chính xác. Điều này có thể dẫn đến các bản cập nhật không hiệu quả vì React phải kiểm tra các phần của UI không có bất kỳ thay đổi _ý nghĩa_ nào. -The compiler uses its knowledge of JavaScript and React's rules to automatically memoize values or groups of values within your components and hooks. If it detects breakages of the rules, it will automatically skip over just those components or hooks, and continue safely compiling other code. +Trình biên dịch sử dụng kiến thức về JavaScript và các quy tắc của React để tự động memoize các giá trị hoặc nhóm giá trị trong các component và hook của bạn. Nếu nó phát hiện ra các vi phạm quy tắc, nó sẽ tự động bỏ qua các component hoặc hook đó và tiếp tục biên dịch an toàn các code khác. <Note> -React Compiler can statically detect when Rules of React are broken, and safely opt-out of optimizing just the affected components or hooks. It is not necessary for the compiler to optimize 100% of your codebase. +React Compiler có thể phát hiện tĩnh khi Rules of React bị vi phạm và tự động chọn không tối ưu hóa chỉ các component hoặc hook bị ảnh hưởng. Không cần thiết để trình biên dịch tối ưu hóa 100% codebase của bạn. </Note> -If your codebase is already very well-memoized, you might not expect to see major performance improvements with the compiler. However, in practice memoizing the correct dependencies that cause performance issues is tricky to get right by hand. +Nếu codebase của bạn đã được memoize rất tốt, bạn có thể không thấy những cải thiện lớn về hiệu suất với trình biên dịch. Tuy nhiên, trong thực tế, việc memoize các dependency chính xác gây ra các vấn đề về hiệu suất là rất khó để thực hiện bằng tay. <DeepDive> -#### What kind of memoization does React Compiler add? {/*what-kind-of-memoization-does-react-compiler-add*/} +#### Loại memoization nào mà React Compiler thêm vào? {/*what-kind-of-memoization-does-react-compiler-add*/} -The initial release of React Compiler is primarily focused on **improving update performance** (re-rendering existing components), so it focuses on these two use cases: +Bản phát hành ban đầu của React Compiler chủ yếu tập trung vào **cải thiện hiệu suất cập nhật** (re-rendering các component hiện có), vì vậy nó tập trung vào hai trường hợp sử dụng sau: -1. **Skipping cascading re-rendering of components** - * Re-rendering `<Parent />` causes many components in its component tree to re-render, even though only `<Parent />` has changed -1. **Skipping expensive calculations from outside of React** - * For example, calling `expensivelyProcessAReallyLargeArrayOfObjects()` inside of your component or hook that needs that data +1. **Bỏ qua re-rendering theo tầng của các component** + * Re-rendering `<Parent />` khiến nhiều component trong cây component của nó re-render, mặc dù chỉ có `<Parent />` thay đổi +2. **Bỏ qua các tính toán tốn kém từ bên ngoài React** + * Ví dụ: gọi `expensivelyProcessAReallyLargeArrayOfObjects()` bên trong component hoặc hook của bạn cần dữ liệu đó -#### Optimizing Re-renders {/*optimizing-re-renders*/} +#### Tối ưu hóa Re-renders {/*optimizing-re-renders*/} -React lets you express your UI as a function of their current state (more concretely: their props, state, and context). In its current implementation, when a component's state changes, React will re-render that component _and all of its children_ — unless you have applied some form of manual memoization with `useMemo()`, `useCallback()`, or `React.memo()`. For example, in the following example, `<MessageButton>` will re-render whenever `<FriendList>`'s state changes: +React cho phép bạn thể hiện UI của mình như một hàm của trạng thái hiện tại của chúng (cụ thể hơn: props, state và context của chúng). Trong implementation hiện tại, khi state của một component thay đổi, React sẽ re-render component đó _và tất cả các component con của nó_ — trừ khi bạn đã áp dụng một số hình thức memoization thủ công với `useMemo()`, `useCallback()` hoặc `React.memo()`. Ví dụ: trong ví dụ sau, `<MessageButton>` sẽ re-render bất cứ khi nào state của `<FriendList>` thay đổi: ```javascript function FriendList({ friends }) { const onlineCount = useFriendOnlineCount(); if (friends.length === 0) { - return <NoFriends />; + return <NoFriends />; } return ( - <div> - <span>{onlineCount} online</span> - {friends.map((friend) => ( - <FriendListCard key={friend.id} friend={friend} /> - ))} - <MessageButton /> - </div> + <div> + <span>{onlineCount} online</span> + {friends.map((friend) => ( + <FriendListCard key={friend.id} friend={friend} /> + ))} + <MessageButton /> + </div> ); } ``` -[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA) +[_Xem ví dụ này trong React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA) -React Compiler automatically applies the equivalent of manual memoization, ensuring that only the relevant parts of an app re-render as state changes, which is sometimes referred to as "fine-grained reactivity". In the above example, React Compiler determines that the return value of `<FriendListCard />` can be reused even as `friends` changes, and can avoid recreating this JSX _and_ avoid re-rendering `<MessageButton>` as the count changes. +React Compiler tự động áp dụng tương đương với memoization thủ công, đảm bảo rằng chỉ các phần liên quan của ứng dụng re-render khi state thay đổi, đôi khi được gọi là "fine-grained reactivity". Trong ví dụ trên, React Compiler xác định rằng giá trị trả về của `<FriendListCard />` có thể được sử dụng lại ngay cả khi `friends` thay đổi và có thể tránh tạo lại JSX này _và_ tránh re-rendering `<MessageButton>` khi số lượng thay đổi. -#### Expensive calculations also get memoized {/*expensive-calculations-also-get-memoized*/} +#### Các tính toán tốn kém cũng được memoize {/*expensive-calculations-also-get-memoized*/} -The compiler can also automatically memoize for expensive calculations used during rendering: +Trình biên dịch cũng có thể tự động memoize cho các tính toán tốn kém được sử dụng trong quá trình rendering: ```js -// **Not** memoized by React Compiler, since this is not a component or hook +// **Không** được memoize bởi React Compiler, vì đây không phải là component hoặc hook function expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ } -// Memoized by React Compiler since this is a component +// Được memoize bởi React Compiler vì đây là một component function TableContainer({ items }) { - // This function call would be memoized: + // Lệnh gọi hàm này sẽ được memoize: const data = expensivelyProcessAReallyLargeArrayOfObjects(items); // ... } ``` -[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA) +[_Xem ví dụ này trong React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA) -However, if `expensivelyProcessAReallyLargeArrayOfObjects` is truly an expensive function, you may want to consider implementing its own memoization outside of React, because: +Tuy nhiên, nếu `expensivelyProcessAReallyLargeArrayOfObjects` thực sự là một hàm tốn kém, bạn có thể muốn xem xét việc triển khai memoization của riêng nó bên ngoài React, vì: -- React Compiler only memoizes React components and hooks, not every function -- React Compiler's memoization is not shared across multiple components or hooks +- React Compiler chỉ memoize các React component và hook, không phải mọi hàm +- Memoization của React Compiler không được chia sẻ giữa nhiều component hoặc hook -So if `expensivelyProcessAReallyLargeArrayOfObjects` was used in many different components, even if the same exact items were passed down, that expensive calculation would be run repeatedly. We recommend [profiling](https://react.dev/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive) first to see if it really is that expensive before making code more complicated. +Vì vậy, nếu `expensivelyProcessAReallyLargeArrayOfObjects` được sử dụng trong nhiều component khác nhau, ngay cả khi các item giống hệt nhau được truyền xuống, thì tính toán tốn kém đó sẽ được chạy lặp đi lặp lại. Chúng tôi khuyên bạn nên [profiling](https://react.dev/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive) trước để xem nó có thực sự tốn kém hay không trước khi làm cho code trở nên phức tạp hơn. </DeepDive> -### Should I try out the compiler? {/*should-i-try-out-the-compiler*/} +### Tôi có nên dùng thử trình biên dịch không? {/*should-i-try-out-the-compiler*/} -Please note that the compiler is still in Beta and has many rough edges. While it has been used in production at companies like Meta, rolling out the compiler to production for your app will depend on the health of your codebase and how well you've followed the [Rules of React](/reference/rules). +Xin lưu ý rằng trình biên dịch vẫn đang ở giai đoạn Beta và có nhiều điểm chưa hoàn thiện. Mặc dù nó đã được sử dụng trong sản xuất tại các công ty như Meta, nhưng việc triển khai trình biên dịch vào sản xuất cho ứng dụng của bạn sẽ phụ thuộc vào tình trạng của codebase và mức độ tuân thủ [Rules of React](/reference/rules). -**You don't have to rush into using the compiler now. It's okay to wait until it reaches a stable release before adopting it.** However, we do appreciate trying it out in small experiments in your apps so that you can [provide feedback](#reporting-issues) to us to help make the compiler better. +**Bạn không cần phải vội vàng sử dụng trình biên dịch ngay bây giờ. Bạn có thể đợi cho đến khi nó đạt đến bản phát hành ổn định trước khi áp dụng nó.** Tuy nhiên, chúng tôi đánh giá cao việc bạn dùng thử nó trong các thử nghiệm nhỏ trong ứng dụng của mình để bạn có thể [cung cấp phản hồi](#reporting-issues) cho chúng tôi để giúp trình biên dịch tốt hơn. -## Getting Started {/*getting-started*/} +## Bắt đầu {/*getting-started*/} -In addition to these docs, we recommend checking the [React Compiler Working Group](https://github.com/reactwg/react-compiler) for additional information and discussion about the compiler. +Ngoài các tài liệu này, chúng tôi khuyên bạn nên kiểm tra [React Compiler Working Group](https://github.com/reactwg/react-compiler) để biết thêm thông tin và thảo luận về trình biên dịch. -### Installing eslint-plugin-react-compiler {/*installing-eslint-plugin-react-compiler*/} +### Cài đặt eslint-plugin-react-compiler {/*installing-eslint-plugin-react-compiler*/} -React Compiler also powers an ESLint plugin. The ESLint plugin can be used **independently** of the compiler, meaning you can use the ESLint plugin even if you don't use the compiler. +React Compiler cũng cung cấp một ESLint plugin. ESLint plugin có thể được sử dụng **độc lập** với trình biên dịch, có nghĩa là bạn có thể sử dụng ESLint plugin ngay cả khi bạn không sử dụng trình biên dịch. <TerminalBlock> npm install -D eslint-plugin-react-compiler@beta </TerminalBlock> -Then, add it to your ESLint config: +Sau đó, thêm nó vào cấu hình ESLint của bạn: ```js import reactCompiler from 'eslint-plugin-react-compiler' export default [ { - plugins: { - 'react-compiler': reactCompiler, - }, - rules: { - 'react-compiler/react-compiler': 'error', - }, + plugins: { + 'react-compiler': reactCompiler, + }, + rules: { + 'react-compiler/react-compiler': 'error', + }, }, ] ``` -Or, in the deprecated eslintrc config format: +Hoặc, trong định dạng cấu hình eslintrc không được dùng nữa: ```js module.exports = { plugins: [ - 'eslint-plugin-react-compiler', + 'eslint-plugin-react-compiler', ], rules: { - 'react-compiler/react-compiler': 'error', + 'react-compiler/react-compiler': 'error', }, } ``` -The ESLint plugin will display any violations of the rules of React in your editor. When it does this, it means that the compiler has skipped over optimizing that component or hook. This is perfectly okay, and the compiler can recover and continue optimizing other components in your codebase. +ESLint plugin sẽ hiển thị bất kỳ vi phạm nào đối với các quy tắc của React trong trình soạn thảo của bạn. Khi nó làm điều này, điều đó có nghĩa là trình biên dịch đã bỏ qua việc tối ưu hóa component hoặc hook đó. Điều này hoàn toàn ổn và trình biên dịch có thể khôi phục và tiếp tục tối ưu hóa các component khác trong codebase của bạn. <Note> -**You don't have to fix all ESLint violations straight away.** You can address them at your own pace to increase the amount of components and hooks being optimized, but it is not required to fix everything before you can use the compiler. +**Bạn không cần phải sửa tất cả các vi phạm ESLint ngay lập tức.** Bạn có thể giải quyết chúng theo tốc độ của riêng bạn để tăng số lượng component và hook được tối ưu hóa, nhưng không bắt buộc phải sửa mọi thứ trước khi bạn có thể sử dụng trình biên dịch. </Note> -### Rolling out the compiler to your codebase {/*using-the-compiler-effectively*/} +### Triển khai trình biên dịch cho codebase của bạn {/*using-the-compiler-effectively*/} -#### Existing projects {/*existing-projects*/} -The compiler is designed to compile functional components and hooks that follow the [Rules of React](/reference/rules). It can also handle code that breaks those rules by bailing out (skipping over) those components or hooks. However, due to the flexible nature of JavaScript, the compiler cannot catch every possible violation and may compile with false negatives: that is, the compiler may accidentally compile a component/hook that breaks the Rules of React which can lead to undefined behavior. +#### Các dự án hiện có {/*existing-projects*/} +Trình biên dịch được thiết kế để biên dịch các functional component và hook tuân theo [Rules of React](/reference/rules). Nó cũng có thể xử lý code vi phạm các quy tắc đó bằng cách bỏ qua (skipping over) các component hoặc hook đó. Tuy nhiên, do tính chất linh hoạt của JavaScript, trình biên dịch không thể bắt mọi vi phạm có thể xảy ra và có thể biên dịch với false negative: nghĩa là, trình biên dịch có thể vô tình biên dịch một component/hook vi phạm Rules of React, điều này có thể dẫn đến hành vi không xác định. -For this reason, to adopt the compiler successfully on existing projects, we recommend running it on a small directory in your product code first. You can do this by configuring the compiler to only run on a specific set of directories: +Vì lý do này, để áp dụng trình biên dịch thành công trên các dự án hiện có, chúng tôi khuyên bạn nên chạy nó trên một thư mục nhỏ trong product code của bạn trước. Bạn có thể thực hiện việc này bằng cách định cấu hình trình biên dịch chỉ chạy trên một tập hợp các thư mục cụ thể: ```js {3} const ReactCompilerConfig = { sources: (filename) => { - return filename.indexOf('src/path/to/dir') !== -1; + return filename.indexOf('src/path/to/dir') !== -1; }, }; ``` -When you have more confidence with rolling out the compiler, you can expand coverage to other directories as well and slowly roll it out to your whole app. +Khi bạn tự tin hơn với việc triển khai trình biên dịch, bạn có thể mở rộng phạm vi phủ sóng sang các thư mục khác và từ từ triển khai nó cho toàn bộ ứng dụng của bạn. -#### New projects {/*new-projects*/} +#### Các dự án mới {/*new-projects*/} -If you're starting a new project, you can enable the compiler on your entire codebase, which is the default behavior. +Nếu bạn đang bắt đầu một dự án mới, bạn có thể bật trình biên dịch trên toàn bộ codebase của mình, đây là hành vi mặc định. -### Using React Compiler with React 17 or 18 {/*using-react-compiler-with-react-17-or-18*/} +### Sử dụng React Compiler với React 17 hoặc 18 {/*using-react-compiler-with-react-17-or-18*/} -React Compiler works best with React 19 RC. If you are unable to upgrade, you can install the extra `react-compiler-runtime` package which will allow the compiled code to run on versions prior to 19. However, note that the minimum supported version is 17. +React Compiler hoạt động tốt nhất với React 19 RC. Nếu bạn không thể nâng cấp, bạn có thể cài đặt gói `react-compiler-runtime` bổ sung, gói này sẽ cho phép code đã biên dịch chạy trên các phiên bản trước 19. Tuy nhiên, lưu ý rằng phiên bản được hỗ trợ tối thiểu là 17. <TerminalBlock> npm install react-compiler-runtime@beta </TerminalBlock> -You should also add the correct `target` to your compiler config, where `target` is the major version of React you are targeting: +Bạn cũng nên thêm `target` chính xác vào cấu hình trình biên dịch của mình, trong đó `target` là phiên bản chính của React mà bạn đang nhắm mục tiêu: ```js {3} // babel.config.js @@ -207,24 +207,24 @@ const ReactCompilerConfig = { module.exports = function () { return { - plugins: [ - ['babel-plugin-react-compiler', ReactCompilerConfig], - ], + plugins: [ + ['babel-plugin-react-compiler', ReactCompilerConfig], + ], }; }; ``` -### Using the compiler on libraries {/*using-the-compiler-on-libraries*/} +### Sử dụng trình biên dịch trên các thư viện {/*using-the-compiler-on-libraries*/} -React Compiler can also be used to compile libraries. Because React Compiler needs to run on the original source code prior to any code transformations, it is not possible for an application's build pipeline to compile the libraries they use. Hence, our recommendation is for library maintainers to independently compile and test their libraries with the compiler, and ship compiled code to npm. +React Compiler cũng có thể được sử dụng để biên dịch các thư viện. Vì React Compiler cần chạy trên source code gốc trước bất kỳ chuyển đổi code nào, nên không thể để pipeline build của ứng dụng biên dịch các thư viện mà chúng sử dụng. Do đó, chúng tôi khuyên các người duy trì thư viện nên độc lập biên dịch và kiểm tra thư viện của họ bằng trình biên dịch và chuyển code đã biên dịch lên npm. -Because your code is pre-compiled, users of your library will not need to have the compiler enabled in order to benefit from the automatic memoization applied to your library. If your library targets apps not yet on React 19, specify a minimum [`target` and add `react-compiler-runtime` as a direct dependency](#using-react-compiler-with-react-17-or-18). The runtime package will use the correct implementation of APIs depending on the application's version, and polyfill the missing APIs if necessary. +Vì code của bạn được biên dịch trước, người dùng thư viện của bạn sẽ không cần bật trình biên dịch để hưởng lợi từ memoization tự động được áp dụng cho thư viện của bạn. Nếu thư viện của bạn nhắm mục tiêu đến các ứng dụng chưa có trên React 19, hãy chỉ định [`target` tối thiểu và thêm `react-compiler-runtime` làm dependency trực tiếp](#using-react-compiler-with-react-17-or-18). Gói runtime sẽ sử dụng implementation chính xác của các API tùy thuộc vào phiên bản của ứng dụng và polyfill các API bị thiếu nếu cần thiết. -Library code can often require more complex patterns and usage of escape hatches. For this reason, we recommend ensuring that you have sufficient testing in order to identify any issues that might arise from using the compiler on your library. If you identify any issues, you can always opt-out the specific components or hooks with the [`'use no memo'` directive](#something-is-not-working-after-compilation). +Code thư viện thường có thể yêu cầu các pattern phức tạp hơn và sử dụng các escape hatch. Vì lý do này, chúng tôi khuyên bạn nên đảm bảo rằng bạn có đủ thử nghiệm để xác định bất kỳ vấn đề nào có thể phát sinh từ việc sử dụng trình biên dịch trên thư viện của bạn. Nếu bạn xác định bất kỳ vấn đề nào, bạn luôn có thể chọn không sử dụng các component hoặc hook cụ thể bằng directive [`'use no memo'`](#something-is-not-working-after-compilation). -Similarly to apps, it is not necessary to fully compile 100% of your components or hooks to see benefits in your library. A good starting point might be to identify the most performance sensitive parts of your library and ensuring that they don't break the [Rules of React](/reference/rules), which you can use `eslint-plugin-react-compiler` to identify. +Tương tự như các ứng dụng, không cần thiết phải biên dịch hoàn toàn 100% component hoặc hook của bạn để thấy lợi ích trong thư viện của bạn. Một điểm khởi đầu tốt có thể là xác định các phần nhạy cảm nhất về hiệu suất của thư viện của bạn và đảm bảo rằng chúng không vi phạm [Rules of React](/reference/rules), bạn có thể sử dụng `eslint-plugin-react-compiler` để xác định. -## Usage {/*installation*/} +## Cách sử dụng {/*installation*/} ### Babel {/*usage-with-babel*/} @@ -232,9 +232,9 @@ Similarly to apps, it is not necessary to fully compile 100% of your components npm install babel-plugin-react-compiler@beta </TerminalBlock> -The compiler includes a Babel plugin which you can use in your build pipeline to run the compiler. +Trình biên dịch bao gồm một Babel plugin mà bạn có thể sử dụng trong pipeline build của mình để chạy trình biên dịch. -After installing, add it to your Babel config. Please note that it's critical that the compiler run **first** in the pipeline: +Sau khi cài đặt, hãy thêm nó vào cấu hình Babel của bạn. Xin lưu ý rằng điều quan trọng là trình biên dịch phải chạy **đầu tiên** trong pipeline: ```js {7} // babel.config.js @@ -242,19 +242,19 @@ const ReactCompilerConfig = { /* ... */ }; module.exports = function () { return { - plugins: [ - ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first! - // ... - ], + plugins: [ + ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first! + // ... + ], }; }; ``` -`babel-plugin-react-compiler` should run first before other Babel plugins as the compiler requires the input source information for sound analysis. +`babel-plugin-react-compiler` phải chạy trước các Babel plugin khác vì trình biên dịch yêu cầu thông tin source đầu vào để phân tích âm thanh. ### Vite {/*usage-with-vite*/} -If you use Vite, you can add the plugin to vite-plugin-react: +Nếu bạn sử dụng Vite, bạn có thể thêm plugin vào vite-plugin-react: ```js {10} // vite.config.js @@ -262,26 +262,26 @@ const ReactCompilerConfig = { /* ... */ }; export default defineConfig(() => { return { - plugins: [ - react({ - babel: { - plugins: [ - ["babel-plugin-react-compiler", ReactCompilerConfig], - ], - }, - }), - ], - // ... + plugins: [ + react({ + babel: { + plugins: [ + ["babel-plugin-react-compiler", ReactCompilerConfig], + ], + }, + }), + ], + // ... }; }); ``` ### Next.js {/*usage-with-nextjs*/} -Please refer to the [Next.js docs](https://nextjs.org/docs/app/api-reference/next-config-js/reactCompiler) for more information. +Vui lòng tham khảo [tài liệu Next.js](https://nextjs.org/docs/app/api-reference/next-config-js/reactCompiler) để biết thêm thông tin. ### Remix {/*usage-with-remix*/} -Install `vite-plugin-babel`, and add the compiler's Babel plugin to it: +Cài đặt `vite-plugin-babel` và thêm Babel plugin của trình biên dịch vào nó: <TerminalBlock> npm install vite-plugin-babel @@ -295,66 +295,66 @@ const ReactCompilerConfig = { /* ... */ }; export default defineConfig({ plugins: [ - remix({ /* ... */}), - babel({ - filter: /\.[jt]sx?$/, - babelConfig: { - presets: ["@babel/preset-typescript"], // if you use TypeScript - plugins: [ - ["babel-plugin-react-compiler", ReactCompilerConfig], - ], - }, - }), + remix({ /* ... */}), + babel({ + filter: /\.[jt]sx?$/, + babelConfig: { + presets: ["@babel/preset-typescript"], // if you use TypeScript + plugins: [ + ["babel-plugin-react-compiler", ReactCompilerConfig], + ], + }, + }), ], }); ``` ### Webpack {/*usage-with-webpack*/} -A community webpack loader is [now available here](https://github.com/SukkaW/react-compiler-webpack). +Một webpack loader cộng đồng [hiện có tại đây](https://github.com/SukkaW/react-compiler-webpack). ### Expo {/*usage-with-expo*/} -Please refer to [Expo's docs](https://docs.expo.dev/guides/react-compiler/) to enable and use the React Compiler in Expo apps. +Vui lòng tham khảo [tài liệu của Expo](https://docs.expo.dev/guides/react-compiler/) để bật và sử dụng React Compiler trong các ứng dụng Expo. ### Metro (React Native) {/*usage-with-react-native-metro*/} -React Native uses Babel via Metro, so refer to the [Usage with Babel](#usage-with-babel) section for installation instructions. +React Native sử dụng Babel thông qua Metro, vì vậy hãy tham khảo phần [Cách sử dụng với Babel](#usage-with-babel) để biết hướng dẫn cài đặt. ### Rspack {/*usage-with-rspack*/} -Please refer to [Rspack's docs](https://rspack.dev/guide/tech/react#react-compiler) to enable and use the React Compiler in Rspack apps. +Vui lòng tham khảo [tài liệu của Rspack](https://rspack.dev/guide/tech/react#react-compiler) để bật và sử dụng React Compiler trong các ứng dụng Rspack. ### Rsbuild {/*usage-with-rsbuild*/} -Please refer to [Rsbuild's docs](https://rsbuild.dev/guide/framework/react#react-compiler) to enable and use the React Compiler in Rsbuild apps. +Vui lòng tham khảo [tài liệu của Rsbuild](https://rsbuild.dev/guide/framework/react#react-compiler) để bật và sử dụng React Compiler trong các ứng dụng Rsbuild. -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -To report issues, please first create a minimal repro on the [React Compiler Playground](https://playground.react.dev/) and include it in your bug report. You can open issues in the [facebook/react](https://github.com/facebook/react/issues) repo. +Để báo cáo sự cố, trước tiên hãy tạo một bản repro tối thiểu trên [React Compiler Playground](https://playground.react.dev/) và đưa nó vào báo cáo lỗi của bạn. Bạn có thể mở các issue trong repo [facebook/react](https://github.com/facebook/react/issues). -You can also provide feedback in the React Compiler Working Group by applying to be a member. Please see [the README for more details on joining](https://github.com/reactwg/react-compiler). +Bạn cũng có thể cung cấp phản hồi trong React Compiler Working Group bằng cách đăng ký làm thành viên. Vui lòng xem [README để biết thêm chi tiết về cách tham gia](https://github.com/reactwg/react-compiler). -### What does the compiler assume? {/*what-does-the-compiler-assume*/} +### Trình biên dịch giả định điều gì? {/*what-does-the-compiler-assume*/} -React Compiler assumes that your code: +React Compiler giả định rằng code của bạn: -1. Is valid, semantic JavaScript. -2. Tests that nullable/optional values and properties are defined before accessing them (for example, by enabling [`strictNullChecks`](https://www.typescriptlang.org/tsconfig/#strictNullChecks) if using TypeScript), i.e., `if (object.nullableProperty) { object.nullableProperty.foo }` or with optional-chaining `object.nullableProperty?.foo`. -3. Follows the [Rules of React](https://react.dev/reference/rules). +1. Là JavaScript hợp lệ, ngữ nghĩa. +2. Kiểm tra xem các giá trị và thuộc tính nullable/optional có được xác định trước khi truy cập chúng hay không (ví dụ: bằng cách bật [`strictNullChecks`](https://www.typescriptlang.org/tsconfig/#strictNullChecks) nếu sử dụng TypeScript), tức là `if (object.nullableProperty) { object.nullableProperty.foo }` hoặc với optional-chaining `object.nullableProperty?.foo`. +3. Tuân theo [Rules of React](https://react.dev/reference/rules). -React Compiler can verify many of the Rules of React statically, and will safely skip compilation when it detects an error. To see the errors we recommend also installing [eslint-plugin-react-compiler](https://www.npmjs.com/package/eslint-plugin-react-compiler). +React Compiler có thể xác minh nhiều Rules of React một cách tĩnh và sẽ bỏ qua quá trình biên dịch một cách an toàn khi phát hiện lỗi. Để xem các lỗi, chúng tôi khuyên bạn cũng nên cài đặt [eslint-plugin-react-compiler](https://www.npmjs.com/package/eslint-plugin-react-compiler). -### How do I know my components have been optimized? {/*how-do-i-know-my-components-have-been-optimized*/} +### Làm cách nào để biết các component của tôi đã được tối ưu hóa? {/*how-do-i-know-my-components-have-been-optimized*/} -[React DevTools](/learn/react-developer-tools) (v5.0+) and [React Native DevTools](https://reactnative.dev/docs/react-native-devtools) have built-in support for React Compiler and will display a "Memo ✨" badge next to components that have been optimized by the compiler. +[React DevTools](/learn/react-developer-tools) (v5.0+) và [React Native DevTools](https://reactnative.dev/docs/react-native-devtools) có hỗ trợ tích hợp cho React Compiler và sẽ hiển thị huy hiệu "Memo ✨" bên cạnh các component đã được tối ưu hóa bởi trình biên dịch. -### Something is not working after compilation {/*something-is-not-working-after-compilation*/} -If you have eslint-plugin-react-compiler installed, the compiler will display any violations of the rules of React in your editor. When it does this, it means that the compiler has skipped over optimizing that component or hook. This is perfectly okay, and the compiler can recover and continue optimizing other components in your codebase. **You don't have to fix all ESLint violations straight away.** You can address them at your own pace to increase the amount of components and hooks being optimized. +### Có gì đó không hoạt động sau khi biên dịch {/*something-is-not-working-after-compilation*/} +Nếu bạn đã cài đặt eslint-plugin-react-compiler, trình biên dịch sẽ hiển thị bất kỳ vi phạm nào đối với các quy tắc của React trong trình soạn thảo của bạn. Khi nó làm điều này, điều đó có nghĩa là trình biên dịch đã bỏ qua việc tối ưu hóa component hoặc hook đó. Điều này hoàn toàn ổn và trình biên dịch có thể khôi phục và tiếp tục tối ưu hóa các component khác trong codebase của bạn. **Bạn không cần phải sửa tất cả các vi phạm ESLint ngay lập tức.** Bạn có thể giải quyết chúng theo tốc độ của riêng bạn để tăng số lượng component và hook được tối ưu hóa. -Due to the flexible and dynamic nature of JavaScript however, it's not possible to comprehensively detect all cases. Bugs and undefined behavior such as infinite loops may occur in those cases. +Tuy nhiên, do tính chất linh hoạt và động của JavaScript, không thể phát hiện toàn diện tất cả các trường hợp. Các lỗi và hành vi không xác định như vòng lặp vô hạn có thể xảy ra trong những trường hợp đó. -If your app doesn't work properly after compilation and you aren't seeing any ESLint errors, the compiler may be incorrectly compiling your code. To confirm this, try to make the issue go away by aggressively opting out any component or hook you think might be related via the [`"use no memo"` directive](#opt-out-of-the-compiler-for-a-component). +Nếu ứng dụng của bạn không hoạt động bình thường sau khi biên dịch và bạn không thấy bất kỳ lỗi ESLint nào, thì trình biên dịch có thể đang biên dịch code của bạn không chính xác. Để xác nhận điều này, hãy thử làm cho sự cố biến mất bằng cách chủ động chọn không sử dụng bất kỳ component hoặc hook nào bạn cho là có liên quan thông qua directive [`"use no memo"`](#opt-out-of-the-compiler-for-a-component). ```js {2} function SuspiciousComponent() { @@ -366,13 +366,13 @@ function SuspiciousComponent() { <Note> #### `"use no memo"` {/*use-no-memo*/} -`"use no memo"` is a _temporary_ escape hatch that lets you opt-out components and hooks from being compiled by the React Compiler. This directive is not meant to be long lived the same way as eg [`"use client"`](/reference/rsc/use-client) is. +`"use no memo"` là một escape hatch _tạm thời_ cho phép bạn chọn không biên dịch các component và hook bởi React Compiler. Directive này không có nghĩa là tồn tại lâu dài giống như [`"use client"`](/reference/rsc/use-client). -It is not recommended to reach for this directive unless it's strictly necessary. Once you opt-out a component or hook, it is opted-out forever until the directive is removed. This means that even if you fix the code, the compiler will still skip over compiling it unless you remove the directive. +Không nên sử dụng directive này trừ khi thực sự cần thiết. Khi bạn chọn không sử dụng một component hoặc hook, nó sẽ bị chọn không sử dụng vĩnh viễn cho đến khi directive bị xóa. Điều này có nghĩa là ngay cả khi bạn sửa code, trình biên dịch vẫn sẽ bỏ qua việc biên dịch nó trừ khi bạn xóa directive. </Note> -When you make the error go away, confirm that removing the opt out directive makes the issue come back. Then share a bug report with us (you can try to reduce it to a small repro, or if it's open source code you can also just paste the entire source) using the [React Compiler Playground](https://playground.react.dev) so we can identify and help fix the issue. +Khi bạn làm cho lỗi biến mất, hãy xác nhận rằng việc xóa directive chọn không sử dụng sẽ khiến sự cố quay trở lại. Sau đó, hãy chia sẻ báo cáo lỗi với chúng tôi (bạn có thể thử giảm nó xuống một bản repro nhỏ hoặc nếu đó là code nguồn mở, bạn cũng có thể chỉ cần dán toàn bộ source) bằng [React Compiler Playground](https://playground.react.dev) để chúng tôi có thể xác định và giúp khắc phục sự cố. -### Other issues {/*other-issues*/} +### Các vấn đề khác {/*other-issues*/} -Please see https://github.com/reactwg/react-compiler/discussions/7. +Vui lòng xem https://github.com/reactwg/react-compiler/discussions/7. diff --git a/src/content/learn/reacting-to-input-with-state.md b/src/content/learn/reacting-to-input-with-state.md index da559dc0f..aa010d16e 100644 --- a/src/content/learn/reacting-to-input-with-state.md +++ b/src/content/learn/reacting-to-input-with-state.md @@ -1,37 +1,37 @@ --- -title: Reacting to Input with State +title: Phản hồi Đầu vào bằng State --- <Intro> -React provides a declarative way to manipulate the UI. Instead of manipulating individual pieces of the UI directly, you describe the different states that your component can be in, and switch between them in response to the user input. This is similar to how designers think about the UI. +React cung cấp một cách khai báo để thao tác UI. Thay vì thao tác trực tiếp các phần tử UI riêng lẻ, bạn mô tả các trạng thái khác nhau mà component của bạn có thể ở và chuyển đổi giữa chúng để phản hồi đầu vào của người dùng. Điều này tương tự như cách các nhà thiết kế nghĩ về UI. </Intro> <YouWillLearn> -* How declarative UI programming differs from imperative UI programming -* How to enumerate the different visual states your component can be in -* How to trigger the changes between the different visual states from code +* Sự khác biệt giữa lập trình UI khai báo và lập trình UI mệnh lệnh +* Cách liệt kê các trạng thái hiển thị khác nhau mà component của bạn có thể ở +* Cách kích hoạt các thay đổi giữa các trạng thái hiển thị khác nhau từ code </YouWillLearn> -## How declarative UI compares to imperative {/*how-declarative-ui-compares-to-imperative*/} +## So sánh UI khai báo với UI mệnh lệnh {/*how-declarative-ui-compares-to-imperative*/} -When you design UI interactions, you probably think about how the UI *changes* in response to user actions. Consider a form that lets the user submit an answer: +Khi bạn thiết kế các tương tác UI, bạn có thể nghĩ về cách UI *thay đổi* để phản hồi các hành động của người dùng. Hãy xem xét một biểu mẫu cho phép người dùng gửi câu trả lời: -* When you type something into the form, the "Submit" button **becomes enabled.** -* When you press "Submit", both the form and the button **become disabled,** and a spinner **appears.** -* If the network request succeeds, the form **gets hidden,** and the "Thank you" message **appears.** -* If the network request fails, an error message **appears,** and the form **becomes enabled** again. +* Khi bạn nhập nội dung gì đó vào biểu mẫu, nút "Gửi" **sẽ được bật.** +* Khi bạn nhấn "Gửi", cả biểu mẫu và nút **sẽ bị tắt,** và một spinner **xuất hiện.** +* Nếu yêu cầu mạng thành công, biểu mẫu **sẽ bị ẩn,** và thông báo "Cảm ơn" **xuất hiện.** +* Nếu yêu cầu mạng không thành công, một thông báo lỗi **xuất hiện,** và biểu mẫu **sẽ được bật** lại. -In **imperative programming,** the above corresponds directly to how you implement interaction. You have to write the exact instructions to manipulate the UI depending on what just happened. Here's another way to think about this: imagine riding next to someone in a car and telling them turn by turn where to go. +Trong **lập trình mệnh lệnh,** điều trên tương ứng trực tiếp với cách bạn triển khai tương tác. Bạn phải viết các hướng dẫn chính xác để thao tác UI tùy thuộc vào những gì vừa xảy ra. Đây là một cách khác để nghĩ về điều này: hãy tưởng tượng bạn đang đi cạnh ai đó trong xe hơi và chỉ cho họ từng ngã rẽ nơi cần đi. -<Illustration src="/images/docs/illustrations/i_imperative-ui-programming.png" alt="In a car driven by an anxious-looking person representing JavaScript, a passenger orders the driver to execute a sequence of complicated turn by turn navigations." /> +<Illustration src="/images/docs/illustrations/i_imperative-ui-programming.png" alt="Trong một chiếc xe hơi do một người trông lo lắng đại diện cho JavaScript lái, một hành khách ra lệnh cho người lái xe thực hiện một chuỗi điều hướng phức tạp từng ngã rẽ." /> -They don't know where you want to go, they just follow your commands. (And if you get the directions wrong, you end up in the wrong place!) It's called *imperative* because you have to "command" each element, from the spinner to the button, telling the computer *how* to update the UI. +Họ không biết bạn muốn đi đâu, họ chỉ làm theo lệnh của bạn. (Và nếu bạn chỉ sai đường, bạn sẽ đến nhầm chỗ!) Nó được gọi là *mệnh lệnh* vì bạn phải "ra lệnh" cho từng phần tử, từ spinner đến nút, cho máy tính biết *cách* cập nhật UI. -In this example of imperative UI programming, the form is built *without* React. It only uses the browser [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model): +Trong ví dụ về lập trình UI mệnh lệnh này, biểu mẫu được xây dựng *không* có React. Nó chỉ sử dụng [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) của trình duyệt: <Sandpack> @@ -131,37 +131,37 @@ body { font-family: sans-serif; margin: 20px; padding: 0; } </Sandpack> -Manipulating the UI imperatively works well enough for isolated examples, but it gets exponentially more difficult to manage in more complex systems. Imagine updating a page full of different forms like this one. Adding a new UI element or a new interaction would require carefully checking all existing code to make sure you haven't introduced a bug (for example, forgetting to show or hide something). +Thao tác UI một cách mệnh lệnh hoạt động đủ tốt cho các ví dụ riêng lẻ, nhưng nó trở nên khó quản lý hơn theo cấp số nhân trong các hệ thống phức tạp hơn. Hãy tưởng tượng việc cập nhật một trang đầy các biểu mẫu khác nhau như thế này. Việc thêm một phần tử UI mới hoặc một tương tác mới sẽ yêu cầu kiểm tra cẩn thận tất cả code hiện có để đảm bảo bạn không gây ra lỗi (ví dụ: quên hiển thị hoặc ẩn nội dung gì đó). -React was built to solve this problem. +React được xây dựng để giải quyết vấn đề này. -In React, you don't directly manipulate the UI--meaning you don't enable, disable, show, or hide components directly. Instead, you **declare what you want to show,** and React figures out how to update the UI. Think of getting into a taxi and telling the driver where you want to go instead of telling them exactly where to turn. It's the driver's job to get you there, and they might even know some shortcuts you haven't considered! +Trong React, bạn không thao tác trực tiếp UI--nghĩa là bạn không bật, tắt, hiển thị hoặc ẩn các component trực tiếp. Thay vào đó, bạn **khai báo những gì bạn muốn hiển thị,** và React sẽ tìm ra cách cập nhật UI. Hãy nghĩ đến việc bắt taxi và nói với tài xế nơi bạn muốn đến thay vì chỉ cho họ chính xác nơi cần rẽ. Công việc của tài xế là đưa bạn đến đó, và họ thậm chí có thể biết một số đường tắt mà bạn chưa xem xét! -<Illustration src="/images/docs/illustrations/i_declarative-ui-programming.png" alt="In a car driven by React, a passenger asks to be taken to a specific place on the map. React figures out how to do that." /> +<Illustration src="/images/docs/illustrations/i_declarative-ui-programming.png" alt="Trong một chiếc xe hơi do React lái, một hành khách yêu cầu được đưa đến một địa điểm cụ thể trên bản đồ. React tìm ra cách thực hiện điều đó." /> -## Thinking about UI declaratively {/*thinking-about-ui-declaratively*/} +## Tư duy về UI một cách khai báo {/*thinking-about-ui-declaratively*/} -You've seen how to implement a form imperatively above. To better understand how to think in React, you'll walk through reimplementing this UI in React below: +Bạn đã thấy cách triển khai một biểu mẫu một cách mệnh lệnh ở trên. Để hiểu rõ hơn về cách tư duy trong React, bạn sẽ thực hiện lại UI này trong React bên dưới: -1. **Identify** your component's different visual states -2. **Determine** what triggers those state changes -3. **Represent** the state in memory using `useState` -4. **Remove** any non-essential state variables -5. **Connect** the event handlers to set the state +1. **Xác định** các trạng thái hiển thị khác nhau của component của bạn +2. **Xác định** điều gì kích hoạt những thay đổi trạng thái đó +3. **Biểu diễn** trạng thái trong bộ nhớ bằng `useState` +4. **Loại bỏ** bất kỳ biến trạng thái không cần thiết nào +5. **Kết nối** các trình xử lý sự kiện để đặt trạng thái -### Step 1: Identify your component's different visual states {/*step-1-identify-your-components-different-visual-states*/} +### Bước 1: Xác định các trạng thái hiển thị khác nhau của component của bạn {/*step-1-identify-your-components-different-visual-states*/} -In computer science, you may hear about a ["state machine"](https://en.wikipedia.org/wiki/Finite-state_machine) being in one of several “states”. If you work with a designer, you may have seen mockups for different "visual states". React stands at the intersection of design and computer science, so both of these ideas are sources of inspiration. +Trong khoa học máy tính, bạn có thể nghe nói về một ["máy trạng thái"](https://en.wikipedia.org/wiki/Finite-state_machine) đang ở một trong số các "trạng thái". Nếu bạn làm việc với một nhà thiết kế, bạn có thể đã thấy các bản mô phỏng cho các "trạng thái hiển thị" khác nhau. React đứng ở giao điểm giữa thiết kế và khoa học máy tính, vì vậy cả hai ý tưởng này đều là nguồn cảm hứng. -First, you need to visualize all the different "states" of the UI the user might see: +Đầu tiên, bạn cần hình dung tất cả các "trạng thái" khác nhau của UI mà người dùng có thể thấy: -* **Empty**: Form has a disabled "Submit" button. -* **Typing**: Form has an enabled "Submit" button. -* **Submitting**: Form is completely disabled. Spinner is shown. -* **Success**: "Thank you" message is shown instead of a form. -* **Error**: Same as Typing state, but with an extra error message. +* **Trống**: Biểu mẫu có nút "Gửi" bị tắt. +* **Đang nhập**: Biểu mẫu có nút "Gửi" được bật. +* **Đang gửi**: Biểu mẫu hoàn toàn bị tắt. Spinner được hiển thị. +* **Thành công**: Thông báo "Cảm ơn" được hiển thị thay vì biểu mẫu. +* **Lỗi**: Giống như trạng thái Đang nhập, nhưng có thêm thông báo lỗi. -Just like a designer, you'll want to "mock up" or create "mocks" for the different states before you add logic. For example, here is a mock for just the visual part of the form. This mock is controlled by a prop called `status` with a default value of `'empty'`: +Giống như một nhà thiết kế, bạn sẽ muốn "mô phỏng" hoặc tạo "bản mô phỏng" cho các trạng thái khác nhau trước khi bạn thêm logic. Ví dụ: đây là bản mô phỏng chỉ cho phần hiển thị của biểu mẫu. Bản mô phỏng này được điều khiển bởi một prop có tên là `status` với giá trị mặc định là `'empty'`: <Sandpack> @@ -192,7 +192,7 @@ export default function Form({ </Sandpack> -You could call that prop anything you like, the naming is not important. Try editing `status = 'empty'` to `status = 'success'` to see the success message appear. Mocking lets you quickly iterate on the UI before you wire up any logic. Here is a more fleshed out prototype of the same component, still "controlled" by the `status` prop: +Bạn có thể gọi prop đó là bất cứ điều gì bạn thích, việc đặt tên không quan trọng. Hãy thử chỉnh sửa `status = 'empty'` thành `status = 'success'` để thấy thông báo thành công xuất hiện. Mô phỏng cho phép bạn nhanh chóng lặp lại trên UI trước khi bạn kết nối bất kỳ logic nào. Dưới đây là một nguyên mẫu đầy đủ hơn của cùng một component, vẫn "được điều khiển" bởi prop `status`: <Sandpack> @@ -240,9 +240,9 @@ export default function Form({ <DeepDive> -#### Displaying many visual states at once {/*displaying-many-visual-states-at-once*/} +#### Hiển thị nhiều trạng thái hiển thị cùng một lúc {/*displaying-many-visual-states-at-once*/} -If a component has a lot of visual states, it can be convenient to show them all on one page: +Nếu một component có nhiều trạng thái hiển thị, có thể thuận tiện để hiển thị tất cả chúng trên một trang: <Sandpack> @@ -307,61 +307,61 @@ body { margin: 0; } </Sandpack> -Pages like this are often called "living styleguides" or "storybooks". +Các trang như thế này thường được gọi là "living styleguides" hoặc "storybooks". </DeepDive> -### Step 2: Determine what triggers those state changes {/*step-2-determine-what-triggers-those-state-changes*/} +### Bước 2: Xác định điều gì kích hoạt những thay đổi trạng thái đó {/*step-2-determine-what-triggers-those-state-changes*/} -You can trigger state updates in response to two kinds of inputs: +Bạn có thể kích hoạt cập nhật trạng thái để phản hồi hai loại đầu vào: -* **Human inputs,** like clicking a button, typing in a field, navigating a link. -* **Computer inputs,** like a network response arriving, a timeout completing, an image loading. +* **Đầu vào của con người,** như nhấp vào nút, nhập vào một trường, điều hướng một liên kết. +* **Đầu vào của máy tính,** như phản hồi mạng đến, thời gian chờ hoàn thành, hình ảnh tải. <IllustrationBlock> <Illustration caption="Human inputs" alt="A finger." src="/images/docs/illustrations/i_inputs1.png" /> <Illustration caption="Computer inputs" alt="Ones and zeroes." src="/images/docs/illustrations/i_inputs2.png" /> </IllustrationBlock> -In both cases, **you must set [state variables](/learn/state-a-components-memory#anatomy-of-usestate) to update the UI.** For the form you're developing, you will need to change state in response to a few different inputs: +Trong cả hai trường hợp, **bạn phải đặt [biến trạng thái](/learn/state-a-components-memory#anatomy-of-usestate) để cập nhật UI.** Đối với biểu mẫu bạn đang phát triển, bạn sẽ cần thay đổi trạng thái để phản hồi một vài đầu vào khác nhau: -* **Changing the text input** (human) should switch it from the *Empty* state to the *Typing* state or back, depending on whether the text box is empty or not. -* **Clicking the Submit button** (human) should switch it to the *Submitting* state. -* **Successful network response** (computer) should switch it to the *Success* state. -* **Failed network response** (computer) should switch it to the *Error* state with the matching error message. +* **Thay đổi đầu vào văn bản** (con người) sẽ chuyển nó từ trạng thái *Trống* sang trạng thái *Đang nhập* hoặc ngược lại, tùy thuộc vào việc hộp văn bản có trống hay không. +* **Nhấp vào nút Gửi** (con người) sẽ chuyển nó sang trạng thái *Đang gửi*. +* **Phản hồi mạng thành công** (máy tính) sẽ chuyển nó sang trạng thái *Thành công*. +* **Phản hồi mạng không thành công** (máy tính) sẽ chuyển nó sang trạng thái *Lỗi* với thông báo lỗi phù hợp. <Note> -Notice that human inputs often require [event handlers](/learn/responding-to-events)! +Lưu ý rằng đầu vào của con người thường yêu cầu [trình xử lý sự kiện](/learn/responding-to-events)! </Note> -To help visualize this flow, try drawing each state on paper as a labeled circle, and each change between two states as an arrow. You can sketch out many flows this way and sort out bugs long before implementation. +Để giúp hình dung luồng này, hãy thử vẽ từng trạng thái trên giấy dưới dạng một vòng tròn được gắn nhãn và mỗi thay đổi giữa hai trạng thái dưới dạng một mũi tên. Bạn có thể phác thảo nhiều luồng theo cách này và sắp xếp các lỗi trước khi triển khai. <DiagramGroup> -<Diagram name="responding_to_input_flow" height={350} width={688} alt="Flow chart moving left to right with 5 nodes. The first node labeled 'empty' has one edge labeled 'start typing' connected to a node labeled 'typing'. That node has one edge labeled 'press submit' connected to a node labeled 'submitting', which has two edges. The left edge is labeled 'network error' connecting to a node labeled 'error'. The right edge is labeled 'network success' connecting to a node labeled 'success'."> +<Diagram name="responding_to_input_flow" height={350} width={688} alt="Lưu đồ di chuyển từ trái sang phải với 5 nút. Nút đầu tiên có nhãn 'trống' có một cạnh có nhãn 'bắt đầu nhập' được kết nối với một nút có nhãn 'đang nhập'. Nút đó có một cạnh có nhãn 'nhấn gửi' được kết nối với một nút có nhãn 'đang gửi', nút này có hai cạnh. Cạnh bên trái có nhãn 'lỗi mạng' kết nối với một nút có nhãn 'lỗi'. Cạnh bên phải có nhãn 'mạng thành công' kết nối với một nút có nhãn 'thành công'." > -Form states +Trạng thái biểu mẫu </Diagram> </DiagramGroup> -### Step 3: Represent the state in memory with `useState` {/*step-3-represent-the-state-in-memory-with-usestate*/} +### Bước 3: Biểu diễn trạng thái trong bộ nhớ bằng `useState` {/*step-3-represent-the-state-in-memory-with-usestate*/} -Next you'll need to represent the visual states of your component in memory with [`useState`.](/reference/react/useState) Simplicity is key: each piece of state is a "moving piece", and **you want as few "moving pieces" as possible.** More complexity leads to more bugs! +Tiếp theo, bạn sẽ cần biểu diễn các trạng thái hiển thị của component của bạn trong bộ nhớ bằng [`useState`.](/reference/react/useState) Sự đơn giản là chìa khóa: mỗi phần của trạng thái là một "mảnh ghép chuyển động" và **bạn muốn càng ít "mảnh ghép chuyển động" càng tốt.** Càng phức tạp thì càng có nhiều lỗi! -Start with the state that *absolutely must* be there. For example, you'll need to store the `answer` for the input, and the `error` (if it exists) to store the last error: +Bắt đầu với trạng thái *tuyệt đối phải* có ở đó. Ví dụ: bạn sẽ cần lưu trữ `answer` cho đầu vào và `error` (nếu có) để lưu trữ lỗi cuối cùng: ```js const [answer, setAnswer] = useState(''); const [error, setError] = useState(null); ``` -Then, you'll need a state variable representing which one of the visual states that you want to display. There's usually more than a single way to represent that in memory, so you'll need to experiment with it. +Sau đó, bạn sẽ cần một biến trạng thái đại diện cho một trong các trạng thái hiển thị mà bạn muốn hiển thị. Thường có nhiều hơn một cách để biểu diễn điều đó trong bộ nhớ, vì vậy bạn sẽ cần thử nghiệm với nó. -If you struggle to think of the best way immediately, start by adding enough state that you're *definitely* sure that all the possible visual states are covered: +Nếu bạn gặp khó khăn trong việc nghĩ ra cách tốt nhất ngay lập tức, hãy bắt đầu bằng cách thêm đủ trạng thái mà bạn *chắc chắn* rằng tất cả các trạng thái hiển thị có thể có đều được bao phủ: ```js const [isEmpty, setIsEmpty] = useState(true); @@ -371,19 +371,19 @@ const [isSuccess, setIsSuccess] = useState(false); const [isError, setIsError] = useState(false); ``` -Your first idea likely won't be the best, but that's ok--refactoring state is a part of the process! +Ý tưởng đầu tiên của bạn có thể không phải là tốt nhất, nhưng điều đó không sao--tái cấu trúc trạng thái là một phần của quy trình! -### Step 4: Remove any non-essential state variables {/*step-4-remove-any-non-essential-state-variables*/} +### Bước 4: Loại bỏ bất kỳ biến trạng thái không cần thiết nào {/*step-4-remove-any-non-essential-state-variables*/} -You want to avoid duplication in the state content so you're only tracking what is essential. Spending a little time on refactoring your state structure will make your components easier to understand, reduce duplication, and avoid unintended meanings. Your goal is to **prevent the cases where the state in memory doesn't represent any valid UI that you'd want a user to see.** (For example, you never want to show an error message and disable the input at the same time, or the user won't be able to correct the error!) +Bạn muốn tránh trùng lặp trong nội dung trạng thái để bạn chỉ theo dõi những gì cần thiết. Dành một chút thời gian để tái cấu trúc cấu trúc trạng thái của bạn sẽ giúp các component của bạn dễ hiểu hơn, giảm trùng lặp và tránh các ý nghĩa không mong muốn. Mục tiêu của bạn là **ngăn chặn các trường hợp trạng thái trong bộ nhớ không đại diện cho bất kỳ UI hợp lệ nào mà bạn muốn người dùng thấy.** (Ví dụ: bạn không bao giờ muốn hiển thị thông báo lỗi và tắt đầu vào cùng một lúc, nếu không người dùng sẽ không thể sửa lỗi!) -Here are some questions you can ask about your state variables: +Dưới đây là một số câu hỏi bạn có thể hỏi về các biến trạng thái của mình: -* **Does this state cause a paradox?** For example, `isTyping` and `isSubmitting` can't both be `true`. A paradox usually means that the state is not constrained enough. There are four possible combinations of two booleans, but only three correspond to valid states. To remove the "impossible" state, you can combine these into a `status` that must be one of three values: `'typing'`, `'submitting'`, or `'success'`. -* **Is the same information available in another state variable already?** Another paradox: `isEmpty` and `isTyping` can't be `true` at the same time. By making them separate state variables, you risk them going out of sync and causing bugs. Fortunately, you can remove `isEmpty` and instead check `answer.length === 0`. -* **Can you get the same information from the inverse of another state variable?** `isError` is not needed because you can check `error !== null` instead. +* **Trạng thái này có gây ra nghịch lý không?** Ví dụ: `isTyping` và `isSubmitting` không thể đồng thời là `true`. Một nghịch lý thường có nghĩa là trạng thái không đủ ràng buộc. Có bốn tổ hợp có thể có của hai boolean, nhưng chỉ ba tổ hợp tương ứng với các trạng thái hợp lệ. Để loại bỏ trạng thái "không thể", bạn có thể kết hợp chúng thành một `status` phải là một trong ba giá trị: `'typing'`, `'submitting'` hoặc `'success'`. +* **Thông tin tương tự đã có trong một biến trạng thái khác chưa?** Một nghịch lý khác: `isEmpty` và `isTyping` không thể đồng thời là `true`. Bằng cách tạo chúng thành các biến trạng thái riêng biệt, bạn có nguy cơ chúng không đồng bộ và gây ra lỗi. May mắn thay, bạn có thể loại bỏ `isEmpty` và thay vào đó kiểm tra `answer.length === 0`. +* **Bạn có thể nhận được thông tin tương tự từ nghịch đảo của một biến trạng thái khác không?** Không cần `isError` vì bạn có thể kiểm tra `error !== null` thay thế. -After this clean-up, you're left with 3 (down from 7!) *essential* state variables: +Sau khi dọn dẹp này, bạn còn lại 3 (giảm từ 7!) biến trạng thái *cần thiết*: ```js const [answer, setAnswer] = useState(''); @@ -391,19 +391,19 @@ const [error, setError] = useState(null); const [status, setStatus] = useState('typing'); // 'typing', 'submitting', or 'success' ``` -You know they are essential, because you can't remove any of them without breaking the functionality. +Bạn biết chúng là cần thiết, vì bạn không thể loại bỏ bất kỳ biến nào trong số chúng mà không làm hỏng chức năng. <DeepDive> -#### Eliminating “impossible” states with a reducer {/*eliminating-impossible-states-with-a-reducer*/} +#### Loại bỏ các trạng thái “không thể” bằng một reducer {/*eliminating-impossible-states-with-a-reducer*/} -These three variables are a good enough representation of this form's state. However, there are still some intermediate states that don't fully make sense. For example, a non-null `error` doesn't make sense when `status` is `'success'`. To model the state more precisely, you can [extract it into a reducer.](/learn/extracting-state-logic-into-a-reducer) Reducers let you unify multiple state variables into a single object and consolidate all the related logic! +Ba biến này là một biểu diễn đủ tốt về trạng thái của biểu mẫu này. Tuy nhiên, vẫn còn một số trạng thái trung gian không hoàn toàn có ý nghĩa. Ví dụ: `error` khác null không có ý nghĩa khi `status` là `'success'`. Để mô hình hóa trạng thái chính xác hơn, bạn có thể [trích xuất nó vào một reducer.](/learn/extracting-state-logic-into-a-reducer) Reducer cho phép bạn hợp nhất nhiều biến trạng thái thành một đối tượng duy nhất và hợp nhất tất cả logic liên quan! </DeepDive> -### Step 5: Connect the event handlers to set state {/*step-5-connect-the-event-handlers-to-set-state*/} +### Bước 5: Kết nối các trình xử lý sự kiện để đặt trạng thái {/*step-5-connect-the-event-handlers-to-set-state*/} -Lastly, create event handlers that update the state. Below is the final form, with all event handlers wired up: +Cuối cùng, tạo các trình xử lý sự kiện để cập nhật trạng thái. Dưới đây là biểu mẫu cuối cùng, với tất cả các trình xử lý sự kiện được kết nối: <Sandpack> @@ -485,29 +485,27 @@ function submitForm(answer) { </Sandpack> -Although this code is longer than the original imperative example, it is much less fragile. Expressing all interactions as state changes lets you later introduce new visual states without breaking existing ones. It also lets you change what should be displayed in each state without changing the logic of the interaction itself. +Mặc dù code này dài hơn ví dụ mệnh lệnh ban đầu, nhưng nó ít bị lỗi hơn nhiều. Việc thể hiện tất cả các tương tác như là các thay đổi trạng thái cho phép bạn giới thiệu các trạng thái hiển thị mới mà không làm hỏng các trạng thái hiện có. Nó cũng cho phép bạn thay đổi những gì sẽ được hiển thị trong mỗi trạng thái mà không thay đổi logic của chính tương tác đó. <Recap> -* Declarative programming means describing the UI for each visual state rather than micromanaging the UI (imperative). -* When developing a component: - 1. Identify all its visual states. - 2. Determine the human and computer triggers for state changes. - 3. Model the state with `useState`. - 4. Remove non-essential state to avoid bugs and paradoxes. - 5. Connect the event handlers to set state. +* Lập trình khai báo có nghĩa là mô tả giao diện người dùng cho mỗi trạng thái hiển thị thay vì quản lý chi tiết giao diện người dùng (mệnh lệnh). +* Khi phát triển một component: + 1. Xác định tất cả các trạng thái hiển thị của nó. + 2. Xác định các tác nhân kích hoạt thay đổi trạng thái từ con người và máy tính. + 3. Mô hình hóa trạng thái bằng `useState`. + 4. Loại bỏ trạng thái không cần thiết để tránh lỗi và nghịch lý. + 5. Kết nối các trình xử lý sự kiện để đặt trạng thái. </Recap> - - <Challenges> -#### Add and remove a CSS class {/*add-and-remove-a-css-class*/} +#### Thêm và xóa một lớp CSS {/*add-and-remove-a-css-class*/} -Make it so that clicking on the picture *removes* the `background--active` CSS class from the outer `<div>`, but *adds* the `picture--active` class to the `<img>`. Clicking the background again should restore the original CSS classes. +Làm cho việc nhấp vào hình ảnh *xóa* lớp CSS `background--active` khỏi `<div>` bên ngoài, nhưng *thêm* lớp `picture--active` vào `<img>`. Nhấp lại vào nền sẽ khôi phục các lớp CSS ban đầu. -Visually, you should expect that clicking on the picture removes the purple background and highlights the picture border. Clicking outside the picture highlights the background, but removes the picture border highlight. +Về mặt hình ảnh, bạn nên mong đợi rằng việc nhấp vào hình ảnh sẽ loại bỏ nền màu tím và làm nổi bật đường viền của hình ảnh. Nhấp vào bên ngoài hình ảnh sẽ làm nổi bật nền, nhưng loại bỏ điểm nổi bật của đường viền hình ảnh. <Sandpack> @@ -557,14 +555,14 @@ body { margin: 0; padding: 0; height: 250px; } <Solution> -This component has two visual states: when the image is active, and when the image is inactive: +Component này có hai trạng thái hiển thị: khi hình ảnh hoạt động và khi hình ảnh không hoạt động: -* When the image is active, the CSS classes are `background` and `picture picture--active`. -* When the image is inactive, the CSS classes are `background background--active` and `picture`. +* Khi hình ảnh hoạt động, các lớp CSS là `background` và `picture picture--active`. +* Khi hình ảnh không hoạt động, các lớp CSS là `background background--active` và `picture`. -A single boolean state variable is enough to remember whether the image is active. The original task was to remove or add CSS classes. However, in React you need to *describe* what you want to see rather than *manipulate* the UI elements. So you need to calculate both CSS classes based on the current state. You also need to [stop the propagation](/learn/responding-to-events#stopping-propagation) so that clicking the image doesn't register as a click on the background. +Một biến trạng thái boolean duy nhất là đủ để ghi nhớ xem hình ảnh có hoạt động hay không. Nhiệm vụ ban đầu là xóa hoặc thêm các lớp CSS. Tuy nhiên, trong React, bạn cần *mô tả* những gì bạn muốn thấy thay vì *thao tác* các phần tử giao diện người dùng. Vì vậy, bạn cần tính toán cả hai lớp CSS dựa trên trạng thái hiện tại. Bạn cũng cần [ngăn chặn sự lan truyền](/learn/responding-to-events#stopping-propagation) để việc nhấp vào hình ảnh không được đăng ký là một cú nhấp vào nền. -Verify that this version works by clicking the image and then outside of it: +Xác minh rằng phiên bản này hoạt động bằng cách nhấp vào hình ảnh và sau đó bên ngoài nó: <Sandpack> @@ -631,7 +629,7 @@ body { margin: 0; padding: 0; height: 250px; } </Sandpack> -Alternatively, you could return two separate chunks of JSX: +Ngoài ra, bạn có thể trả về hai đoạn JSX riêng biệt: <Sandpack> @@ -698,13 +696,13 @@ body { margin: 0; padding: 0; height: 250px; } </Sandpack> -Keep in mind that if two different JSX chunks describe the same tree, their nesting (first `<div>` → first `<img>`) has to line up. Otherwise, toggling `isActive` would recreate the whole tree below and [reset its state.](/learn/preserving-and-resetting-state) This is why, if a similar JSX tree gets returned in both cases, it is better to write them as a single piece of JSX. +Hãy nhớ rằng nếu hai đoạn JSX khác nhau mô tả cùng một cây, thì sự lồng nhau của chúng (thẻ `<div>` đầu tiên → thẻ `<img>` đầu tiên) phải thẳng hàng. Nếu không, việc chuyển đổi `isActive` sẽ tạo lại toàn bộ cây bên dưới và [đặt lại trạng thái của nó.](/learn/preserving-and-resetting-state) Đây là lý do tại sao, nếu một cây JSX tương tự được trả về trong cả hai trường hợp, thì tốt hơn là viết chúng dưới dạng một đoạn JSX duy nhất. </Solution> -#### Profile editor {/*profile-editor*/} +#### Trình chỉnh sửa hồ sơ {/*profile-editor*/} -Here is a small form implemented with plain JavaScript and DOM. Play with it to understand its behavior: +Đây là một biểu mẫu nhỏ được triển khai bằng JavaScript và DOM thuần túy. Hãy chơi với nó để hiểu hành vi của nó: <Sandpack> @@ -801,11 +799,11 @@ label { display: block; margin-bottom: 20px; } </Sandpack> -This form switches between two modes: in the editing mode, you see the inputs, and in the viewing mode, you only see the result. The button label changes between "Edit" and "Save" depending on the mode you're in. When you change the inputs, the welcome message at the bottom updates in real time. +Biểu mẫu này chuyển đổi giữa hai chế độ: ở chế độ chỉnh sửa, bạn thấy các trường nhập liệu và ở chế độ xem, bạn chỉ thấy kết quả. Nhãn nút thay đổi giữa "Chỉnh sửa" và "Lưu" tùy thuộc vào chế độ bạn đang ở. Khi bạn thay đổi các trường nhập liệu, thông báo chào mừng ở phía dưới sẽ cập nhật theo thời gian thực. -Your task is to reimplement it in React in the sandbox below. For your convenience, the markup was already converted to JSX, but you'll need to make it show and hide the inputs like the original does. +Nhiệm vụ của bạn là triển khai lại nó trong React trong sandbox bên dưới. Để thuận tiện cho bạn, đánh dấu đã được chuyển đổi sang JSX, nhưng bạn sẽ cần làm cho nó hiển thị và ẩn các trường nhập liệu như bản gốc. -Make sure that it updates the text at the bottom, too! +Đảm bảo rằng nó cũng cập nhật văn bản ở phía dưới! <Sandpack> @@ -840,9 +838,9 @@ label { display: block; margin-bottom: 20px; } <Solution> -You will need two state variables to hold the input values: `firstName` and `lastName`. You're also going to need an `isEditing` state variable that holds whether to display the inputs or not. You should _not_ need a `fullName` variable because the full name can always be calculated from the `firstName` and the `lastName`. +Bạn sẽ cần hai biến trạng thái để giữ các giá trị đầu vào: `firstName` và `lastName`. Bạn cũng sẽ cần một biến trạng thái `isEditing` để biết có hiển thị các trường nhập liệu hay không. Bạn _không_ nên cần một biến `fullName` vì tên đầy đủ luôn có thể được tính từ `firstName` và `lastName`. -Finally, you should use [conditional rendering](/learn/conditional-rendering) to show or hide the inputs depending on `isEditing`. +Cuối cùng, bạn nên sử dụng [kết xuất có điều kiện](/learn/conditional-rendering) để hiển thị hoặc ẩn các trường nhập liệu tùy thuộc vào `isEditing`. <Sandpack> @@ -900,13 +898,13 @@ label { display: block; margin-bottom: 20px; } </Sandpack> -Compare this solution to the original imperative code. How are they different? +So sánh giải pháp này với mã mệnh lệnh ban đầu. Chúng khác nhau như thế nào? </Solution> -#### Refactor the imperative solution without React {/*refactor-the-imperative-solution-without-react*/} +#### Tái cấu trúc giải pháp mệnh lệnh mà không cần React {/*refactor-the-imperative-solution-without-react*/} -Here is the original sandbox from the previous challenge, written imperatively without React: +Đây là sandbox ban đầu từ thử thách trước, được viết theo kiểu mệnh lệnh mà không cần React: <Sandpack> @@ -1003,9 +1001,9 @@ label { display: block; margin-bottom: 20px; } </Sandpack> -Imagine React didn't exist. Can you refactor this code in a way that makes the logic less fragile and more similar to the React version? What would it look like if the state was explicit, like in React? +Hãy tưởng tượng React không tồn tại. Bạn có thể tái cấu trúc mã này theo cách làm cho logic ít bị lỗi hơn và tương tự như phiên bản React hơn không? Nó sẽ trông như thế nào nếu trạng thái là rõ ràng, giống như trong React? -If you're struggling to think where to start, the stub below already has most of the structure in place. If you start here, fill in the missing logic in the `updateDOM` function. (Refer to the original code where needed.) +Nếu bạn đang gặp khó khăn trong việc suy nghĩ về nơi bắt đầu, thì đoạn mã dưới đây đã có hầu hết cấu trúc tại chỗ. Nếu bạn bắt đầu từ đây, hãy điền vào logic còn thiếu trong hàm `updateDOM`. (Tham khảo mã gốc khi cần.) <Sandpack> @@ -1228,8 +1226,7 @@ label { display: block; margin-bottom: 20px; } ``` </Sandpack> - -The `updateDOM` function you wrote shows what React does under the hood when you set the state. (However, React also avoids touching the DOM for properties that have not changed since the last time they were set.) +Hàm `updateDOM` mà bạn đã viết cho thấy những gì React thực hiện bên dưới khi bạn đặt trạng thái. (Tuy nhiên, React cũng tránh chạm vào DOM đối với các thuộc tính không thay đổi kể từ lần cuối cùng chúng được đặt.) </Solution> diff --git a/src/content/learn/referencing-values-with-refs.md b/src/content/learn/referencing-values-with-refs.md index 4faf18786..4c9eded9d 100644 --- a/src/content/learn/referencing-values-with-refs.md +++ b/src/content/learn/referencing-values-with-refs.md @@ -1,49 +1,49 @@ --- -title: 'Referencing Values with Refs' +title: 'Tham chiếu các giá trị với Refs' --- <Intro> -When you want a component to "remember" some information, but you don't want that information to [trigger new renders](/learn/render-and-commit), you can use a *ref*. +Khi bạn muốn một component "ghi nhớ" một số thông tin, nhưng bạn không muốn thông tin đó [kích hoạt các lần render mới](/learn/render-and-commit), bạn có thể sử dụng *ref*. </Intro> <YouWillLearn> -- How to add a ref to your component -- How to update a ref's value -- How refs are different from state -- How to use refs safely +- Cách thêm một ref vào component của bạn +- Cách cập nhật giá trị của một ref +- Sự khác biệt giữa refs và state +- Cách sử dụng refs một cách an toàn </YouWillLearn> -## Adding a ref to your component {/*adding-a-ref-to-your-component*/} +## Thêm một ref vào component của bạn {/*adding-a-ref-to-your-component*/} -You can add a ref to your component by importing the `useRef` Hook from React: +Bạn có thể thêm một ref vào component của mình bằng cách import Hook `useRef` từ React: ```js import { useRef } from 'react'; ``` -Inside your component, call the `useRef` Hook and pass the initial value that you want to reference as the only argument. For example, here is a ref to the value `0`: +Bên trong component của bạn, hãy gọi Hook `useRef` và truyền giá trị ban đầu mà bạn muốn tham chiếu làm đối số duy nhất. Ví dụ: đây là một ref đến giá trị `0`: ```js const ref = useRef(0); ``` -`useRef` returns an object like this: +`useRef` trả về một đối tượng như thế này: ```js { - current: 0 // The value you passed to useRef + current: 0 // Giá trị bạn đã truyền cho useRef } ``` -<Illustration src="/images/docs/illustrations/i_ref.png" alt="An arrow with 'current' written on it stuffed into a pocket with 'ref' written on it." /> +<Illustration src="/images/docs/illustrations/i_ref.png" alt="Một mũi tên có chữ 'current' được viết trên đó nhét vào một túi có chữ 'ref' được viết trên đó." /> -You can access the current value of that ref through the `ref.current` property. This value is intentionally mutable, meaning you can both read and write to it. It's like a secret pocket of your component that React doesn't track. (This is what makes it an "escape hatch" from React's one-way data flow--more on that below!) +Bạn có thể truy cập giá trị hiện tại của ref đó thông qua thuộc tính `ref.current`. Giá trị này có thể thay đổi một cách có chủ ý, có nghĩa là bạn có thể đọc và ghi vào nó. Nó giống như một túi bí mật của component mà React không theo dõi. (Đây là điều làm cho nó trở thành một "lối thoát" khỏi luồng dữ liệu một chiều của React--thêm về điều đó bên dưới!) -Here, a button will increment `ref.current` on every click: +Ở đây, một nút sẽ tăng `ref.current` trên mỗi lần nhấp: <Sandpack> @@ -55,12 +55,12 @@ export default function Counter() { function handleClick() { ref.current = ref.current + 1; - alert('You clicked ' + ref.current + ' times!'); + alert('Bạn đã nhấp ' + ref.current + ' lần!'); } return ( <button onClick={handleClick}> - Click me! + Nhấp vào tôi! </button> ); } @@ -68,20 +68,20 @@ export default function Counter() { </Sandpack> -The ref points to a number, but, like [state](/learn/state-a-components-memory), you could point to anything: a string, an object, or even a function. Unlike state, ref is a plain JavaScript object with the `current` property that you can read and modify. +Ref trỏ đến một số, nhưng, giống như [state](/learn/state-a-components-memory), bạn có thể trỏ đến bất cứ thứ gì: một chuỗi, một đối tượng hoặc thậm chí một hàm. Không giống như state, ref là một đối tượng JavaScript thuần túy với thuộc tính `current` mà bạn có thể đọc và sửa đổi. -Note that **the component doesn't re-render with every increment.** Like state, refs are retained by React between re-renders. However, setting state re-renders a component. Changing a ref does not! +Lưu ý rằng **component không re-render với mỗi lần tăng.** Giống như state, refs được React giữ lại giữa các lần re-render. Tuy nhiên, việc đặt state sẽ re-render một component. Thay đổi một ref thì không! -## Example: building a stopwatch {/*example-building-a-stopwatch*/} +## Ví dụ: xây dựng đồng hồ bấm giờ {/*example-building-a-stopwatch*/} -You can combine refs and state in a single component. For example, let's make a stopwatch that the user can start or stop by pressing a button. In order to display how much time has passed since the user pressed "Start", you will need to keep track of when the Start button was pressed and what the current time is. **This information is used for rendering, so you'll keep it in state:** +Bạn có thể kết hợp refs và state trong một component duy nhất. Ví dụ: hãy tạo một đồng hồ bấm giờ mà người dùng có thể bắt đầu hoặc dừng bằng cách nhấn một nút. Để hiển thị thời gian đã trôi qua kể từ khi người dùng nhấn "Bắt đầu", bạn sẽ cần theo dõi thời điểm nút Bắt đầu được nhấn và thời gian hiện tại là bao nhiêu. **Thông tin này được sử dụng để render, vì vậy bạn sẽ giữ nó trong state:** ```js const [startTime, setStartTime] = useState(null); const [now, setNow] = useState(null); ``` -When the user presses "Start", you'll use [`setInterval`](https://developer.mozilla.org/docs/Web/API/setInterval) in order to update the time every 10 milliseconds: +Khi người dùng nhấn "Bắt đầu", bạn sẽ sử dụng [`setInterval`](https://developer.mozilla.org/docs/Web/API/setInterval) để cập nhật thời gian sau mỗi 10 mili giây: <Sandpack> @@ -93,12 +93,12 @@ export default function Stopwatch() { const [now, setNow] = useState(null); function handleStart() { - // Start counting. + // Bắt đầu đếm. setStartTime(Date.now()); setNow(Date.now()); setInterval(() => { - // Update the current time every 10ms. + // Cập nhật thời gian hiện tại sau mỗi 10ms. setNow(Date.now()); }, 10); } @@ -110,9 +110,9 @@ export default function Stopwatch() { return ( <> - <h1>Time passed: {secondsPassed.toFixed(3)}</h1> + <h1>Thời gian đã trôi qua: {secondsPassed.toFixed(3)}</h1> <button onClick={handleStart}> - Start + Bắt đầu </button> </> ); @@ -121,7 +121,7 @@ export default function Stopwatch() { </Sandpack> -When the "Stop" button is pressed, you need to cancel the existing interval so that it stops updating the `now` state variable. You can do this by calling [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval), but you need to give it the interval ID that was previously returned by the `setInterval` call when the user pressed Start. You need to keep the interval ID somewhere. **Since the interval ID is not used for rendering, you can keep it in a ref:** +Khi nút "Dừng" được nhấn, bạn cần hủy interval hiện có để nó ngừng cập nhật biến state `now`. Bạn có thể thực hiện việc này bằng cách gọi [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval), nhưng bạn cần cung cấp cho nó ID interval đã được trả về trước đó bởi lệnh gọi `setInterval` khi người dùng nhấn Bắt đầu. Bạn cần giữ ID interval ở đâu đó. **Vì ID interval không được sử dụng để render, bạn có thể giữ nó trong một ref:** <Sandpack> @@ -154,12 +154,12 @@ export default function Stopwatch() { return ( <> - <h1>Time passed: {secondsPassed.toFixed(3)}</h1> + <h1>Thời gian đã trôi qua: {secondsPassed.toFixed(3)}</h1> <button onClick={handleStart}> - Start + Bắt đầu </button> <button onClick={handleStop}> - Stop + Dừng </button> </> ); @@ -168,20 +168,20 @@ export default function Stopwatch() { </Sandpack> -When a piece of information is used for rendering, keep it in state. When a piece of information is only needed by event handlers and changing it doesn't require a re-render, using a ref may be more efficient. +Khi một phần thông tin được sử dụng để render, hãy giữ nó trong state. Khi một phần thông tin chỉ cần thiết cho các trình xử lý sự kiện và việc thay đổi nó không yêu cầu re-render, thì việc sử dụng ref có thể hiệu quả hơn. -## Differences between refs and state {/*differences-between-refs-and-state*/} +## Sự khác biệt giữa refs và state {/*differences-between-refs-and-state*/} -Perhaps you're thinking refs seem less "strict" than state—you can mutate them instead of always having to use a state setting function, for instance. But in most cases, you'll want to use state. Refs are an "escape hatch" you won't need often. Here's how state and refs compare: +Có lẽ bạn đang nghĩ rằng refs có vẻ ít "nghiêm ngặt" hơn state—ví dụ: bạn có thể thay đổi chúng thay vì luôn phải sử dụng một hàm thiết lập state. Nhưng trong hầu hết các trường hợp, bạn sẽ muốn sử dụng state. Refs là một "lối thoát" mà bạn sẽ không cần thường xuyên. Đây là cách so sánh state và refs: | refs | state | | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| `useRef(initialValue)` returns `{ current: initialValue }` | `useState(initialValue)` returns the current value of a state variable and a state setter function ( `[value, setValue]`) | -| Doesn't trigger re-render when you change it. | Triggers re-render when you change it. | -| Mutable—you can modify and update `current`'s value outside of the rendering process. | "Immutable"—you must use the state setting function to modify state variables to queue a re-render. | -| You shouldn't read (or write) the `current` value during rendering. | You can read state at any time. However, each render has its own [snapshot](/learn/state-as-a-snapshot) of state which does not change. +| `useRef(initialValue)` trả về `{ current: initialValue }` | `useState(initialValue)` trả về giá trị hiện tại của một biến state và một hàm thiết lập state ( `[value, setValue]`) | +| Không kích hoạt re-render khi bạn thay đổi nó. | Kích hoạt re-render khi bạn thay đổi nó. | +| Có thể thay đổi—bạn có thể sửa đổi và cập nhật giá trị của `current` bên ngoài quá trình render. | "Bất biến"—bạn phải sử dụng hàm thiết lập state để sửa đổi các biến state để xếp hàng đợi re-render. | +| Bạn không nên đọc (hoặc ghi) giá trị `current` trong quá trình render. | Bạn có thể đọc state bất cứ lúc nào. Tuy nhiên, mỗi lần render có [ảnh chụp nhanh](/learn/state-as-a-snapshot) riêng của state mà không thay đổi. -Here is a counter button that's implemented with state: +Đây là một nút đếm được triển khai bằng state: <Sandpack> @@ -197,7 +197,7 @@ export default function Counter() { return ( <button onClick={handleClick}> - You clicked {count} times + Bạn đã nhấp {count} lần </button> ); } @@ -205,9 +205,9 @@ export default function Counter() { </Sandpack> -Because the `count` value is displayed, it makes sense to use a state value for it. When the counter's value is set with `setCount()`, React re-renders the component and the screen updates to reflect the new count. +Vì giá trị `count` được hiển thị, nên việc sử dụng giá trị state cho nó là hợp lý. Khi giá trị của bộ đếm được đặt bằng `setCount()`, React sẽ re-render component và màn hình sẽ cập nhật để phản ánh số lượng mới. -If you tried to implement this with a ref, React would never re-render the component, so you'd never see the count change! See how clicking this button **does not update its text**: +Nếu bạn cố gắng triển khai điều này với một ref, React sẽ không bao giờ re-render component, vì vậy bạn sẽ không bao giờ thấy số lượng thay đổi! Xem cách nhấp vào nút này **không cập nhật văn bản của nó**: <Sandpack> @@ -218,13 +218,13 @@ export default function Counter() { let countRef = useRef(0); function handleClick() { - // This doesn't re-render the component! + // Điều này không re-render component! countRef.current = countRef.current + 1; } return ( <button onClick={handleClick}> - You clicked {countRef.current} times + Bạn đã nhấp {countRef.current} lần </button> ); } @@ -232,82 +232,80 @@ export default function Counter() { </Sandpack> -This is why reading `ref.current` during render leads to unreliable code. If you need that, use state instead. +Đây là lý do tại sao việc đọc `ref.current` trong quá trình render dẫn đến mã không đáng tin cậy. Nếu bạn cần điều đó, hãy sử dụng state thay thế. <DeepDive> -#### How does useRef work inside? {/*how-does-use-ref-work-inside*/} +#### useRef hoạt động như thế nào bên trong? {/*how-does-use-ref-work-inside*/} -Although both `useState` and `useRef` are provided by React, in principle `useRef` could be implemented _on top of_ `useState`. You can imagine that inside of React, `useRef` is implemented like this: +Mặc dù cả `useState` và `useRef` đều được cung cấp bởi React, nhưng về nguyên tắc, `useRef` có thể được triển khai _trên_ `useState`. Bạn có thể tưởng tượng rằng bên trong React, `useRef` được triển khai như thế này: ```js -// Inside of React +// Bên trong React function useRef(initialValue) { const [ref, unused] = useState({ current: initialValue }); return ref; } ``` -During the first render, `useRef` returns `{ current: initialValue }`. This object is stored by React, so during the next render the same object will be returned. Note how the state setter is unused in this example. It is unnecessary because `useRef` always needs to return the same object! +Trong lần render đầu tiên, `useRef` trả về `{ current: initialValue }`. Đối tượng này được React lưu trữ, vì vậy trong lần render tiếp theo, cùng một đối tượng sẽ được trả về. Lưu ý cách setter state không được sử dụng trong ví dụ này. Nó là không cần thiết vì `useRef` luôn cần trả về cùng một đối tượng! -React provides a built-in version of `useRef` because it is common enough in practice. But you can think of it as a regular state variable without a setter. If you're familiar with object-oriented programming, refs might remind you of instance fields--but instead of `this.something` you write `somethingRef.current`. +React cung cấp một phiên bản tích hợp của `useRef` vì nó đủ phổ biến trong thực tế. Nhưng bạn có thể coi nó như một biến state thông thường mà không có setter. Nếu bạn quen thuộc với lập trình hướng đối tượng, refs có thể nhắc bạn về các trường instance--nhưng thay vì `this.something`, bạn viết `somethingRef.current`. </DeepDive> -## When to use refs {/*when-to-use-refs*/} +## Khi nào nên sử dụng refs {/*when-to-use-refs*/} -Typically, you will use a ref when your component needs to "step outside" React and communicate with external APIs—often a browser API that won't impact the appearance of the component. Here are a few of these rare situations: +Thông thường, bạn sẽ sử dụng ref khi component của bạn cần "bước ra ngoài" React và giao tiếp với các API bên ngoài—thường là một API trình duyệt sẽ không ảnh hưởng đến giao diện của component. Dưới đây là một vài trong số những tình huống hiếm gặp này: -- Storing [timeout IDs](https://developer.mozilla.org/docs/Web/API/setTimeout) -- Storing and manipulating [DOM elements](https://developer.mozilla.org/docs/Web/API/Element), which we cover on [the next page](/learn/manipulating-the-dom-with-refs) -- Storing other objects that aren't necessary to calculate the JSX. +- Lưu trữ [ID timeout](https://developer.mozilla.org/docs/Web/API/setTimeout) +- Lưu trữ và thao tác [các phần tử DOM](https://developer.mozilla.org/docs/Web/API/Element), mà chúng ta sẽ đề cập trên [trang tiếp theo](/learn/manipulating-the-dom-with-refs) +- Lưu trữ các đối tượng khác không cần thiết để tính toán JSX. -If your component needs to store some value, but it doesn't impact the rendering logic, choose refs. +Nếu component của bạn cần lưu trữ một số giá trị, nhưng nó không ảnh hưởng đến logic render, hãy chọn refs. -## Best practices for refs {/*best-practices-for-refs*/} +## Các phương pháp hay nhất cho refs {/*best-practices-for-refs*/} -Following these principles will make your components more predictable: +Tuân theo các nguyên tắc này sẽ làm cho các component của bạn dễ đoán hơn: -- **Treat refs as an escape hatch.** Refs are useful when you work with external systems or browser APIs. If much of your application logic and data flow relies on refs, you might want to rethink your approach. -- **Don't read or write `ref.current` during rendering.** If some information is needed during rendering, use [state](/learn/state-a-components-memory) instead. Since React doesn't know when `ref.current` changes, even reading it while rendering makes your component's behavior difficult to predict. (The only exception to this is code like `if (!ref.current) ref.current = new Thing()` which only sets the ref once during the first render.) +- **Coi refs như một lối thoát.** Refs rất hữu ích khi bạn làm việc với các hệ thống bên ngoài hoặc API trình duyệt. Nếu phần lớn logic ứng dụng và luồng dữ liệu của bạn dựa vào refs, bạn có thể muốn xem xét lại cách tiếp cận của mình. +- **Không đọc hoặc ghi `ref.current` trong quá trình render.** Nếu một số thông tin là cần thiết trong quá trình render, hãy sử dụng [state](/learn/state-a-components-memory) thay thế. Vì React không biết khi nào `ref.current` thay đổi, ngay cả việc đọc nó trong khi render cũng khiến hành vi của component của bạn khó dự đoán. (Ngoại lệ duy nhất cho điều này là mã như `if (!ref.current) ref.current = new Thing()` chỉ đặt ref một lần trong lần render đầu tiên.) -Limitations of React state don't apply to refs. For example, state acts like a [snapshot for every render](/learn/state-as-a-snapshot) and [doesn't update synchronously.](/learn/queueing-a-series-of-state-updates) But when you mutate the current value of a ref, it changes immediately: +Các hạn chế của state React không áp dụng cho refs. Ví dụ: state hoạt động như một [ảnh chụp nhanh cho mỗi lần render](/learn/state-as-a-snapshot) và [không cập nhật đồng bộ.](/learn/queueing-a-series-of-state-updates) Nhưng khi bạn thay đổi giá trị hiện tại của một ref, nó sẽ thay đổi ngay lập tức: ```js ref.current = 5; console.log(ref.current); // 5 ``` -This is because **the ref itself is a regular JavaScript object,** and so it behaves like one. +Điều này là do **bản thân ref là một đối tượng JavaScript thông thường,** và do đó nó hoạt động như một đối tượng. -You also don't need to worry about [avoiding mutation](/learn/updating-objects-in-state) when you work with a ref. As long as the object you're mutating isn't used for rendering, React doesn't care what you do with the ref or its contents. +Bạn cũng không cần phải lo lắng về việc [tránh đột biến](/learn/updating-objects-in-state) khi bạn làm việc với một ref. Miễn là đối tượng bạn đang đột biến không được sử dụng để render, React không quan tâm bạn làm gì với ref hoặc nội dung của nó. -## Refs and the DOM {/*refs-and-the-dom*/} +## Refs và DOM {/*refs-and-the-dom*/} -You can point a ref to any value. However, the most common use case for a ref is to access a DOM element. For example, this is handy if you want to focus an input programmatically. When you pass a ref to a `ref` attribute in JSX, like `<div ref={myRef}>`, React will put the corresponding DOM element into `myRef.current`. Once the element is removed from the DOM, React will update `myRef.current` to be `null`. You can read more about this in [Manipulating the DOM with Refs.](/learn/manipulating-the-dom-with-refs) +Bạn có thể trỏ một ref đến bất kỳ giá trị nào. Tuy nhiên, trường hợp sử dụng phổ biến nhất cho một ref là truy cập một phần tử DOM. Ví dụ: điều này rất hữu ích nếu bạn muốn tập trung vào một đầu vào theo chương trình. Khi bạn chuyển một ref đến một thuộc tính `ref` trong JSX, như `<div ref={myRef}>`, React sẽ đặt phần tử DOM tương ứng vào `myRef.current`. Khi phần tử bị xóa khỏi DOM, React sẽ cập nhật `myRef.current` thành `null`. Bạn có thể đọc thêm về điều này trong [Thao tác DOM với Refs.](/learn/manipulating-the-dom-with-refs) <Recap> -- Refs are an escape hatch to hold onto values that aren't used for rendering. You won't need them often. -- A ref is a plain JavaScript object with a single property called `current`, which you can read or set. -- You can ask React to give you a ref by calling the `useRef` Hook. -- Like state, refs let you retain information between re-renders of a component. -- Unlike state, setting the ref's `current` value does not trigger a re-render. -- Don't read or write `ref.current` during rendering. This makes your component hard to predict. +- Refs là một lối thoát để giữ các giá trị không được sử dụng để render. Bạn sẽ không cần chúng thường xuyên. +- Một ref là một đối tượng JavaScript thuần túy với một thuộc tính duy nhất có tên là `current`, mà bạn có thể đọc hoặc đặt. +- Bạn có thể yêu cầu React cung cấp cho bạn một ref bằng cách gọi Hook `useRef`. +- Giống như state, refs cho phép bạn giữ lại thông tin giữa các lần re-render của một component. +- Không giống như state, việc đặt giá trị `current` của ref không kích hoạt re-render. +- Không đọc hoặc ghi `ref.current` trong quá trình render. Điều này làm cho component của bạn khó dự đoán. </Recap> - - <Challenges> -#### Fix a broken chat input {/*fix-a-broken-chat-input*/} +#### Sửa một đầu vào trò chuyện bị hỏng {/*fix-a-broken-chat-input*/} -Type a message and click "Send". You will notice there is a three second delay before you see the "Sent!" alert. During this delay, you can see an "Undo" button. Click it. This "Undo" button is supposed to stop the "Sent!" message from appearing. It does this by calling [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout) for the timeout ID saved during `handleSend`. However, even after "Undo" is clicked, the "Sent!" message still appears. Find why it doesn't work, and fix it. +Nhập một tin nhắn và nhấp vào "Gửi". Bạn sẽ nhận thấy có một độ trễ ba giây trước khi bạn thấy cảnh báo "Đã gửi!". Trong thời gian trễ này, bạn có thể thấy nút "Hoàn tác". Nhấp vào nó. Nút "Hoàn tác" này được cho là để ngăn thông báo "Đã gửi!" xuất hiện. Nó thực hiện điều này bằng cách gọi [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout) cho ID timeout được lưu trong `handleSend`. Tuy nhiên, ngay cả sau khi nhấp vào "Hoàn tác", thông báo "Đã gửi!" vẫn xuất hiện. Tìm lý do tại sao nó không hoạt động và sửa nó. <Hint> -Regular variables like `let timeoutID` don't "survive" between re-renders because every render runs your component (and initializes its variables) from scratch. Should you keep the timeout ID somewhere else? +Các biến thông thường như `let timeoutID` không "sống sót" giữa các lần re-render vì mỗi lần render chạy component của bạn (và khởi tạo các biến của nó) từ đầu. Bạn có nên giữ ID timeout ở một nơi khác không? </Hint> @@ -324,7 +322,7 @@ export default function Chat() { function handleSend() { setIsSending(true); timeoutID = setTimeout(() => { - alert('Sent!'); + alert('Đã gửi!'); setIsSending(false); }, 3000); } @@ -344,11 +342,11 @@ export default function Chat() { <button disabled={isSending} onClick={handleSend}> - {isSending ? 'Sending...' : 'Send'} + {isSending ? 'Đang gửi...' : 'Gửi'} </button> {isSending && <button onClick={handleUndo}> - Undo + Hoàn tác </button> } </> @@ -360,7 +358,7 @@ export default function Chat() { <Solution> -Whenever your component re-renders (such as when you set state), all local variables get initialized from scratch. This is why you can't save the timeout ID in a local variable like `timeoutID` and then expect another event handler to "see" it in the future. Instead, store it in a ref, which React will preserve between renders. +Bất cứ khi nào component của bạn re-render (chẳng hạn như khi bạn đặt state), tất cả các biến cục bộ sẽ được khởi tạo từ đầu. Đây là lý do tại sao bạn không thể lưu ID timeout trong một biến cục bộ như `timeoutID` và sau đó mong đợi một trình xử lý sự kiện khác "nhìn thấy" nó trong tương lai. Thay vào đó, hãy lưu trữ nó trong một ref, mà React sẽ giữ lại giữa các lần render. <Sandpack> @@ -375,7 +373,7 @@ export default function Chat() { function handleSend() { setIsSending(true); timeoutRef.current = setTimeout(() => { - alert('Sent!'); + alert('Đã gửi!'); setIsSending(false); }, 3000); } @@ -395,11 +393,11 @@ export default function Chat() { <button disabled={isSending} onClick={handleSend}> - {isSending ? 'Sending...' : 'Send'} + {isSending ? 'Đang gửi...' : 'Gửi'} </button> {isSending && <button onClick={handleUndo}> - Undo + Hoàn tác </button> } </> @@ -411,10 +409,9 @@ export default function Chat() { </Solution> +#### Sửa một component không re-render {/*fix-a-component-failing-to-re-render*/} -#### Fix a component failing to re-render {/*fix-a-component-failing-to-re-render*/} - -This button is supposed to toggle between showing "On" and "Off". However, it always shows "Off". What is wrong with this code? Fix it. +Nút này được cho là chuyển đổi giữa hiển thị "Bật" và "Tắt". Tuy nhiên, nó luôn hiển thị "Tắt". Có gì sai với mã này? Sửa nó. <Sandpack> @@ -428,7 +425,7 @@ export default function Toggle() { <button onClick={() => { isOnRef.current = !isOnRef.current; }}> - {isOnRef.current ? 'On' : 'Off'} + {isOnRef.current ? 'Bật' : 'Tắt'} </button> ); } @@ -438,7 +435,7 @@ export default function Toggle() { <Solution> -In this example, the current value of a ref is used to calculate the rendering output: `{isOnRef.current ? 'On' : 'Off'}`. This is a sign that this information should not be in a ref, and should have instead been put in state. To fix it, remove the ref and use state instead: +Trong ví dụ này, giá trị hiện tại của một ref được sử dụng để tính toán đầu ra render: `{isOnRef.current ? 'Bật' : 'Tắt'}`. Đây là một dấu hiệu cho thấy thông tin này không nên ở trong một ref, và thay vào đó nên được đưa vào state. Để sửa nó, hãy xóa ref và sử dụng state thay thế: <Sandpack> @@ -452,7 +449,7 @@ export default function Toggle() { <button onClick={() => { setIsOn(!isOn); }}> - {isOn ? 'On' : 'Off'} + {isOn ? 'Bật' : 'Tắt'} </button> ); } @@ -462,17 +459,17 @@ export default function Toggle() { </Solution> -#### Fix debouncing {/*fix-debouncing*/} +#### Sửa lỗi debouncing {/*fix-debouncing*/} -In this example, all button click handlers are ["debounced".](https://redd.one/blog/debounce-vs-throttle) To see what this means, press one of the buttons. Notice how the message appears a second later. If you press the button while waiting for the message, the timer will reset. So if you keep clicking the same button fast many times, the message won't appear until a second *after* you stop clicking. Debouncing lets you delay some action until the user "stops doing things". +Trong ví dụ này, tất cả các trình xử lý nhấp vào nút đều được ["debounced".](https://redd.one/blog/debounce-vs-throttle) Để xem điều này có nghĩa là gì, hãy nhấn một trong các nút. Lưu ý cách thông báo xuất hiện một giây sau đó. Nếu bạn nhấn nút trong khi chờ thông báo, bộ hẹn giờ sẽ đặt lại. Vì vậy, nếu bạn tiếp tục nhấp vào cùng một nút nhanh nhiều lần, thông báo sẽ không xuất hiện cho đến một giây *sau khi* bạn ngừng nhấp. Debouncing cho phép bạn trì hoãn một số hành động cho đến khi người dùng "ngừng làm mọi thứ". -This example works, but not quite as intended. The buttons are not independent. To see the problem, click one of the buttons, and then immediately click another button. You'd expect that after a delay, you would see both button's messages. But only the last button's message shows up. The first button's message gets lost. +Ví dụ này hoạt động, nhưng không hoàn toàn như dự định. Các nút không độc lập. Để xem sự cố, hãy nhấp vào một trong các nút, sau đó nhấp ngay vào một nút khác. Bạn sẽ mong đợi rằng sau một thời gian trễ, bạn sẽ thấy thông báo của cả hai nút. Nhưng chỉ có thông báo của nút cuối cùng hiển thị. Thông báo của nút đầu tiên bị mất. -Why are the buttons interfering with each other? Find and fix the issue. +Tại sao các nút lại can thiệp lẫn nhau? Tìm và sửa sự cố. <Hint> -The last timeout ID variable is shared between all `DebouncedButton` components. This is why clicking one button resets another button's timeout. Can you store a separate timeout ID for each button? +Biến ID timeout cuối cùng được chia sẻ giữa tất cả các component `DebouncedButton`. Đây là lý do tại sao việc nhấp vào một nút sẽ đặt lại timeout của một nút khác. Bạn có thể lưu trữ một ID timeout riêng cho mỗi nút không? </Hint> @@ -498,19 +495,19 @@ export default function Dashboard() { return ( <> <DebouncedButton - onClick={() => alert('Spaceship launched!')} + onClick={() => alert('Tàu vũ trụ đã được phóng!')} > - Launch the spaceship + Phóng tàu vũ trụ </DebouncedButton> <DebouncedButton - onClick={() => alert('Soup boiled!')} + onClick={() => alert('Súp đã được đun sôi!')} > - Boil the soup + Đun sôi súp </DebouncedButton> <DebouncedButton - onClick={() => alert('Lullaby sung!')} + onClick={() => alert('Bài hát ru đã được hát!')} > - Sing a lullaby + Hát một bài hát ru </DebouncedButton> </> ) @@ -525,7 +522,7 @@ button { display: block; margin: 10px; } <Solution> -A variable like `timeoutID` is shared between all components. This is why clicking on the second button resets the first button's pending timeout. To fix this, you can keep timeout in a ref. Each button will get its own ref, so they won't conflict with each other. Notice how clicking two buttons fast will show both messages. +Một biến như `timeoutID` được chia sẻ giữa tất cả các component. Đây là lý do tại sao việc nhấp vào nút thứ hai sẽ đặt lại timeout đang chờ xử lý của nút đầu tiên. Để sửa lỗi này, bạn có thể giữ timeout trong một ref. Mỗi nút sẽ nhận được ref riêng, vì vậy chúng sẽ không xung đột với nhau. Lưu ý cách nhấp vào hai nút nhanh sẽ hiển thị cả hai thông báo. <Sandpack> @@ -550,19 +547,19 @@ export default function Dashboard() { return ( <> <DebouncedButton - onClick={() => alert('Spaceship launched!')} + onClick={() => alert('Tàu vũ trụ đã được phóng!')} > - Launch the spaceship + Phóng tàu vũ trụ </DebouncedButton> <DebouncedButton - onClick={() => alert('Soup boiled!')} + onClick={() => alert('Súp đã được đun sôi!')} > - Boil the soup + Đun sôi súp </DebouncedButton> <DebouncedButton - onClick={() => alert('Lullaby sung!')} + onClick={() => alert('Bài hát ru đã được hát!')} > - Sing a lullaby + Hát một bài hát ru </DebouncedButton> </> ) @@ -577,11 +574,11 @@ button { display: block; margin: 10px; } </Solution> -#### Read the latest state {/*read-the-latest-state*/} +#### Đọc state mới nhất {/*read-the-latest-state*/} -In this example, after you press "Send", there is a small delay before the message is shown. Type "hello", press Send, and then quickly edit the input again. Despite your edits, the alert would still show "hello" (which was the value of state [at the time](/learn/state-as-a-snapshot#state-over-time) the button was clicked). +Trong ví dụ này, sau khi bạn nhấn "Gửi", có một độ trễ nhỏ trước khi thông báo được hiển thị. Nhập "xin chào", nhấn Gửi, và sau đó nhanh chóng chỉnh sửa lại đầu vào. Mặc dù bạn đã chỉnh sửa, cảnh báo vẫn sẽ hiển thị "xin chào" (đó là giá trị của state [vào thời điểm](/learn/state-as-a-snapshot#state-over-time) nút được nhấp). -Usually, this behavior is what you want in an app. However, there may be occasional cases where you want some asynchronous code to read the *latest* version of some state. Can you think of a way to make the alert show the *current* input text rather than what it was at the time of the click? +Thông thường, hành vi này là những gì bạn muốn trong một ứng dụng. Tuy nhiên, có thể có những trường hợp bạn muốn một số mã không đồng bộ đọc phiên bản *mới nhất* của một số state. Bạn có thể nghĩ ra cách nào để làm cho cảnh báo hiển thị văn bản đầu vào *hiện tại* thay vì những gì nó đã có tại thời điểm nhấp không? <Sandpack> @@ -593,7 +590,7 @@ export default function Chat() { function handleSend() { setTimeout(() => { - alert('Sending: ' + text); + alert('Đang gửi: ' + text); }, 3000); } @@ -605,7 +602,7 @@ export default function Chat() { /> <button onClick={handleSend}> - Send + Gửi </button> </> ); @@ -616,7 +613,7 @@ export default function Chat() { <Solution> -State works [like a snapshot](/learn/state-as-a-snapshot), so you can't read the latest state from an asynchronous operation like a timeout. However, you can keep the latest input text in a ref. A ref is mutable, so you can read the `current` property at any time. Since the current text is also used for rendering, in this example, you will need *both* a state variable (for rendering), *and* a ref (to read it in the timeout). You will need to update the current ref value manually. +State hoạt động [giống như một ảnh chụp nhanh](/learn/state-as-a-snapshot), vì vậy bạn không thể đọc state mới nhất từ một thao tác không đồng bộ như timeout. Tuy nhiên, bạn có thể giữ văn bản đầu vào mới nhất trong một ref. Một ref có thể thay đổi, vì vậy bạn có thể đọc thuộc tính `current` bất cứ lúc nào. Vì văn bản hiện tại cũng được sử dụng để render, trong ví dụ này, bạn sẽ cần *cả* một biến state (để render), *và* một ref (để đọc nó trong timeout). Bạn sẽ cần cập nhật giá trị ref hiện tại theo cách thủ công. <Sandpack> @@ -634,7 +631,7 @@ export default function Chat() { function handleSend() { setTimeout(() => { - alert('Sending: ' + textRef.current); + alert('Đang gửi: ' + textRef.current); }, 3000); } @@ -646,7 +643,7 @@ export default function Chat() { /> <button onClick={handleSend}> - Send + Gửi </button> </> ); diff --git a/src/content/learn/rendering-lists.md b/src/content/learn/rendering-lists.md index 32f81c447..f7d9f0821 100644 --- a/src/content/learn/rendering-lists.md +++ b/src/content/learn/rendering-lists.md @@ -3,72 +3,71 @@ title: Rendering Lists --- <Intro> - -You will often want to display multiple similar components from a collection of data. You can use the [JavaScript array methods](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array#) to manipulate an array of data. On this page, you'll use [`filter()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) and [`map()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map) with React to filter and transform your array of data into an array of components. +Bạn thường muốn hiển thị nhiều component tương tự từ một tập hợp dữ liệu. Bạn có thể sử dụng [các phương thức mảng JavaScript](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array#) để thao tác với một mảng dữ liệu. Trên trang này, bạn sẽ sử dụng [`filter()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) và [`map()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map) với React để lọc và chuyển đổi mảng dữ liệu của bạn thành một mảng các component. </Intro> <YouWillLearn> -* How to render components from an array using JavaScript's `map()` -* How to render only specific components using JavaScript's `filter()` -* When and why to use React keys +* Cách hiển thị các component từ một mảng bằng cách sử dụng `map()` của JavaScript +* Cách chỉ hiển thị các component cụ thể bằng cách sử dụng `filter()` của JavaScript +* Khi nào và tại sao nên sử dụng các key của React </YouWillLearn> -## Rendering data from arrays {/*rendering-data-from-arrays*/} +## Hiển thị dữ liệu từ mảng {/*rendering-data-from-arrays*/} -Say that you have a list of content. +Giả sử bạn có một danh sách nội dung. ```js <ul> - <li>Creola Katherine Johnson: mathematician</li> - <li>Mario José Molina-Pasquel Henríquez: chemist</li> - <li>Mohammad Abdus Salam: physicist</li> - <li>Percy Lavon Julian: chemist</li> - <li>Subrahmanyan Chandrasekhar: astrophysicist</li> + <li>Creola Katherine Johnson: nhà toán học</li> + <li>Mario José Molina-Pasquel Henríquez: nhà hóa học</li> + <li>Mohammad Abdus Salam: nhà vật lý</li> + <li>Percy Lavon Julian: nhà hóa học</li> + <li>Subrahmanyan Chandrasekhar: nhà vật lý thiên văn</li> </ul> ``` -The only difference among those list items is their contents, their data. You will often need to show several instances of the same component using different data when building interfaces: from lists of comments to galleries of profile images. In these situations, you can store that data in JavaScript objects and arrays and use methods like [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [`filter()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) to render lists of components from them. +Sự khác biệt duy nhất giữa các mục danh sách đó là nội dung của chúng, dữ liệu của chúng. Bạn thường cần hiển thị một số phiên bản của cùng một component bằng cách sử dụng dữ liệu khác nhau khi xây dựng giao diện: từ danh sách các bình luận đến thư viện ảnh hồ sơ. Trong những tình huống này, bạn có thể lưu trữ dữ liệu đó trong các đối tượng và mảng JavaScript và sử dụng các phương thức như [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) và [`filter()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) để hiển thị danh sách các component từ chúng. -Here’s a short example of how to generate a list of items from an array: +Dưới đây là một ví dụ ngắn gọn về cách tạo danh sách các mục từ một mảng: -1. **Move** the data into an array: +1. **Di chuyển** dữ liệu vào một mảng: ```js const people = [ - 'Creola Katherine Johnson: mathematician', - 'Mario José Molina-Pasquel Henríquez: chemist', - 'Mohammad Abdus Salam: physicist', - 'Percy Lavon Julian: chemist', - 'Subrahmanyan Chandrasekhar: astrophysicist' + 'Creola Katherine Johnson: nhà toán học', + 'Mario José Molina-Pasquel Henríquez: nhà hóa học', + 'Mohammad Abdus Salam: nhà vật lý', + 'Percy Lavon Julian: nhà hóa học', + 'Subrahmanyan Chandrasekhar: nhà vật lý thiên văn' ]; ``` -2. **Map** the `people` members into a new array of JSX nodes, `listItems`: +2. **Ánh xạ** các thành viên `people` vào một mảng mới các nút JSX, `listItems`: ```js const listItems = people.map(person => <li>{person}</li>); ``` -3. **Return** `listItems` from your component wrapped in a `<ul>`: +3. **Trả về** `listItems` từ component của bạn được bao bọc trong một thẻ `<ul>`: ```js return <ul>{listItems}</ul>; ``` -Here is the result: +Đây là kết quả: <Sandpack> ```js const people = [ - 'Creola Katherine Johnson: mathematician', - 'Mario José Molina-Pasquel Henríquez: chemist', - 'Mohammad Abdus Salam: physicist', - 'Percy Lavon Julian: chemist', - 'Subrahmanyan Chandrasekhar: astrophysicist' + 'Creola Katherine Johnson: nhà toán học', + 'Mario José Molina-Pasquel Henríquez: nhà hóa học', + 'Mohammad Abdus Salam: nhà vật lý', + 'Percy Lavon Julian: nhà hóa học', + 'Subrahmanyan Chandrasekhar: nhà vật lý thiên văn' ]; export default function List() { @@ -85,19 +84,19 @@ li { margin-bottom: 10px; } </Sandpack> -Notice the sandbox above displays a console error: +Lưu ý sandbox ở trên hiển thị một lỗi trong bảng điều khiển: <ConsoleBlock level="error"> -Warning: Each child in a list should have a unique "key" prop. +Warning: Mỗi phần tử con trong một danh sách nên có một prop "key" duy nhất. </ConsoleBlock> -You'll learn how to fix this error later on this page. Before we get to that, let's add some structure to your data. +Bạn sẽ học cách sửa lỗi này sau trên trang này. Trước khi chúng ta đi đến đó, hãy thêm một số cấu trúc vào dữ liệu của bạn. -## Filtering arrays of items {/*filtering-arrays-of-items*/} +## Lọc mảng các mục {/*filtering-arrays-of-items*/} -This data can be structured even more. +Dữ liệu này có thể được cấu trúc hơn nữa. ```js const people = [{ @@ -123,11 +122,11 @@ const people = [{ }]; ``` -Let's say you want a way to only show people whose profession is `'chemist'`. You can use JavaScript's `filter()` method to return just those people. This method takes an array of items, passes them through a “test” (a function that returns `true` or `false`), and returns a new array of only those items that passed the test (returned `true`). +Giả sử bạn muốn một cách để chỉ hiển thị những người có nghề nghiệp là `'chemist'`. Bạn có thể sử dụng phương thức `filter()` của JavaScript để chỉ trả về những người đó. Phương thức này lấy một mảng các mục, chuyển chúng qua một "bài kiểm tra" (một hàm trả về `true` hoặc `false`) và trả về một mảng mới chỉ gồm những mục đã vượt qua bài kiểm tra (trả về `true`). -You only want the items where `profession` is `'chemist'`. The "test" function for this looks like `(person) => person.profession === 'chemist'`. Here's how to put it together: +Bạn chỉ muốn các mục mà `profession` là `'chemist'`. Hàm "kiểm tra" cho việc này trông giống như `(person) => person.profession === 'chemist'`. Đây là cách để kết hợp nó: -1. **Create** a new array of just “chemist” people, `chemists`, by calling `filter()` on the `people` filtering by `person.profession === 'chemist'`: +1. **Tạo** một mảng mới chỉ gồm những người là "chemist", `chemists`, bằng cách gọi `filter()` trên `people` lọc theo `person.profession === 'chemist'`: ```js const chemists = people.filter(person => @@ -135,7 +134,7 @@ const chemists = people.filter(person => ); ``` -2. Now **map** over `chemists`: +2. Bây giờ **ánh xạ** trên `chemists`: ```js {1,13} const listItems = chemists.map(person => @@ -153,7 +152,7 @@ const listItems = chemists.map(person => ); ``` -3. Lastly, **return** the `listItems` from your component: +3. Cuối cùng, **trả về** `listItems` từ component của bạn: ```js return <ul>{listItems}</ul>; @@ -246,37 +245,37 @@ img { width: 100px; height: 100px; border-radius: 50%; } <Pitfall> -Arrow functions implicitly return the expression right after `=>`, so you didn't need a `return` statement: +Các hàm mũi tên trả về biểu thức ngay sau `=>` một cách ngầm định, vì vậy bạn không cần câu lệnh `return`: ```js const listItems = chemists.map(person => - <li>...</li> // Implicit return! + <li>...</li> // Trả về ngầm định! ); ``` -However, **you must write `return` explicitly if your `=>` is followed by a `{` curly brace!** +Tuy nhiên, **bạn phải viết `return` một cách rõ ràng nếu `=>` của bạn theo sau bởi dấu ngoặc nhọn `{`!** ```js -const listItems = chemists.map(person => { // Curly brace +const listItems = chemists.map(person => { // Dấu ngoặc nhọn return <li>...</li>; }); ``` -Arrow functions containing `=> {` are said to have a ["block body".](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#function_body) They let you write more than a single line of code, but you *have to* write a `return` statement yourself. If you forget it, nothing gets returned! +Các hàm mũi tên chứa `=> {` được cho là có ["thân khối".](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#function_body) Chúng cho phép bạn viết nhiều hơn một dòng mã, nhưng bạn *phải* tự viết câu lệnh `return`. Nếu bạn quên nó, sẽ không có gì được trả về! </Pitfall> -## Keeping list items in order with `key` {/*keeping-list-items-in-order-with-key*/} +## Giữ các mục danh sách theo thứ tự với `key` {/*keeping-list-items-in-order-with-key*/} -Notice that all the sandboxes above show an error in the console: +Lưu ý rằng tất cả các sandbox ở trên đều hiển thị một lỗi trong bảng điều khiển: <ConsoleBlock level="error"> -Warning: Each child in a list should have a unique "key" prop. +Warning: Mỗi phần tử con trong một danh sách nên có một prop "key" duy nhất. </ConsoleBlock> -You need to give each array item a `key` -- a string or a number that uniquely identifies it among other items in that array: +Bạn cần cung cấp cho mỗi mục trong mảng một `key` -- một chuỗi hoặc một số nhận dạng duy nhất nó giữa các mục khác trong mảng đó: ```js <li key={person.id}>...</li> @@ -284,13 +283,13 @@ You need to give each array item a `key` -- a string or a number that uniquely i <Note> -JSX elements directly inside a `map()` call always need keys! +Các phần tử JSX trực tiếp bên trong một lệnh gọi `map()` luôn cần các key! </Note> -Keys tell React which array item each component corresponds to, so that it can match them up later. This becomes important if your array items can move (e.g. due to sorting), get inserted, or get deleted. A well-chosen `key` helps React infer what exactly has happened, and make the correct updates to the DOM tree. +Các key cho React biết mục mảng nào mà mỗi component tương ứng, để nó có thể khớp chúng sau này. Điều này trở nên quan trọng nếu các mục trong mảng của bạn có thể di chuyển (ví dụ: do sắp xếp), được chèn hoặc bị xóa. Một `key` được chọn tốt sẽ giúp React suy ra chính xác những gì đã xảy ra và thực hiện các cập nhật chính xác cho cây DOM. -Rather than generating keys on the fly, you should include them in your data: +Thay vì tạo các key một cách nhanh chóng, bạn nên đưa chúng vào dữ liệu của mình: <Sandpack> @@ -318,31 +317,31 @@ export default function List() { ```js src/data.js active export const people = [{ - id: 0, // Used in JSX as a key + id: 0, // Được sử dụng trong JSX làm key name: 'Creola Katherine Johnson', profession: 'mathematician', accomplishment: 'spaceflight calculations', imageId: 'MK3eW3A' }, { - id: 1, // Used in JSX as a key + id: 1, // Được sử dụng trong JSX làm key name: 'Mario José Molina-Pasquel Henríquez', profession: 'chemist', accomplishment: 'discovery of Arctic ozone hole', imageId: 'mynHUSa' }, { - id: 2, // Used in JSX as a key + id: 2, // Được sử dụng trong JSX làm key name: 'Mohammad Abdus Salam', profession: 'physicist', accomplishment: 'electromagnetism theory', imageId: 'bE7W1ji' }, { - id: 3, // Used in JSX as a key + id: 3, // Được sử dụng trong JSX làm key name: 'Percy Lavon Julian', profession: 'chemist', accomplishment: 'pioneering cortisone drugs, steroids and birth control pills', imageId: 'IOjWm71' }, { - id: 4, // Used in JSX as a key + id: 4, // Được sử dụng trong JSX làm key name: 'Subrahmanyan Chandrasekhar', profession: 'astrophysicist', accomplishment: 'white dwarf star mass calculations', @@ -376,11 +375,11 @@ img { width: 100px; height: 100px; border-radius: 50%; } <DeepDive> -#### Displaying several DOM nodes for each list item {/*displaying-several-dom-nodes-for-each-list-item*/} +#### Hiển thị một số nút DOM cho mỗi mục danh sách {/*displaying-several-dom-nodes-for-each-list-item*/} -What do you do when each item needs to render not one, but several DOM nodes? +Bạn làm gì khi mỗi mục cần hiển thị không phải một, mà là một số nút DOM? -The short [`<>...</>` Fragment](/reference/react/Fragment) syntax won't let you pass a key, so you need to either group them into a single `<div>`, or use the slightly longer and [more explicit `<Fragment>` syntax:](/reference/react/Fragment#rendering-a-list-of-fragments) +Cú pháp [`<>...</>` Fragment](/reference/react/Fragment) ngắn gọn sẽ không cho phép bạn truyền một key, vì vậy bạn cần nhóm chúng thành một `<div>` duy nhất hoặc sử dụng cú pháp `<Fragment>` dài hơn và [rõ ràng hơn:](/reference/react/Fragment#rendering-a-list-of-fragments) ```js import { Fragment } from 'react'; @@ -395,46 +394,46 @@ const listItems = people.map(person => ); ``` -Fragments disappear from the DOM, so this will produce a flat list of `<h1>`, `<p>`, `<h1>`, `<p>`, and so on. +Các Fragment biến mất khỏi DOM, vì vậy điều này sẽ tạo ra một danh sách phẳng gồm `<h1>`, `<p>`, `<h1>`, `<p>`, v.v. </DeepDive> -### Where to get your `key` {/*where-to-get-your-key*/} +### Nơi để lấy `key` của bạn {/*where-to-get-your-key*/} -Different sources of data provide different sources of keys: +Các nguồn dữ liệu khác nhau cung cấp các nguồn key khác nhau: -* **Data from a database:** If your data is coming from a database, you can use the database keys/IDs, which are unique by nature. -* **Locally generated data:** If your data is generated and persisted locally (e.g. notes in a note-taking app), use an incrementing counter, [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) or a package like [`uuid`](https://www.npmjs.com/package/uuid) when creating items. +* **Dữ liệu từ cơ sở dữ liệu:** Nếu dữ liệu của bạn đến từ cơ sở dữ liệu, bạn có thể sử dụng các key/ID của cơ sở dữ liệu, vốn dĩ là duy nhất. +* **Dữ liệu được tạo cục bộ:** Nếu dữ liệu của bạn được tạo và lưu trữ cục bộ (ví dụ: ghi chú trong một ứng dụng ghi chú), hãy sử dụng một bộ đếm tăng dần, [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) hoặc một gói như [`uuid`](https://www.npmjs.com/package/uuid) khi tạo các mục. -### Rules of keys {/*rules-of-keys*/} +### Các quy tắc của key {/*rules-of-keys*/} -* **Keys must be unique among siblings.** However, it’s okay to use the same keys for JSX nodes in _different_ arrays. -* **Keys must not change** or that defeats their purpose! Don't generate them while rendering. +* **Các key phải là duy nhất giữa các anh chị em.** Tuy nhiên, bạn có thể sử dụng cùng một key cho các nút JSX trong các mảng _khác nhau_. +* **Các key không được thay đổi** nếu không điều đó sẽ phá vỡ mục đích của chúng! Không tạo chúng trong khi hiển thị. -### Why does React need keys? {/*why-does-react-need-keys*/} +### Tại sao React cần các key? {/*why-does-react-need-keys*/} -Imagine that files on your desktop didn't have names. Instead, you'd refer to them by their order -- the first file, the second file, and so on. You could get used to it, but once you delete a file, it would get confusing. The second file would become the first file, the third file would be the second file, and so on. +Hãy tưởng tượng rằng các tệp trên màn hình của bạn không có tên. Thay vào đó, bạn sẽ tham khảo chúng theo thứ tự của chúng -- tệp đầu tiên, tệp thứ hai, v.v. Bạn có thể làm quen với nó, nhưng một khi bạn xóa một tệp, nó sẽ trở nên khó hiểu. Tệp thứ hai sẽ trở thành tệp đầu tiên, tệp thứ ba sẽ là tệp thứ hai, v.v. -File names in a folder and JSX keys in an array serve a similar purpose. They let us uniquely identify an item between its siblings. A well-chosen key provides more information than the position within the array. Even if the _position_ changes due to reordering, the `key` lets React identify the item throughout its lifetime. +Tên tệp trong một thư mục và các key JSX trong một mảng phục vụ một mục đích tương tự. Chúng cho phép chúng ta xác định duy nhất một mục giữa các anh chị em của nó. Một key được chọn tốt cung cấp nhiều thông tin hơn vị trí trong mảng. Ngay cả khi _vị trí_ thay đổi do sắp xếp lại, `key` cho phép React xác định mục trong suốt vòng đời của nó. <Pitfall> -You might be tempted to use an item's index in the array as its key. In fact, that's what React will use if you don't specify a `key` at all. But the order in which you render items will change over time if an item is inserted, deleted, or if the array gets reordered. Index as a key often leads to subtle and confusing bugs. +Bạn có thể bị cám dỗ sử dụng chỉ mục của một mục trong mảng làm key của nó. Trên thực tế, đó là những gì React sẽ sử dụng nếu bạn không chỉ định `key` nào cả. Nhưng thứ tự mà bạn hiển thị các mục sẽ thay đổi theo thời gian nếu một mục được chèn, xóa hoặc nếu mảng được sắp xếp lại. Chỉ mục làm key thường dẫn đến các lỗi tinh vi và khó hiểu. -Similarly, do not generate keys on the fly, e.g. with `key={Math.random()}`. This will cause keys to never match up between renders, leading to all your components and DOM being recreated every time. Not only is this slow, but it will also lose any user input inside the list items. Instead, use a stable ID based on the data. +Tương tự, không tạo các key một cách nhanh chóng, ví dụ: với `key={Math.random()}`. Điều này sẽ khiến các key không bao giờ khớp giữa các lần hiển thị, dẫn đến tất cả các component và DOM của bạn được tạo lại mỗi lần. Điều này không chỉ chậm mà còn làm mất mọi dữ liệu đầu vào của người dùng bên trong các mục danh sách. Thay vào đó, hãy sử dụng một ID ổn định dựa trên dữ liệu. -Note that your components won't receive `key` as a prop. It's only used as a hint by React itself. If your component needs an ID, you have to pass it as a separate prop: `<Profile key={id} userId={id} />`. +Lưu ý rằng các component của bạn sẽ không nhận được `key` làm một prop. Nó chỉ được sử dụng như một gợi ý bởi chính React. Nếu component của bạn cần một ID, bạn phải chuyển nó như một prop riêng biệt: `<Profile key={id} userId={id} />`. </Pitfall> <Recap> -On this page you learned: +Trên trang này, bạn đã học: -* How to move data out of components and into data structures like arrays and objects. -* How to generate sets of similar components with JavaScript's `map()`. -* How to create arrays of filtered items with JavaScript's `filter()`. -* Why and how to set `key` on each component in a collection so React can keep track of each of them even if their position or data changes. +* Cách di chuyển dữ liệu ra khỏi các component và vào các cấu trúc dữ liệu như mảng và đối tượng. +* Cách tạo các tập hợp các component tương tự với `map()` của JavaScript. +* Cách tạo các mảng các mục được lọc với `filter()` của JavaScript. +* Tại sao và cách đặt `key` trên mỗi component trong một tập hợp để React có thể theo dõi từng component ngay cả khi vị trí hoặc dữ liệu của chúng thay đổi. </Recap> @@ -442,11 +441,11 @@ On this page you learned: <Challenges> -#### Splitting a list in two {/*splitting-a-list-in-two*/} +#### Chia một danh sách thành hai {/*splitting-a-list-in-two*/} -This example shows a list of all people. +Ví dụ này hiển thị một danh sách tất cả mọi người. -Change it to show two separate lists one after another: **Chemists** and **Everyone Else.** Like previously, you can determine whether a person is a chemist by checking if `person.profession === 'chemist'`. +Thay đổi nó để hiển thị hai danh sách riêng biệt liên tiếp: **Các nhà hóa học** và **Mọi người khác.** Giống như trước đây, bạn có thể xác định xem một người có phải là nhà hóa học hay không bằng cách kiểm tra xem `person.profession === 'chemist'`. <Sandpack> @@ -537,7 +536,7 @@ img { width: 100px; height: 100px; border-radius: 50%; } <Solution> -You could use `filter()` twice, creating two separate arrays, and then `map` over both of them: +Bạn có thể sử dụng `filter()` hai lần, tạo hai mảng riêng biệt, sau đó `map` trên cả hai: <Sandpack> @@ -650,9 +649,9 @@ img { width: 100px; height: 100px; border-radius: 50%; } </Sandpack> -In this solution, the `map` calls are placed directly inline into the parent `<ul>` elements, but you could introduce variables for them if you find that more readable. +Trong giải pháp này, các lệnh gọi `map` được đặt trực tiếp vào các phần tử `<ul>` cha, nhưng bạn có thể giới thiệu các biến cho chúng nếu bạn thấy điều đó dễ đọc hơn. -There is still a bit duplication between the rendered lists. You can go further and extract the repetitive parts into a `<ListSection>` component: +Vẫn còn một chút trùng lặp giữa các danh sách được hiển thị. Bạn có thể tiến xa hơn và trích xuất các phần lặp đi lặp lại vào một component `<ListSection>`: <Sandpack> @@ -763,10 +762,9 @@ img { width: 100px; height: 100px; border-radius: 50%; } ``` </Sandpack> +Một người đọc rất kỹ có thể nhận thấy rằng với hai lệnh gọi `filter`, chúng ta kiểm tra nghề nghiệp của mỗi người hai lần. Kiểm tra một thuộc tính rất nhanh, vì vậy trong ví dụ này thì ổn. Nếu logic của bạn tốn kém hơn thế, bạn có thể thay thế các lệnh gọi `filter` bằng một vòng lặp tự xây dựng các mảng và kiểm tra mỗi người một lần. -A very attentive reader might notice that with two `filter` calls, we check each person's profession twice. Checking a property is very fast, so in this example it's fine. If your logic was more expensive than that, you could replace the `filter` calls with a loop that manually constructs the arrays and checks each person once. - -In fact, if `people` never change, you could move this code out of your component. From React's perspective, all that matters is that you give it an array of JSX nodes in the end. It doesn't care how you produce that array: +Trên thực tế, nếu `people` không bao giờ thay đổi, bạn có thể di chuyển đoạn mã này ra khỏi component của mình. Theo quan điểm của React, tất cả những gì quan trọng là bạn cung cấp cho nó một mảng các nút JSX ở cuối. Nó không quan tâm bạn tạo ra mảng đó như thế nào: <Sandpack> @@ -884,13 +882,13 @@ img { width: 100px; height: 100px; border-radius: 50%; } </Solution> -#### Nested lists in one component {/*nested-lists-in-one-component*/} +#### Danh sách lồng nhau trong một component {/*nested-lists-in-one-component*/} -Make a list of recipes from this array! For each recipe in the array, display its name as an `<h2>` and list its ingredients in a `<ul>`. +Tạo một danh sách các công thức từ mảng này! Đối với mỗi công thức trong mảng, hiển thị tên của nó dưới dạng `<h2>` và liệt kê các thành phần của nó trong một `<ul>`. <Hint> -This will require nesting two different `map` calls. +Điều này sẽ yêu cầu lồng hai lệnh gọi `map` khác nhau. </Hint> @@ -928,7 +926,7 @@ export const recipes = [{ <Solution> -Here is one way you could go about it: +Đây là một cách bạn có thể thực hiện: <Sandpack> @@ -974,13 +972,13 @@ export const recipes = [{ </Sandpack> -Each of the `recipes` already includes an `id` field, so that's what the outer loop uses for its `key`. There is no ID you could use to loop over ingredients. However, it's reasonable to assume that the same ingredient won't be listed twice within the same recipe, so its name can serve as a `key`. Alternatively, you could change the data structure to add IDs, or use index as a `key` (with the caveat that you can't safely reorder ingredients). +Mỗi `recipes` đã bao gồm một trường `id`, vì vậy đó là những gì vòng lặp bên ngoài sử dụng cho `key` của nó. Không có ID nào bạn có thể sử dụng để lặp lại các thành phần. Tuy nhiên, có lý do để cho rằng cùng một thành phần sẽ không được liệt kê hai lần trong cùng một công thức, vì vậy tên của nó có thể đóng vai trò là `key`. Ngoài ra, bạn có thể thay đổi cấu trúc dữ liệu để thêm ID hoặc sử dụng chỉ mục làm `key` (với cảnh báo rằng bạn không thể sắp xếp lại các thành phần một cách an toàn). </Solution> -#### Extracting a list item component {/*extracting-a-list-item-component*/} +#### Trích xuất một component mục danh sách {/*extracting-a-list-item-component*/} -This `RecipeList` component contains two nested `map` calls. To simplify it, extract a `Recipe` component from it which will accept `id`, `name`, and `ingredients` props. Where do you place the outer `key` and why? +Component `RecipeList` này chứa hai lệnh gọi `map` lồng nhau. Để đơn giản hóa nó, hãy trích xuất một component `Recipe` từ nó, component này sẽ chấp nhận các props `id`, `name` và `ingredients`. Bạn đặt `key` bên ngoài ở đâu và tại sao? <Sandpack> @@ -1028,7 +1026,7 @@ export const recipes = [{ <Solution> -You can copy-paste the JSX from the outer `map` into a new `Recipe` component and return that JSX. Then you can change `recipe.name` to `name`, `recipe.id` to `id`, and so on, and pass them as props to the `Recipe`: +Bạn có thể sao chép-dán JSX từ `map` bên ngoài vào một component `Recipe` mới và trả về JSX đó. Sau đó, bạn có thể thay đổi `recipe.name` thành `name`, `recipe.id` thành `id`, v.v. và chuyển chúng làm props cho `Recipe`: <Sandpack> @@ -1080,15 +1078,15 @@ export const recipes = [{ </Sandpack> -Here, `<Recipe {...recipe} key={recipe.id} />` is a syntax shortcut saying "pass all properties of the `recipe` object as props to the `Recipe` component". You could also write each prop explicitly: `<Recipe id={recipe.id} name={recipe.name} ingredients={recipe.ingredients} key={recipe.id} />`. +Ở đây, `<Recipe {...recipe} key={recipe.id} />` là một cú pháp tắt cho biết "truyền tất cả các thuộc tính của đối tượng `recipe` làm props cho component `Recipe`". Bạn cũng có thể viết rõ ràng từng prop: `<Recipe id={recipe.id} name={recipe.name} ingredients={recipe.ingredients} key={recipe.id} />`. -**Note that the `key` is specified on the `<Recipe>` itself rather than on the root `<div>` returned from `Recipe`.** This is because this `key` is needed directly within the context of the surrounding array. Previously, you had an array of `<div>`s so each of them needed a `key`, but now you have an array of `<Recipe>`s. In other words, when you extract a component, don't forget to leave the `key` outside the JSX you copy and paste. +**Lưu ý rằng `key` được chỉ định trên chính `<Recipe>` chứ không phải trên `<div>` gốc được trả về từ `Recipe`.** Điều này là do `key` này cần thiết trực tiếp trong ngữ cảnh của mảng xung quanh. Trước đây, bạn có một mảng các `<div>` nên mỗi `<div>` cần một `key`, nhưng bây giờ bạn có một mảng các `<Recipe>`. Nói cách khác, khi bạn trích xuất một component, đừng quên để `key` bên ngoài JSX bạn sao chép và dán. </Solution> -#### List with a separator {/*list-with-a-separator*/} +#### Danh sách với dấu phân cách {/*list-with-a-separator*/} -This example renders a famous haiku by Tachibana Hokushi, with each line wrapped in a `<p>` tag. Your job is to insert an `<hr />` separator between each paragraph. Your resulting structure should look like this: +Ví dụ này hiển thị một bài haiku nổi tiếng của Tachibana Hokushi, với mỗi dòng được bao bọc trong một thẻ `<p>`. Nhiệm vụ của bạn là chèn một dấu phân cách `<hr />` giữa mỗi đoạn văn. Cấu trúc kết quả của bạn sẽ như thế này: ```js <article> @@ -1100,7 +1098,7 @@ This example renders a famous haiku by Tachibana Hokushi, with each line wrapped </article> ``` -A haiku only contains three lines, but your solution should work with any number of lines. Note that `<hr />` elements only appear *between* the `<p>` elements, not in the beginning or the end! +Một bài haiku chỉ chứa ba dòng, nhưng giải pháp của bạn phải hoạt động với bất kỳ số lượng dòng nào. Lưu ý rằng các phần tử `<hr />` chỉ xuất hiện *giữa* các phần tử `<p>`, không phải ở đầu hoặc cuối! <Sandpack> @@ -1143,17 +1141,17 @@ hr { </Sandpack> -(This is a rare case where index as a key is acceptable because a poem's lines will never reorder.) +(Đây là một trường hợp hiếm hoi mà chỉ mục làm key là chấp nhận được vì các dòng của một bài thơ sẽ không bao giờ được sắp xếp lại.) <Hint> -You'll either need to convert `map` to a manual loop, or use a Fragment. +Bạn sẽ cần chuyển đổi `map` thành một vòng lặp thủ công hoặc sử dụng Fragment. </Hint> <Solution> -You can write a manual loop, inserting `<hr />` and `<p>...</p>` into the output array as you go: +Bạn có thể viết một vòng lặp thủ công, chèn `<hr />` và `<p>...</p>` vào mảng đầu ra khi bạn thực hiện: <Sandpack> @@ -1208,9 +1206,9 @@ hr { </Sandpack> -Using the original line index as a `key` doesn't work anymore because each separator and paragraph are now in the same array. However, you can give each of them a distinct key using a suffix, e.g. `key={i + '-text'}`. +Sử dụng chỉ mục dòng ban đầu làm `key` không còn hoạt động nữa vì mỗi dấu phân cách và đoạn văn hiện nằm trong cùng một mảng. Tuy nhiên, bạn có thể cung cấp cho mỗi phần tử một key riêng biệt bằng cách sử dụng một hậu tố, ví dụ: `key={i + '-text'}`. -Alternatively, you could render a collection of Fragments which contain `<hr />` and `<p>...</p>`. However, the `<>...</>` shorthand syntax doesn't support passing keys, so you'd have to write `<Fragment>` explicitly: +Ngoài ra, bạn có thể hiển thị một tập hợp các Fragment chứa `<hr />` và `<p>...</p>`. Tuy nhiên, cú pháp viết tắt `<>...</>` không hỗ trợ truyền key, vì vậy bạn sẽ phải viết `<Fragment>` một cách rõ ràng: <Sandpack> @@ -1256,7 +1254,7 @@ hr { </Sandpack> -Remember, Fragments (often written as `<> </>`) let you group JSX nodes without adding extra `<div>`s! +Hãy nhớ rằng, Fragments (thường được viết là `<> </>`) cho phép bạn nhóm các nút JSX mà không cần thêm `<div>` bổ sung! </Solution> diff --git a/src/content/learn/responding-to-events.md b/src/content/learn/responding-to-events.md index 17bd087ed..d637085bc 100644 --- a/src/content/learn/responding-to-events.md +++ b/src/content/learn/responding-to-events.md @@ -1,24 +1,24 @@ --- -title: Responding to Events +title: Phản hồi sự kiện --- <Intro> -React lets you add *event handlers* to your JSX. Event handlers are your own functions that will be triggered in response to interactions like clicking, hovering, focusing form inputs, and so on. +React cho phép bạn thêm *trình xử lý sự kiện* vào JSX của mình. Trình xử lý sự kiện là các hàm của riêng bạn sẽ được kích hoạt để đáp ứng các tương tác như nhấp chuột, di chuột, tập trung vào các đầu vào biểu mẫu, v.v. </Intro> <YouWillLearn> -* Different ways to write an event handler -* How to pass event handling logic from a parent component -* How events propagate and how to stop them +* Các cách khác nhau để viết một trình xử lý sự kiện +* Cách truyền logic xử lý sự kiện từ một component cha +* Cách các sự kiện lan truyền và cách ngăn chặn chúng </YouWillLearn> -## Adding event handlers {/*adding-event-handlers*/} +## Thêm trình xử lý sự kiện {/*adding-event-handlers*/} -To add an event handler, you will first define a function and then [pass it as a prop](/learn/passing-props-to-a-component) to the appropriate JSX tag. For example, here is a button that doesn't do anything yet: +Để thêm một trình xử lý sự kiện, trước tiên bạn sẽ định nghĩa một hàm và sau đó [truyền nó như một prop](/learn/passing-props-to-a-component) cho thẻ JSX thích hợp. Ví dụ: đây là một nút chưa làm gì cả: <Sandpack> @@ -26,7 +26,7 @@ To add an event handler, you will first define a function and then [pass it as a export default function Button() { return ( <button> - I don't do anything + Tôi không làm gì cả </button> ); } @@ -34,23 +34,23 @@ export default function Button() { </Sandpack> -You can make it show a message when a user clicks by following these three steps: +Bạn có thể làm cho nó hiển thị một thông báo khi người dùng nhấp vào bằng cách làm theo ba bước sau: -1. Declare a function called `handleClick` *inside* your `Button` component. -2. Implement the logic inside that function (use `alert` to show the message). -3. Add `onClick={handleClick}` to the `<button>` JSX. +1. Khai báo một hàm có tên là `handleClick` *bên trong* component `Button` của bạn. +2. Triển khai logic bên trong hàm đó (sử dụng `alert` để hiển thị thông báo). +3. Thêm `onClick={handleClick}` vào JSX `<button>`. <Sandpack> ```js export default function Button() { function handleClick() { - alert('You clicked me!'); + alert('Bạn đã nhấp vào tôi!'); } return ( <button onClick={handleClick}> - Click me + Nhấp vào tôi </button> ); } @@ -62,77 +62,77 @@ button { margin-right: 10px; } </Sandpack> -You defined the `handleClick` function and then [passed it as a prop](/learn/passing-props-to-a-component) to `<button>`. `handleClick` is an **event handler.** Event handler functions: +Bạn đã định nghĩa hàm `handleClick` và sau đó [truyền nó như một prop](/learn/passing-props-to-a-component) cho `<button>`. `handleClick` là một **trình xử lý sự kiện.** Các hàm xử lý sự kiện: -* Are usually defined *inside* your components. -* Have names that start with `handle`, followed by the name of the event. +* Thường được định nghĩa *bên trong* các component của bạn. +* Có tên bắt đầu bằng `handle`, theo sau là tên của sự kiện. -By convention, it is common to name event handlers as `handle` followed by the event name. You'll often see `onClick={handleClick}`, `onMouseEnter={handleMouseEnter}`, and so on. +Theo quy ước, người ta thường đặt tên cho các trình xử lý sự kiện là `handle` theo sau là tên sự kiện. Bạn sẽ thường thấy `onClick={handleClick}`, `onMouseEnter={handleMouseEnter}`, v.v. -Alternatively, you can define an event handler inline in the JSX: +Ngoài ra, bạn có thể định nghĩa một trình xử lý sự kiện nội tuyến trong JSX: ```jsx <button onClick={function handleClick() { - alert('You clicked me!'); + alert('Bạn đã nhấp vào tôi!'); }}> ``` -Or, more concisely, using an arrow function: +Hoặc, ngắn gọn hơn, sử dụng một hàm mũi tên: ```jsx <button onClick={() => { - alert('You clicked me!'); + alert('Bạn đã nhấp vào tôi!'); }}> ``` -All of these styles are equivalent. Inline event handlers are convenient for short functions. +Tất cả các kiểu này là tương đương. Trình xử lý sự kiện nội tuyến rất tiện lợi cho các hàm ngắn. <Pitfall> -Functions passed to event handlers must be passed, not called. For example: +Các hàm được truyền cho trình xử lý sự kiện phải được truyền, không được gọi. Ví dụ: -| passing a function (correct) | calling a function (incorrect) | +| truyền một hàm (chính xác) | gọi một hàm (không chính xác) | | -------------------------------- | ---------------------------------- | | `<button onClick={handleClick}>` | `<button onClick={handleClick()}>` | -The difference is subtle. In the first example, the `handleClick` function is passed as an `onClick` event handler. This tells React to remember it and only call your function when the user clicks the button. +Sự khác biệt là rất nhỏ. Trong ví dụ đầu tiên, hàm `handleClick` được truyền như một trình xử lý sự kiện `onClick`. Điều này cho React biết để ghi nhớ nó và chỉ gọi hàm của bạn khi người dùng nhấp vào nút. -In the second example, the `()` at the end of `handleClick()` fires the function *immediately* during [rendering](/learn/render-and-commit), without any clicks. This is because JavaScript inside the [JSX `{` and `}`](/learn/javascript-in-jsx-with-curly-braces) executes right away. +Trong ví dụ thứ hai, `()` ở cuối `handleClick()` kích hoạt hàm *ngay lập tức* trong quá trình [rendering](/learn/render-and-commit), mà không cần bất kỳ nhấp chuột nào. Điều này là do JavaScript bên trong [JSX `{` và `}`](/learn/javascript-in-jsx-with-curly-braces) thực thi ngay lập tức. -When you write code inline, the same pitfall presents itself in a different way: +Khi bạn viết mã nội tuyến, cùng một cạm bẫy xuất hiện theo một cách khác: -| passing a function (correct) | calling a function (incorrect) | +| truyền một hàm (chính xác) | gọi một hàm (không chính xác) | | --------------------------------------- | --------------------------------- | | `<button onClick={() => alert('...')}>` | `<button onClick={alert('...')}>` | -Passing inline code like this won't fire on click—it fires every time the component renders: +Truyền mã nội tuyến như thế này sẽ không kích hoạt khi nhấp chuột—nó kích hoạt mỗi khi component render: ```jsx -// This alert fires when the component renders, not when clicked! -<button onClick={alert('You clicked me!')}> +// Cảnh báo này kích hoạt khi component render, không phải khi nhấp vào! +<button onClick={alert('Bạn đã nhấp vào tôi!')}> ``` -If you want to define your event handler inline, wrap it in an anonymous function like so: +Nếu bạn muốn định nghĩa trình xử lý sự kiện của mình nội tuyến, hãy bọc nó trong một hàm ẩn danh như sau: ```jsx -<button onClick={() => alert('You clicked me!')}> +<button onClick={() => alert('Bạn đã nhấp vào tôi!')}> ``` -Rather than executing the code inside with every render, this creates a function to be called later. +Thay vì thực thi mã bên trong với mỗi lần render, điều này tạo ra một hàm sẽ được gọi sau. -In both cases, what you want to pass is a function: +Trong cả hai trường hợp, những gì bạn muốn truyền là một hàm: -* `<button onClick={handleClick}>` passes the `handleClick` function. -* `<button onClick={() => alert('...')}>` passes the `() => alert('...')` function. +* `<button onClick={handleClick}>` truyền hàm `handleClick`. +* `<button onClick={() => alert('...')}>` truyền hàm `() => alert('...')`. -[Read more about arrow functions.](https://javascript.info/arrow-functions-basics) +[Đọc thêm về hàm mũi tên.](https://javascript.javascript.info/arrow-functions-basics) </Pitfall> -### Reading props in event handlers {/*reading-props-in-event-handlers*/} +### Đọc props trong trình xử lý sự kiện {/*reading-props-in-event-handlers*/} -Because event handlers are declared inside of a component, they have access to the component's props. Here is a button that, when clicked, shows an alert with its `message` prop: +Vì trình xử lý sự kiện được khai báo bên trong một component, chúng có quyền truy cập vào các props của component đó. Dưới đây là một nút, khi được nhấp vào, sẽ hiển thị một cảnh báo với prop `message` của nó: <Sandpack> @@ -148,11 +148,11 @@ function AlertButton({ message, children }) { export default function Toolbar() { return ( <div> - <AlertButton message="Playing!"> - Play Movie + <AlertButton message="Đang phát!"> + Phát phim </AlertButton> - <AlertButton message="Uploading!"> - Upload Image + <AlertButton message="Đang tải lên!"> + Tải lên ảnh </AlertButton> </div> ); @@ -165,13 +165,13 @@ button { margin-right: 10px; } </Sandpack> -This lets these two buttons show different messages. Try changing the messages passed to them. +Điều này cho phép hai nút này hiển thị các thông báo khác nhau. Hãy thử thay đổi các thông báo được truyền cho chúng. -### Passing event handlers as props {/*passing-event-handlers-as-props*/} +### Truyền trình xử lý sự kiện như props {/*passing-event-handlers-as-props*/} -Often you'll want the parent component to specify a child's event handler. Consider buttons: depending on where you're using a `Button` component, you might want to execute a different function—perhaps one plays a movie and another uploads an image. +Thông thường, bạn sẽ muốn component cha chỉ định trình xử lý sự kiện của một component con. Hãy xem xét các nút: tùy thuộc vào nơi bạn đang sử dụng component `Button`, bạn có thể muốn thực thi một hàm khác—có lẽ một hàm phát một bộ phim và một hàm khác tải lên một hình ảnh. -To do this, pass a prop the component receives from its parent as the event handler like so: +Để thực hiện việc này, hãy truyền một prop mà component nhận được từ cha của nó làm trình xử lý sự kiện như sau: <Sandpack> @@ -186,20 +186,20 @@ function Button({ onClick, children }) { function PlayButton({ movieName }) { function handlePlayClick() { - alert(`Playing ${movieName}!`); + alert(`Đang phát ${movieName}!`); } return ( <Button onClick={handlePlayClick}> - Play "{movieName}" + Phát "{movieName}" </Button> ); } function UploadButton() { return ( - <Button onClick={() => alert('Uploading!')}> - Upload Image + <Button onClick={() => alert('Đang tải lên!')}> + Tải lên ảnh </Button> ); } @@ -207,7 +207,7 @@ function UploadButton() { export default function Toolbar() { return ( <div> - <PlayButton movieName="Kiki's Delivery Service" /> + <PlayButton movieName="Dịch vụ giao hàng của Kiki" /> <UploadButton /> </div> ); @@ -220,22 +220,22 @@ button { margin-right: 10px; } </Sandpack> -Here, the `Toolbar` component renders a `PlayButton` and an `UploadButton`: +Ở đây, component `Toolbar` render một `PlayButton` và một `UploadButton`: -- `PlayButton` passes `handlePlayClick` as the `onClick` prop to the `Button` inside. -- `UploadButton` passes `() => alert('Uploading!')` as the `onClick` prop to the `Button` inside. +- `PlayButton` truyền `handlePlayClick` làm prop `onClick` cho `Button` bên trong. +- `UploadButton` truyền `() => alert('Đang tải lên!')` làm prop `onClick` cho `Button` bên trong. -Finally, your `Button` component accepts a prop called `onClick`. It passes that prop directly to the built-in browser `<button>` with `onClick={onClick}`. This tells React to call the passed function on click. +Cuối cùng, component `Button` của bạn chấp nhận một prop có tên là `onClick`. Nó truyền prop đó trực tiếp đến trình duyệt tích hợp sẵn `<button>` với `onClick={onClick}`. Điều này cho React biết để gọi hàm được truyền khi nhấp vào. -If you use a [design system](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969), it's common for components like buttons to contain styling but not specify behavior. Instead, components like `PlayButton` and `UploadButton` will pass event handlers down. +Nếu bạn sử dụng [hệ thống thiết kế](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969), thì các component như nút thường chứa kiểu dáng nhưng không chỉ định hành vi. Thay vào đó, các component như `PlayButton` và `UploadButton` sẽ truyền các trình xử lý sự kiện xuống. -### Naming event handler props {/*naming-event-handler-props*/} +### Đặt tên cho các props của trình xử lý sự kiện {/*naming-event-handler-props*/} -Built-in components like `<button>` and `<div>` only support [browser event names](/reference/react-dom/components/common#common-props) like `onClick`. However, when you're building your own components, you can name their event handler props any way that you like. +Các component tích hợp sẵn như `<button>` và `<div>` chỉ hỗ trợ [tên sự kiện của trình duyệt](/reference/react-dom/components/common#common-props) như `onClick`. Tuy nhiên, khi bạn đang xây dựng các component của riêng mình, bạn có thể đặt tên cho các props của trình xử lý sự kiện của chúng theo bất kỳ cách nào bạn muốn. -By convention, event handler props should start with `on`, followed by a capital letter. +Theo quy ước, các props của trình xử lý sự kiện nên bắt đầu bằng `on`, theo sau là một chữ cái viết hoa. -For example, the `Button` component's `onClick` prop could have been called `onSmash`: +Ví dụ: prop `onClick` của component `Button` có thể được gọi là `onSmash`: <Sandpack> @@ -251,11 +251,11 @@ function Button({ onSmash, children }) { export default function App() { return ( <div> - <Button onSmash={() => alert('Playing!')}> - Play Movie + <Button onSmash={() => alert('Đang phát!')}> + Phát phim </Button> - <Button onSmash={() => alert('Uploading!')}> - Upload Image + <Button onSmash={() => alert('Đang tải lên!')}> + Tải lên ảnh </Button> </div> ); @@ -268,9 +268,9 @@ button { margin-right: 10px; } </Sandpack> -In this example, `<button onClick={onSmash}>` shows that the browser `<button>` (lowercase) still needs a prop called `onClick`, but the prop name received by your custom `Button` component is up to you! +Trong ví dụ này, `<button onClick={onSmash}>` cho thấy rằng trình duyệt `<button>` (chữ thường) vẫn cần một prop có tên là `onClick`, nhưng tên prop mà component `Button` tùy chỉnh của bạn nhận được là tùy thuộc vào bạn! -When your component supports multiple interactions, you might name event handler props for app-specific concepts. For example, this `Toolbar` component receives `onPlayMovie` and `onUploadImage` event handlers: +Khi component của bạn hỗ trợ nhiều tương tác, bạn có thể đặt tên cho các props của trình xử lý sự kiện cho các khái niệm dành riêng cho ứng dụng. Ví dụ: component `Toolbar` này nhận các trình xử lý sự kiện `onPlayMovie` và `onUploadImage`: <Sandpack> @@ -278,8 +278,8 @@ When your component supports multiple interactions, you might name event handler export default function App() { return ( <Toolbar - onPlayMovie={() => alert('Playing!')} - onUploadImage={() => alert('Uploading!')} + onPlayMovie={() => alert('Đang phát!')} + onUploadImage={() => alert('Đang tải lên!')} /> ); } @@ -288,10 +288,10 @@ function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> - Play Movie + Phát phim </Button> <Button onClick={onUploadImage}> - Upload Image + Tải lên ảnh </Button> </div> ); @@ -312,19 +312,19 @@ button { margin-right: 10px; } </Sandpack> -Notice how the `App` component does not need to know *what* `Toolbar` will do with `onPlayMovie` or `onUploadImage`. That's an implementation detail of the `Toolbar`. Here, `Toolbar` passes them down as `onClick` handlers to its `Button`s, but it could later also trigger them on a keyboard shortcut. Naming props after app-specific interactions like `onPlayMovie` gives you the flexibility to change how they're used later. - +Lưu ý cách component `App` không cần biết *những gì* `Toolbar` sẽ làm với `onPlayMovie` hoặc `onUploadImage`. Đó là một chi tiết triển khai của `Toolbar`. Ở đây, `Toolbar` truyền chúng xuống dưới dạng trình xử lý `onClick` cho `Button` của nó, nhưng sau này nó cũng có thể kích hoạt chúng trên một phím tắt. Đặt tên cho các props theo các tương tác dành riêng cho ứng dụng như `onPlayMovie` mang lại cho bạn sự linh hoạt để thay đổi cách chúng được sử dụng sau này. + <Note> -Make sure that you use the appropriate HTML tags for your event handlers. For example, to handle clicks, use [`<button onClick={handleClick}>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) instead of `<div onClick={handleClick}>`. Using a real browser `<button>` enables built-in browser behaviors like keyboard navigation. If you don't like the default browser styling of a button and want to make it look more like a link or a different UI element, you can achieve it with CSS. [Learn more about writing accessible markup.](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) - +Hãy chắc chắn rằng bạn sử dụng các thẻ HTML thích hợp cho các trình xử lý sự kiện của bạn. Ví dụ: để xử lý các nhấp chuột, hãy sử dụng [`<button onClick={handleClick}>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) thay vì `<div onClick={handleClick}>`. Sử dụng một trình duyệt `<button>` thực cho phép các hành vi tích hợp sẵn của trình duyệt như điều hướng bằng bàn phím. Nếu bạn không thích kiểu trình duyệt mặc định của một nút và muốn làm cho nó trông giống một liên kết hoặc một phần tử UI khác, bạn có thể đạt được nó bằng CSS. [Tìm hiểu thêm về viết mã đánh dấu có thể truy cập.](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML) + </Note> -## Event propagation {/*event-propagation*/} +## Sự kiện lan truyền {/*event-propagation*/} -Event handlers will also catch events from any children your component might have. We say that an event "bubbles" or "propagates" up the tree: it starts with where the event happened, and then goes up the tree. +Trình xử lý sự kiện cũng sẽ bắt các sự kiện từ bất kỳ component con nào mà component của bạn có thể có. Chúng ta nói rằng một sự kiện "nổi bọt" hoặc "lan truyền" lên cây: nó bắt đầu với nơi sự kiện xảy ra, và sau đó đi lên cây. -This `<div>` contains two buttons. Both the `<div>` *and* each button have their own `onClick` handlers. Which handlers do you think will fire when you click a button? +`<div>` này chứa hai nút. Cả `<div>` *và* mỗi nút đều có trình xử lý `onClick` riêng. Bạn nghĩ trình xử lý nào sẽ kích hoạt khi bạn nhấp vào một nút? <Sandpack> @@ -332,13 +332,13 @@ This `<div>` contains two buttons. Both the `<div>` *and* each button have their export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { - alert('You clicked on the toolbar!'); + alert('Bạn đã nhấp vào thanh công cụ!'); }}> - <button onClick={() => alert('Playing!')}> - Play Movie + <button onClick={() => alert('Đang phát!')}> + Phát phim </button> - <button onClick={() => alert('Uploading!')}> - Upload Image + <button onClick={() => alert('Đang tải lên!')}> + Tải lên ảnh </button> </div> ); @@ -355,19 +355,19 @@ button { margin: 5px; } </Sandpack> -If you click on either button, its `onClick` will run first, followed by the parent `<div>`'s `onClick`. So two messages will appear. If you click the toolbar itself, only the parent `<div>`'s `onClick` will run. +Nếu bạn nhấp vào một trong hai nút, `onClick` của nó sẽ chạy trước, sau đó là `onClick` của `<div>` cha. Vì vậy, hai thông báo sẽ xuất hiện. Nếu bạn nhấp vào chính thanh công cụ, chỉ `onClick` của `<div>` cha sẽ chạy. <Pitfall> -All events propagate in React except `onScroll`, which only works on the JSX tag you attach it to. +Tất cả các sự kiện lan truyền trong React ngoại trừ `onScroll`, chỉ hoạt động trên thẻ JSX mà bạn gắn nó vào. </Pitfall> -### Stopping propagation {/*stopping-propagation*/} +### Dừng lan truyền {/*stopping-propagation*/} -Event handlers receive an **event object** as their only argument. By convention, it's usually called `e`, which stands for "event". You can use this object to read information about the event. +Trình xử lý sự kiện nhận một **đối tượng sự kiện** làm đối số duy nhất của chúng. Theo quy ước, nó thường được gọi là `e`, viết tắt của "event". Bạn có thể sử dụng đối tượng này để đọc thông tin về sự kiện. -That event object also lets you stop the propagation. If you want to prevent an event from reaching parent components, you need to call `e.stopPropagation()` like this `Button` component does: +Đối tượng sự kiện đó cũng cho phép bạn dừng lan truyền. Nếu bạn muốn ngăn một sự kiện tiếp cận các component cha, bạn cần gọi `e.stopPropagation()` như component `Button` này: <Sandpack> @@ -386,13 +386,13 @@ function Button({ onClick, children }) { export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { - alert('You clicked on the toolbar!'); + alert('Bạn đã nhấp vào thanh công cụ!'); }}> - <Button onClick={() => alert('Playing!')}> - Play Movie + <Button onClick={() => alert('Đang phát!')}> + Phát phim </Button> - <Button onClick={() => alert('Uploading!')}> - Upload Image + <Button onClick={() => alert('Đang tải lên!')}> + Tải lên ảnh </Button> </div> ); @@ -409,43 +409,43 @@ button { margin: 5px; } </Sandpack> -When you click on a button: +Khi bạn nhấp vào một nút: -1. React calls the `onClick` handler passed to `<button>`. -2. That handler, defined in `Button`, does the following: - * Calls `e.stopPropagation()`, preventing the event from bubbling further. - * Calls the `onClick` function, which is a prop passed from the `Toolbar` component. -3. That function, defined in the `Toolbar` component, displays the button's own alert. -4. Since the propagation was stopped, the parent `<div>`'s `onClick` handler does *not* run. +1. React gọi trình xử lý `onClick` được truyền cho `<button>`. +2. Trình xử lý đó, được định nghĩa trong `Button`, thực hiện như sau: + * Gọi `e.stopPropagation()`, ngăn sự kiện nổi bọt thêm. + * Gọi hàm `onClick`, là một prop được truyền từ component `Toolbar`. +3. Hàm đó, được định nghĩa trong component `Toolbar`, hiển thị cảnh báo riêng của nút. +4. Vì sự lan truyền đã bị dừng, trình xử lý `onClick` của `<div>` cha *không* chạy. -As a result of `e.stopPropagation()`, clicking on the buttons now only shows a single alert (from the `<button>`) rather than the two of them (from the `<button>` and the parent toolbar `<div>`). Clicking a button is not the same thing as clicking the surrounding toolbar, so stopping the propagation makes sense for this UI. +Do `e.stopPropagation()`, việc nhấp vào các nút bây giờ chỉ hiển thị một cảnh báo duy nhất (từ `<button>`) thay vì cả hai (từ `<button>` và thanh công cụ `<div>` cha). Nhấp vào một nút không giống như nhấp vào thanh công cụ xung quanh, vì vậy việc dừng lan truyền có ý nghĩa đối với UI này. <DeepDive> -#### Capture phase events {/*capture-phase-events*/} +#### Sự kiện giai đoạn chụp {/*capture-phase-events*/} -In rare cases, you might need to catch all events on child elements, *even if they stopped propagation*. For example, maybe you want to log every click to analytics, regardless of the propagation logic. You can do this by adding `Capture` at the end of the event name: +Trong một số trường hợp hiếm hoi, bạn có thể cần bắt tất cả các sự kiện trên các phần tử con, *ngay cả khi chúng đã dừng lan truyền*. Ví dụ: có thể bạn muốn ghi lại mọi nhấp chuột vào phân tích, bất kể logic lan truyền. Bạn có thể làm điều này bằng cách thêm `Capture` vào cuối tên sự kiện: ```js -<div onClickCapture={() => { /* this runs first */ }}> +<div onClickCapture={() => { /* điều này chạy trước */ }}> <button onClick={e => e.stopPropagation()} /> <button onClick={e => e.stopPropagation()} /> </div> ``` -Each event propagates in three phases: +Mỗi sự kiện lan truyền trong ba giai đoạn: -1. It travels down, calling all `onClickCapture` handlers. -2. It runs the clicked element's `onClick` handler. -3. It travels upwards, calling all `onClick` handlers. +1. Nó di chuyển xuống, gọi tất cả các trình xử lý `onClickCapture`. +2. Nó chạy trình xử lý `onClick` của phần tử được nhấp. +3. Nó di chuyển lên trên, gọi tất cả các trình xử lý `onClick`. -Capture events are useful for code like routers or analytics, but you probably won't use them in app code. +Sự kiện chụp rất hữu ích cho mã như bộ định tuyến hoặc phân tích, nhưng bạn có thể sẽ không sử dụng chúng trong mã ứng dụng. </DeepDive> -### Passing handlers as alternative to propagation {/*passing-handlers-as-alternative-to-propagation*/} +### Truyền trình xử lý như một giải pháp thay thế cho lan truyền {/*passing-handlers-as-alternative-to-propagation*/} -Notice how this click handler runs a line of code _and then_ calls the `onClick` prop passed by the parent: +Lưu ý cách trình xử lý nhấp chuột này chạy một dòng mã _và sau đó_ gọi prop `onClick` được truyền bởi cha: ```js {4,5} function Button({ onClick, children }) { @@ -460,22 +460,22 @@ function Button({ onClick, children }) { } ``` -You could add more code to this handler before calling the parent `onClick` event handler, too. This pattern provides an *alternative* to propagation. It lets the child component handle the event, while also letting the parent component specify some additional behavior. Unlike propagation, it's not automatic. But the benefit of this pattern is that you can clearly follow the whole chain of code that executes as a result of some event. +Bạn cũng có thể thêm nhiều mã hơn vào trình xử lý này trước khi gọi trình xử lý sự kiện `onClick` của cha. Mẫu này cung cấp một *giải pháp thay thế* cho lan truyền. Nó cho phép component con xử lý sự kiện, đồng thời cho phép component cha chỉ định một số hành vi bổ sung. Không giống như lan truyền, nó không tự động. Nhưng lợi ích của mẫu này là bạn có thể theo dõi rõ ràng toàn bộ chuỗi mã thực thi do một số sự kiện gây ra. -If you rely on propagation and it's difficult to trace which handlers execute and why, try this approach instead. +Nếu bạn dựa vào lan truyền và khó theo dõi trình xử lý nào thực thi và tại sao, hãy thử phương pháp này thay thế. -### Preventing default behavior {/*preventing-default-behavior*/} +### Ngăn chặn hành vi mặc định {/*preventing-default-behavior*/} -Some browser events have default behavior associated with them. For example, a `<form>` submit event, which happens when a button inside of it is clicked, will reload the whole page by default: +Một số sự kiện của trình duyệt có hành vi mặc định liên quan đến chúng. Ví dụ: một sự kiện gửi `<form>`, xảy ra khi một nút bên trong nó được nhấp vào, sẽ tải lại toàn bộ trang theo mặc định: <Sandpack> ```js export default function Signup() { return ( - <form onSubmit={() => alert('Submitting!')}> + <form onSubmit={() => alert('Đang gửi!')}> <input /> - <button>Send</button> + <button>Gửi</button> </form> ); } @@ -487,7 +487,7 @@ button { margin-left: 5px; } </Sandpack> -You can call `e.preventDefault()` on the event object to stop this from happening: +Bạn có thể gọi `e.preventDefault()` trên đối tượng sự kiện để ngăn điều này xảy ra: <Sandpack> @@ -496,10 +496,10 @@ export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); - alert('Submitting!'); + alert('Đang gửi!'); }}> <input /> - <button>Send</button> + <button>Gửi</button> </form> ); } @@ -511,38 +511,36 @@ button { margin-left: 5px; } </Sandpack> -Don't confuse `e.stopPropagation()` and `e.preventDefault()`. They are both useful, but are unrelated: +Đừng nhầm lẫn `e.stopPropagation()` và `e.preventDefault()`. Cả hai đều hữu ích, nhưng không liên quan: -* [`e.stopPropagation()`](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) stops the event handlers attached to the tags above from firing. -* [`e.preventDefault()` ](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) prevents the default browser behavior for the few events that have it. +* [`e.stopPropagation()`](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation) ngăn các trình xử lý sự kiện được gắn vào các thẻ ở trên kích hoạt. +* [`e.preventDefault()` ](https://developer.mozilla.org/docs/Web/API/Event/preventDefault) ngăn chặn hành vi mặc định của trình duyệt cho một số ít sự kiện có nó. -## Can event handlers have side effects? {/*can-event-handlers-have-side-effects*/} +## Trình xử lý sự kiện có thể có tác dụng phụ không? {/*can-event-handlers-have-side-effects*/} -Absolutely! Event handlers are the best place for side effects. +Chắc chắn rồi! Trình xử lý sự kiện là nơi tốt nhất cho các tác dụng phụ. -Unlike rendering functions, event handlers don't need to be [pure](/learn/keeping-components-pure), so it's a great place to *change* something—for example, change an input's value in response to typing, or change a list in response to a button press. However, in order to change some information, you first need some way to store it. In React, this is done by using [state, a component's memory.](/learn/state-a-components-memory) You will learn all about it on the next page. +Không giống như các hàm render, trình xử lý sự kiện không cần phải [thuần khiết](/learn/keeping-components-pure), vì vậy đó là một nơi tuyệt vời để *thay đổi* một cái gì đó—ví dụ: thay đổi giá trị của một đầu vào để đáp ứng việc nhập, hoặc thay đổi một danh sách để đáp ứng một lần nhấn nút. Tuy nhiên, để thay đổi một số thông tin, trước tiên bạn cần một số cách để lưu trữ nó. Trong React, điều này được thực hiện bằng cách sử dụng [state, bộ nhớ của một component.](/learn/state-a-components-memory) Bạn sẽ tìm hiểu tất cả về nó trên trang tiếp theo. <Recap> -* You can handle events by passing a function as a prop to an element like `<button>`. -* Event handlers must be passed, **not called!** `onClick={handleClick}`, not `onClick={handleClick()}`. -* You can define an event handler function separately or inline. -* Event handlers are defined inside a component, so they can access props. -* You can declare an event handler in a parent and pass it as a prop to a child. -* You can define your own event handler props with application-specific names. -* Events propagate upwards. Call `e.stopPropagation()` on the first argument to prevent that. -* Events may have unwanted default browser behavior. Call `e.preventDefault()` to prevent that. -* Explicitly calling an event handler prop from a child handler is a good alternative to propagation. +* Bạn có thể xử lý các sự kiện bằng cách truyền một hàm làm prop cho một phần tử như `<button>`. +* Trình xử lý sự kiện phải được truyền, **không được gọi!** `onClick={handleClick}`, không phải `onClick={handleClick()}`. +* Bạn có thể định nghĩa một hàm xử lý sự kiện riêng biệt hoặc nội tuyến. +* Trình xử lý sự kiện được định nghĩa bên trong một component, vì vậy chúng có thể truy cập các props. +* Bạn có thể khai báo một trình xử lý sự kiện trong một component cha và truyền nó như một prop cho một component con. +* Bạn có thể xác định các props của trình xử lý sự kiện của riêng bạn với các tên dành riêng cho ứng dụng. +* Các sự kiện lan truyền lên trên. Gọi `e.stopPropagation()` trên đối số đầu tiên để ngăn chặn điều đó. +* Các sự kiện có thể có hành vi mặc định không mong muốn của trình duyệt. Gọi `e.preventDefault()` để ngăn chặn điều đó. +* Gọi rõ ràng một prop của trình xử lý sự kiện từ một trình xử lý con là một giải pháp thay thế tốt cho lan truyền. </Recap> - - <Challenges> -#### Fix an event handler {/*fix-an-event-handler*/} +#### Sửa một trình xử lý sự kiện {/*fix-an-event-handler*/} -Clicking this button is supposed to switch the page background between white and black. However, nothing happens when you click it. Fix the problem. (Don't worry about the logic inside `handleClick`—that part is fine.) +Việc nhấp vào nút này được cho là chuyển đổi nền trang giữa trắng và đen. Tuy nhiên, không có gì xảy ra khi bạn nhấp vào nó. Sửa vấn đề. (Đừng lo lắng về logic bên trong `handleClick`—phần đó vẫn ổn.) <Sandpack> @@ -559,7 +557,7 @@ export default function LightSwitch() { return ( <button onClick={handleClick()}> - Toggle the lights + Bật tắt đèn </button> ); } @@ -569,7 +567,7 @@ export default function LightSwitch() { <Solution> -The problem is that `<button onClick={handleClick()}>` _calls_ the `handleClick` function while rendering instead of _passing_ it. Removing the `()` call so that it's `<button onClick={handleClick}>` fixes the issue: +Vấn đề là `<button onClick={handleClick()}>` _gọi_ hàm `handleClick` trong khi render thay vì _truyền_ nó. Xóa lệnh gọi `()` để nó là `<button onClick={handleClick}>` sẽ khắc phục sự cố: <Sandpack> @@ -586,7 +584,7 @@ export default function LightSwitch() { return ( <button onClick={handleClick}> - Toggle the lights + Bật tắt đèn </button> ); } @@ -594,7 +592,7 @@ export default function LightSwitch() { </Sandpack> -Alternatively, you could wrap the call into another function, like `<button onClick={() => handleClick()}>`: +Ngoài ra, bạn có thể bọc lệnh gọi vào một hàm khác, như `<button onClick={() => handleClick()}>`: <Sandpack> @@ -611,7 +609,7 @@ export default function LightSwitch() { return ( <button onClick={() => handleClick()}> - Toggle the lights + Bật tắt đèn </button> ); } @@ -621,11 +619,11 @@ export default function LightSwitch() { </Solution> -#### Wire up the events {/*wire-up-the-events*/} +#### Kết nối các sự kiện {/*wire-up-the-events*/} -This `ColorSwitch` component renders a button. It's supposed to change the page color. Wire it up to the `onChangeColor` event handler prop it receives from the parent so that clicking the button changes the color. +Component `ColorSwitch` này render một nút. Nó được cho là thay đổi màu trang. Kết nối nó với prop trình xử lý sự kiện `onChangeColor` mà nó nhận được từ cha để việc nhấp vào nút sẽ thay đổi màu. -After you do this, notice that clicking the button also increments the page click counter. Your colleague who wrote the parent component insists that `onChangeColor` does not increment any counters. What else might be happening? Fix it so that clicking the button *only* changes the color, and does _not_ increment the counter. +Sau khi bạn làm điều này, hãy lưu ý rằng việc nhấp vào nút cũng làm tăng bộ đếm nhấp chuột của trang. Đồng nghiệp của bạn, người đã viết component cha, khẳng định rằng `onChangeColor` không tăng bất kỳ bộ đếm nào. Điều gì khác có thể xảy ra? Sửa nó để việc nhấp vào nút *chỉ* thay đổi màu và _không_ tăng bộ đếm. <Sandpack> @@ -635,7 +633,7 @@ export default function ColorSwitch({ }) { return ( <button> - Change color + Thay đổi màu </button> ); } @@ -669,7 +667,7 @@ export default function App() { <ColorSwitch onChangeColor={handleChangeColor} /> <br /> <br /> - <h2>Clicks on the page: {clicks}</h2> + <h2>Số lần nhấp trên trang: {clicks}</h2> </div> ); } @@ -679,9 +677,9 @@ export default function App() { <Solution> -First, you need to add the event handler, like `<button onClick={onChangeColor}>`. +Đầu tiên, bạn cần thêm trình xử lý sự kiện, như `<button onClick={onChangeColor}>`. -However, this introduces the problem of the incrementing counter. If `onChangeColor` does not do this, as your colleague insists, then the problem is that this event propagates up, and some handler above does it. To solve this problem, you need to stop the propagation. But don't forget that you should still call `onChangeColor`. +Tuy nhiên, điều này giới thiệu vấn đề về bộ đếm tăng dần. Nếu `onChangeColor` không làm điều này, như đồng nghiệp của bạn khẳng định, thì vấn đề là sự kiện này lan truyền lên trên và một số trình xử lý ở trên thực hiện nó. Để giải quyết vấn đề này, bạn cần dừng lan truyền. Nhưng đừng quên rằng bạn vẫn nên gọi `onChangeColor`. <Sandpack> @@ -694,7 +692,7 @@ export default function ColorSwitch({ e.stopPropagation(); onChangeColor(); }}> - Change color + Thay đổi màu </button> ); } @@ -728,7 +726,7 @@ export default function App() { <ColorSwitch onChangeColor={handleChangeColor} /> <br /> <br /> - <h2>Clicks on the page: {clicks}</h2> + <h2>Số lần nhấp trên trang: {clicks}</h2> </div> ); } diff --git a/src/content/learn/reusing-logic-with-custom-hooks.md b/src/content/learn/reusing-logic-with-custom-hooks.md index ea8d0a313..c8f923a6d 100644 --- a/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/src/content/learn/reusing-logic-with-custom-hooks.md @@ -1091,15 +1091,15 @@ button { margin-left: 10px; } </Sandpack> -Notice how you no longer need to know *how* `useChatRoom` works in order to use it. You could add it to any other component, pass any other options, and it would work the same way. That's the power of custom Hooks. +Lưu ý rằng bạn không còn cần phải biết *cách* `useChatRoom` hoạt động để sử dụng nó. Bạn có thể thêm nó vào bất kỳ component nào khác, truyền bất kỳ tùy chọn nào khác và nó sẽ hoạt động theo cùng một cách. Đó là sức mạnh của Custom Hook. -## When to use custom Hooks {/*when-to-use-custom-hooks*/} +## Khi nào nên sử dụng Custom Hook {/*when-to-use-custom-hooks*/} -You don't need to extract a custom Hook for every little duplicated bit of code. Some duplication is fine. For example, extracting a `useFormInput` Hook to wrap a single `useState` call like earlier is probably unnecessary. +Bạn không cần phải trích xuất một Custom Hook cho mọi đoạn code trùng lặp nhỏ. Một số trùng lặp là chấp nhận được. Ví dụ: trích xuất một Hook `useFormInput` để bọc một lệnh gọi `useState` duy nhất như trước đó có lẽ là không cần thiết. -However, whenever you write an Effect, consider whether it would be clearer to also wrap it in a custom Hook. [You shouldn't need Effects very often,](/learn/you-might-not-need-an-effect) so if you're writing one, it means that you need to "step outside React" to synchronize with some external system or to do something that React doesn't have a built-in API for. Wrapping it into a custom Hook lets you precisely communicate your intent and how the data flows through it. +Tuy nhiên, bất cứ khi nào bạn viết một Effect, hãy cân nhắc xem liệu việc bọc nó trong một Custom Hook có rõ ràng hơn không. [Bạn không nên cần Effect quá thường xuyên,](/learn/you-might-not-need-an-effect) vì vậy nếu bạn đang viết một Effect, điều đó có nghĩa là bạn cần "bước ra ngoài React" để đồng bộ hóa với một số hệ thống bên ngoài hoặc để làm điều gì đó mà React không có API tích hợp sẵn. Việc bọc nó vào một Custom Hook cho phép bạn truyền đạt chính xác ý định của mình và cách dữ liệu luân chuyển qua nó. -For example, consider a `ShippingForm` component that displays two dropdowns: one shows the list of cities, and another shows the list of areas in the selected city. You might start with some code that looks like this: +Ví dụ: hãy xem xét một component `ShippingForm` hiển thị hai dropdown: một hiển thị danh sách các thành phố và một hiển thị danh sách các khu vực trong thành phố đã chọn. Bạn có thể bắt đầu với một số code trông như thế này: ```js {3-16,20-35} function ShippingForm({ country }) { @@ -1141,7 +1141,7 @@ function ShippingForm({ country }) { // ... ``` -Although this code is quite repetitive, [it's correct to keep these Effects separate from each other.](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things) They synchronize two different things, so you shouldn't merge them into one Effect. Instead, you can simplify the `ShippingForm` component above by extracting the common logic between them into your own `useData` Hook: +Mặc dù code này khá lặp đi lặp lại, [việc giữ các Effect này tách biệt nhau là đúng.](/learn/removing-effect-dependencies#is-your-effect-doing-several-unrelated-things) Chúng đồng bộ hóa hai thứ khác nhau, vì vậy bạn không nên hợp nhất chúng thành một Effect. Thay vào đó, bạn có thể đơn giản hóa component `ShippingForm` ở trên bằng cách trích xuất logic chung giữa chúng vào Hook `useData` của riêng bạn: ```js {2-18} function useData(url) { @@ -1165,7 +1165,7 @@ function useData(url) { } ``` -Now you can replace both Effects in the `ShippingForm` components with calls to `useData`: +Bây giờ bạn có thể thay thế cả hai Effect trong các component `ShippingForm` bằng các lệnh gọi đến `useData`: ```js {2,4} function ShippingForm({ country }) { @@ -1175,39 +1175,39 @@ function ShippingForm({ country }) { // ... ``` -Extracting a custom Hook makes the data flow explicit. You feed the `url` in and you get the `data` out. By "hiding" your Effect inside `useData`, you also prevent someone working on the `ShippingForm` component from adding [unnecessary dependencies](/learn/removing-effect-dependencies) to it. With time, most of your app's Effects will be in custom Hooks. +Việc trích xuất một Custom Hook làm cho luồng dữ liệu trở nên rõ ràng. Bạn đưa `url` vào và bạn nhận được `data` ra. Bằng cách "ẩn" Effect của bạn bên trong `useData`, bạn cũng ngăn ai đó làm việc trên component `ShippingForm` thêm [các dependency không cần thiết](/learn/removing-effect-dependencies) vào nó. Theo thời gian, hầu hết các Effect của ứng dụng của bạn sẽ nằm trong Custom Hook. <DeepDive> -#### Keep your custom Hooks focused on concrete high-level use cases {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} +#### Tập trung Custom Hook của bạn vào các trường hợp sử dụng cấp cao cụ thể {/*keep-your-custom-hooks-focused-on-concrete-high-level-use-cases*/} -Start by choosing your custom Hook's name. If you struggle to pick a clear name, it might mean that your Effect is too coupled to the rest of your component's logic, and is not yet ready to be extracted. +Bắt đầu bằng cách chọn tên cho Custom Hook của bạn. Nếu bạn gặp khó khăn trong việc chọn một cái tên rõ ràng, điều đó có thể có nghĩa là Effect của bạn quá gắn liền với phần còn lại của logic component của bạn và chưa sẵn sàng để được trích xuất. -Ideally, your custom Hook's name should be clear enough that even a person who doesn't write code often could have a good guess about what your custom Hook does, what it takes, and what it returns: +Lý tưởng nhất là tên Custom Hook của bạn phải đủ rõ ràng để ngay cả một người không viết code thường xuyên cũng có thể đoán được Custom Hook của bạn làm gì, nó nhận gì và nó trả về gì: * ✅ `useData(url)` * ✅ `useImpressionLog(eventName, extraData)` * ✅ `useChatRoom(options)` -When you synchronize with an external system, your custom Hook name may be more technical and use jargon specific to that system. It's good as long as it would be clear to a person familiar with that system: +Khi bạn đồng bộ hóa với một hệ thống bên ngoài, tên Custom Hook của bạn có thể mang tính kỹ thuật hơn và sử dụng biệt ngữ dành riêng cho hệ thống đó. Điều đó tốt miễn là nó rõ ràng đối với một người quen thuộc với hệ thống đó: * ✅ `useMediaQuery(query)` * ✅ `useSocket(url)` * ✅ `useIntersectionObserver(ref, options)` -**Keep custom Hooks focused on concrete high-level use cases.** Avoid creating and using custom "lifecycle" Hooks that act as alternatives and convenience wrappers for the `useEffect` API itself: +**Tập trung Custom Hook vào các trường hợp sử dụng cấp cao cụ thể.** Tránh tạo và sử dụng Custom Hook "vòng đời" hoạt động như các giải pháp thay thế và trình bao bọc tiện lợi cho chính API `useEffect`: * 🔴 `useMount(fn)` * 🔴 `useEffectOnce(fn)` * 🔴 `useUpdateEffect(fn)` -For example, this `useMount` Hook tries to ensure some code only runs "on mount": +Ví dụ: Hook `useMount` này cố gắng đảm bảo một số code chỉ chạy "khi mount": ```js {4-5,14-15} function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // 🔴 Avoid: using custom "lifecycle" Hooks + // 🔴 Tránh: sử dụng Custom Hook "vòng đời" useMount(() => { const connection = createConnection({ roomId, serverUrl }); connection.connect(); @@ -1217,7 +1217,7 @@ function ChatRoom({ roomId }) { // ... } -// 🔴 Avoid: creating custom "lifecycle" Hooks +// 🔴 Tránh: tạo Custom Hook "vòng đời" function useMount(fn) { useEffect(() => { fn(); @@ -1225,15 +1225,15 @@ function useMount(fn) { } ``` -**Custom "lifecycle" Hooks like `useMount` don't fit well into the React paradigm.** For example, this code example has a mistake (it doesn't "react" to `roomId` or `serverUrl` changes), but the linter won't warn you about it because the linter only checks direct `useEffect` calls. It won't know about your Hook. +**Custom Hook "vòng đời" như `useMount` không phù hợp với mô hình React.** Ví dụ: ví dụ code này có một lỗi (nó không "phản ứng" với các thay đổi `roomId` hoặc `serverUrl`), nhưng trình lint sẽ không cảnh báo bạn về điều đó vì trình lint chỉ kiểm tra các lệnh gọi `useEffect` trực tiếp. Nó sẽ không biết về Hook của bạn. -If you're writing an Effect, start by using the React API directly: +Nếu bạn đang viết một Effect, hãy bắt đầu bằng cách sử dụng trực tiếp API React: ```js function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // ✅ Good: two raw Effects separated by purpose + // ✅ Tốt: hai Effect thô được phân tách theo mục đích useEffect(() => { const connection = createConnection({ serverUrl, roomId }); @@ -1249,28 +1249,28 @@ function ChatRoom({ roomId }) { } ``` -Then, you can (but don't have to) extract custom Hooks for different high-level use cases: +Sau đó, bạn có thể (nhưng không bắt buộc) trích xuất Custom Hook cho các trường hợp sử dụng cấp cao khác nhau: ```js function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); - // ✅ Great: custom Hooks named after their purpose + // ✅ Tuyệt vời: Custom Hook được đặt tên theo mục đích của chúng useChatRoom({ serverUrl, roomId }); useImpressionLog('visit_chat', { roomId }); // ... } ``` -**A good custom Hook makes the calling code more declarative by constraining what it does.** For example, `useChatRoom(options)` can only connect to the chat room, while `useImpressionLog(eventName, extraData)` can only send an impression log to the analytics. If your custom Hook API doesn't constrain the use cases and is very abstract, in the long run it's likely to introduce more problems than it solves. +**Một Custom Hook tốt làm cho code gọi trở nên khai báo hơn bằng cách hạn chế những gì nó làm.** Ví dụ: `useChatRoom(options)` chỉ có thể kết nối với phòng chat, trong khi `useImpressionLog(eventName, extraData)` chỉ có thể gửi nhật ký hiển thị đến phân tích. Nếu API Custom Hook của bạn không hạn chế các trường hợp sử dụng và rất trừu tượng, về lâu dài, nó có khả năng gây ra nhiều vấn đề hơn là giải quyết. </DeepDive> -### Custom Hooks help you migrate to better patterns {/*custom-hooks-help-you-migrate-to-better-patterns*/} +### Custom Hook giúp bạn di chuyển sang các pattern tốt hơn {/*custom-hooks-help-you-migrate-to-better-patterns*/} -Effects are an ["escape hatch"](/learn/escape-hatches): you use them when you need to "step outside React" and when there is no better built-in solution for your use case. With time, the React team's goal is to reduce the number of the Effects in your app to the minimum by providing more specific solutions to more specific problems. Wrapping your Effects in custom Hooks makes it easier to upgrade your code when these solutions become available. +Effect là một ["lối thoát hiểm"](/learn/escape-hatches): bạn sử dụng chúng khi bạn cần "bước ra ngoài React" và khi không có giải pháp tích hợp tốt hơn cho trường hợp sử dụng của bạn. Theo thời gian, mục tiêu của nhóm React là giảm số lượng Effect trong ứng dụng của bạn xuống mức tối thiểu bằng cách cung cấp các giải pháp cụ thể hơn cho các vấn đề cụ thể hơn. Việc bọc Effect của bạn trong Custom Hook giúp bạn dễ dàng nâng cấp code của mình hơn khi các giải pháp này có sẵn. -Let's return to this example: +Hãy quay lại ví dụ này: <Sandpack> @@ -1331,9 +1331,9 @@ export function useOnlineStatus() { </Sandpack> -In the above example, `useOnlineStatus` is implemented with a pair of [`useState`](/reference/react/useState) and [`useEffect`.](/reference/react/useEffect) However, this isn't the best possible solution. There is a number of edge cases it doesn't consider. For example, it assumes that when the component mounts, `isOnline` is already `true`, but this may be wrong if the network already went offline. You can use the browser [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) API to check for that, but using it directly would not work on the server for generating the initial HTML. In short, this code could be improved. +Trong ví dụ trên, `useOnlineStatus` được triển khai với một cặp [`useState`](/reference/react/useState) và [`useEffect`.](/reference/react/useEffect) Tuy nhiên, đây không phải là giải pháp tốt nhất có thể. Có một số trường hợp đặc biệt mà nó không xem xét. Ví dụ: nó giả định rằng khi component mount, `isOnline` đã là `true`, nhưng điều này có thể sai nếu mạng đã ngoại tuyến. Bạn có thể sử dụng API [`navigator.onLine`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/onLine) của trình duyệt để kiểm tra điều đó, nhưng việc sử dụng trực tiếp nó sẽ không hoạt động trên máy chủ để tạo HTML ban đầu. Tóm lại, code này có thể được cải thiện. -React includes a dedicated API called [`useSyncExternalStore`](/reference/react/useSyncExternalStore) which takes care of all of these problems for you. Here is your `useOnlineStatus` Hook, rewritten to take advantage of this new API: +React bao gồm một API chuyên dụng có tên là [`useSyncExternalStore`](/reference/react/useSyncExternalStore) để giải quyết tất cả những vấn đề này cho bạn. Đây là Hook `useOnlineStatus` của bạn, được viết lại để tận dụng API mới này: <Sandpack> @@ -1393,7 +1393,7 @@ export function useOnlineStatus() { </Sandpack> -Notice how **you didn't need to change any of the components** to make this migration: +Lưu ý rằng **bạn không cần phải thay đổi bất kỳ component nào** để thực hiện việc di chuyển này: ```js {2,7} function StatusBar() { @@ -1407,22 +1407,22 @@ function SaveButton() { } ``` -This is another reason for why wrapping Effects in custom Hooks is often beneficial: +Đây là một lý do khác tại sao việc bọc Effect trong Custom Hook thường có lợi: -1. You make the data flow to and from your Effects very explicit. -2. You let your components focus on the intent rather than on the exact implementation of your Effects. -3. When React adds new features, you can remove those Effects without changing any of your components. +1. Bạn làm cho luồng dữ liệu đến và đi từ Effect của bạn rất rõ ràng. +2. Bạn cho phép các component của bạn tập trung vào ý định hơn là vào việc triển khai chính xác Effect của bạn. +3. Khi React thêm các tính năng mới, bạn có thể xóa các Effect đó mà không cần thay đổi bất kỳ component nào của bạn. -Similar to a [design system,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) you might find it helpful to start extracting common idioms from your app's components into custom Hooks. This will keep your components' code focused on the intent, and let you avoid writing raw Effects very often. Many excellent custom Hooks are maintained by the React community. +Tương tự như một [hệ thống thiết kế,](https://uxdesign.cc/everything-you-need-to-know-about-design-systems-54b109851969) bạn có thể thấy hữu ích khi bắt đầu trích xuất các thành ngữ phổ biến từ các component của ứng dụng của bạn vào Custom Hook. Điều này sẽ giúp code của các component của bạn tập trung vào ý định và cho phép bạn tránh viết Effect thô rất thường xuyên. Nhiều Custom Hook tuyệt vời được duy trì bởi cộng đồng React. <DeepDive> -#### Will React provide any built-in solution for data fetching? {/*will-react-provide-any-built-in-solution-for-data-fetching*/} +#### React sẽ cung cấp bất kỳ giải pháp tích hợp sẵn nào để tìm nạp dữ liệu không? {/*will-react-provide-any-built-in-solution-for-data-fetching*/} -We're still working out the details, but we expect that in the future, you'll write data fetching like this: +Chúng tôi vẫn đang hoàn thiện các chi tiết, nhưng chúng tôi hy vọng rằng trong tương lai, bạn sẽ viết code tìm nạp dữ liệu như thế này: ```js {1,4,6} -import { use } from 'react'; // Not available yet! +import { use } from 'react'; // Chưa có sẵn! function ShippingForm({ country }) { const cities = use(fetch(`/api/cities?country=${country}`)); @@ -1431,13 +1431,13 @@ function ShippingForm({ country }) { // ... ``` -If you use custom Hooks like `useData` above in your app, it will require fewer changes to migrate to the eventually recommended approach than if you write raw Effects in every component manually. However, the old approach will still work fine, so if you feel happy writing raw Effects, you can continue to do that. +Nếu bạn sử dụng Custom Hook như `useData` ở trên trong ứng dụng của mình, bạn sẽ yêu cầu ít thay đổi hơn để di chuyển sang phương pháp được đề xuất cuối cùng so với việc bạn viết Effect thô trong mọi component theo cách thủ công. Tuy nhiên, phương pháp cũ vẫn sẽ hoạt động tốt, vì vậy nếu bạn cảm thấy hài lòng khi viết Effect thô, bạn có thể tiếp tục làm điều đó. </DeepDive> -### There is more than one way to do it {/*there-is-more-than-one-way-to-do-it*/} +### Có nhiều hơn một cách để làm điều đó {/*there-is-more-than-one-way-to-do-it*/} -Let's say you want to implement a fade-in animation *from scratch* using the browser [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) API. You might start with an Effect that sets up an animation loop. During each frame of the animation, you could change the opacity of the DOM node you [hold in a ref](/learn/manipulating-the-dom-with-refs) until it reaches `1`. Your code might start like this: +Giả sử bạn muốn triển khai một hiệu ứng hoạt ảnh mờ dần *từ đầu* bằng API [`requestAnimationFrame`](https://developer.mozilla.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) của trình duyệt. Bạn có thể bắt đầu với một Effect thiết lập một vòng lặp hoạt ảnh. Trong mỗi khung hình của hoạt ảnh, bạn có thể thay đổi độ mờ của nút DOM mà bạn [giữ trong một ref](/learn/manipulating-the-dom-with-refs) cho đến khi nó đạt đến `1`. Code của bạn có thể bắt đầu như thế này: <Sandpack> @@ -1520,7 +1520,7 @@ html, body { min-height: 300px; } </Sandpack> -To make the component more readable, you might extract the logic into a `useFadeIn` custom Hook: +Để làm cho component dễ đọc hơn, bạn có thể trích xuất logic vào một Custom Hook `useFadeIn`: <Sandpack> @@ -1611,7 +1611,7 @@ html, body { min-height: 300px; } </Sandpack> -You could keep the `useFadeIn` code as is, but you could also refactor it more. For example, you could extract the logic for setting up the animation loop out of `useFadeIn` into a custom `useAnimationLoop` Hook: +Bạn có thể giữ code `useFadeIn` như hiện tại, nhưng bạn cũng có thể tái cấu trúc nó nhiều hơn. Ví dụ: bạn có thể trích xuất logic để thiết lập vòng lặp hoạt ảnh ra khỏi `useFadeIn` vào một Hook `useAnimationLoop` tùy chỉnh: <Sandpack> @@ -1715,7 +1715,7 @@ html, body { min-height: 300px; } </Sandpack> -However, you didn't *have to* do that. As with regular functions, ultimately you decide where to draw the boundaries between different parts of your code. You could also take a very different approach. Instead of keeping the logic in the Effect, you could move most of the imperative logic inside a JavaScript [class:](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) +Tuy nhiên, bạn không *bắt buộc* phải làm điều đó. Như với các hàm thông thường, cuối cùng bạn quyết định nơi vẽ ranh giới giữa các phần khác nhau của code của bạn. Bạn cũng có thể thực hiện một cách tiếp cận rất khác. Thay vì giữ logic trong Effect, bạn có thể di chuyển hầu hết logic mệnh lệnh bên trong một [class:](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) JavaScript: <Sandpack> @@ -1813,9 +1813,9 @@ html, body { min-height: 300px; } </Sandpack> -Effects let you connect React to external systems. The more coordination between Effects is needed (for example, to chain multiple animations), the more it makes sense to extract that logic out of Effects and Hooks *completely* like in the sandbox above. Then, the code you extracted *becomes* the "external system". This lets your Effects stay simple because they only need to send messages to the system you've moved outside React. +Effect cho phép bạn kết nối React với các hệ thống bên ngoài. Càng cần nhiều sự phối hợp giữa các Effect (ví dụ: để xâu chuỗi nhiều hoạt ảnh), thì càng có ý nghĩa khi trích xuất logic đó ra khỏi Effect và Hook *hoàn toàn* như trong sandbox ở trên. Sau đó, code bạn đã trích xuất *trở thành* "hệ thống bên ngoài". Điều này cho phép Effect của bạn luôn đơn giản vì chúng chỉ cần gửi tin nhắn đến hệ thống bạn đã di chuyển ra ngoài React. -The examples above assume that the fade-in logic needs to be written in JavaScript. However, this particular fade-in animation is both simpler and much more efficient to implement with a plain [CSS Animation:](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) +Các ví dụ trên giả định rằng logic mờ dần cần được viết bằng JavaScript. Tuy nhiên, hoạt ảnh mờ dần cụ thể này vừa đơn giản hơn vừa hiệu quả hơn nhiều để triển khai với một [Hoạt ảnh CSS:](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) đơn giản: <Sandpack> @@ -1870,27 +1870,27 @@ html, body { min-height: 300px; } </Sandpack> -Sometimes, you don't even need a Hook! +Đôi khi, bạn thậm chí không cần một Hook! <Recap> -- Custom Hooks let you share logic between components. -- Custom Hooks must be named starting with `use` followed by a capital letter. -- Custom Hooks only share stateful logic, not state itself. -- You can pass reactive values from one Hook to another, and they stay up-to-date. -- All Hooks re-run every time your component re-renders. -- The code of your custom Hooks should be pure, like your component's code. -- Wrap event handlers received by custom Hooks into Effect Events. -- Don't create custom Hooks like `useMount`. Keep their purpose specific. -- It's up to you how and where to choose the boundaries of your code. +- Custom Hooks cho phép bạn chia sẻ logic giữa các component. +- Custom Hooks phải được đặt tên bắt đầu bằng `use` theo sau là một chữ cái viết hoa. +- Custom Hooks chỉ chia sẻ logic có trạng thái, không phải bản thân trạng thái. +- Bạn có thể truyền các giá trị phản ứng từ Hook này sang Hook khác và chúng luôn được cập nhật. +- Tất cả các Hook chạy lại mỗi khi component của bạn re-render. +- Code của custom Hooks của bạn phải thuần khiết, giống như code của component của bạn. +- Bọc các trình xử lý sự kiện nhận được bởi custom Hooks vào Effect Events. +- Không tạo custom Hooks như `useMount`. Giữ cho mục đích của chúng cụ thể. +- Tùy thuộc vào bạn cách và nơi chọn ranh giới code của bạn. </Recap> <Challenges> -#### Extract a `useCounter` Hook {/*extract-a-usecounter-hook*/} +#### Trích xuất một Hook `useCounter` {/*extract-a-usecounter-hook*/} -This component uses a state variable and an Effect to display a number that increments every second. Extract this logic into a custom Hook called `useCounter`. Your goal is to make the `Counter` component implementation look exactly like this: +Component này sử dụng một biến trạng thái và một Effect để hiển thị một số tăng lên mỗi giây. Trích xuất logic này vào một custom Hook có tên là `useCounter`. Mục tiêu của bạn là làm cho việc triển khai component `Counter` trông giống hệt như sau: ```js export default function Counter() { @@ -1899,7 +1899,7 @@ export default function Counter() { } ``` -You'll need to write your custom Hook in `useCounter.js` and import it into the `App.js` file. +Bạn sẽ cần viết custom Hook của mình trong `useCounter.js` và import nó vào file `App.js`. <Sandpack> @@ -1919,14 +1919,14 @@ export default function Counter() { ``` ```js src/useCounter.js -// Write your custom Hook in this file! +// Viết custom Hook của bạn trong file này! ``` </Sandpack> <Solution> -Your code should look like this: +Code của bạn sẽ trông như thế này: <Sandpack> @@ -1956,13 +1956,13 @@ export function useCounter() { </Sandpack> -Notice that `App.js` doesn't need to import `useState` or `useEffect` anymore. +Lưu ý rằng `App.js` không cần import `useState` hoặc `useEffect` nữa. </Solution> -#### Make the counter delay configurable {/*make-the-counter-delay-configurable*/} +#### Làm cho độ trễ của bộ đếm có thể cấu hình được {/*make-the-counter-delay-configurable*/} -In this example, there is a `delay` state variable controlled by a slider, but its value is not used. Pass the `delay` value to your custom `useCounter` Hook, and change the `useCounter` Hook to use the passed `delay` instead of hardcoding `1000` ms. +Trong ví dụ này, có một biến trạng thái `delay` được điều khiển bởi một thanh trượt, nhưng giá trị của nó không được sử dụng. Truyền giá trị `delay` cho custom Hook `useCounter` của bạn và thay đổi Hook `useCounter` để sử dụng `delay` đã truyền thay vì mã hóa cứng `1000` ms. <Sandpack> @@ -2012,7 +2012,7 @@ export function useCounter() { <Solution> -Pass the `delay` to your Hook with `useCounter(delay)`. Then, inside the Hook, use `delay` instead of the hardcoded `1000` value. You'll need to add `delay` to your Effect's dependencies. This ensures that a change in `delay` will reset the interval. +Truyền `delay` cho Hook của bạn với `useCounter(delay)`. Sau đó, bên trong Hook, sử dụng `delay` thay vì giá trị `1000` được mã hóa cứng. Bạn sẽ cần thêm `delay` vào các dependency của Effect của bạn. Điều này đảm bảo rằng một thay đổi trong `delay` sẽ reset interval. <Sandpack> @@ -2062,9 +2062,9 @@ export function useCounter(delay) { </Solution> -#### Extract `useInterval` out of `useCounter` {/*extract-useinterval-out-of-usecounter*/} +#### Trích xuất `useInterval` ra khỏi `useCounter` {/*extract-useinterval-out-of-usecounter*/} -Currently, your `useCounter` Hook does two things. It sets up an interval, and it also increments a state variable on every interval tick. Split out the logic that sets up the interval into a separate Hook called `useInterval`. It should take two arguments: the `onTick` callback, and the `delay`. After this change, your `useCounter` implementation should look like this: +Hiện tại, Hook `useCounter` của bạn thực hiện hai việc. Nó thiết lập một interval và nó cũng tăng một biến trạng thái trên mỗi tick của interval. Chia logic thiết lập interval thành một Hook riêng biệt có tên là `useInterval`. Nó sẽ nhận hai đối số: callback `onTick` và `delay`. Sau thay đổi này, việc triển khai `useCounter` của bạn sẽ trông như thế này: ```js export function useCounter(delay) { @@ -2076,7 +2076,7 @@ export function useCounter(delay) { } ``` -Write `useInterval` in the `useInterval.js` file and import it into the `useCounter.js` file. +Viết `useInterval` trong file `useInterval.js` và import nó vào file `useCounter.js`. <Sandpack> @@ -2106,14 +2106,14 @@ export function useCounter(delay) { ``` ```js src/useInterval.js -// Write your Hook here! +// Viết Hook của bạn ở đây! ``` </Sandpack> <Solution> -The logic inside `useInterval` should set up and clear the interval. It doesn't need to do anything else. +Logic bên trong `useInterval` sẽ thiết lập và xóa interval. Nó không cần phải làm bất cứ điều gì khác. <Sandpack> @@ -2137,51 +2137,51 @@ export function useCounter(delay) { }, delay); return count; } -``` - ```js src/useInterval.js active import { useEffect } from 'react'; +import { experimental_useEffectEvent as useEffectEvent } from 'react'; -export function useInterval(onTick, delay) { +export function useInterval(callback, delay) { + const onTick = useEffectEvent(callback); useEffect(() => { const id = setInterval(onTick, delay); return () => clearInterval(id); - }, [onTick, delay]); + }, [delay]); } ``` </Sandpack> -Note that there is a bit of a problem with this solution, which you'll solve in the next challenge. +Lưu ý rằng có một chút vấn đề với giải pháp này, bạn sẽ giải quyết nó trong thử thách tiếp theo. </Solution> -#### Fix a resetting interval {/*fix-a-resetting-interval*/} +#### Sửa lỗi interval bị reset {/*fix-a-resetting-interval*/} -In this example, there are *two* separate intervals. +Trong ví dụ này, có *hai* interval riêng biệt. -The `App` component calls `useCounter`, which calls `useInterval` to update the counter every second. But the `App` component *also* calls `useInterval` to randomly update the page background color every two seconds. +Component `App` gọi `useCounter`, cái mà gọi `useInterval` để cập nhật bộ đếm mỗi giây. Nhưng component `App` *cũng* gọi `useInterval` để cập nhật ngẫu nhiên màu nền trang mỗi hai giây. -For some reason, the callback that updates the page background never runs. Add some logs inside `useInterval`: +Vì một vài lý do, callback mà cập nhật màu nền trang không bao giờ chạy. Thêm một vài log bên trong `useInterval`: ```js {2,5} useEffect(() => { - console.log('✅ Setting up an interval with delay ', delay) + console.log('✅ Thiết lập một interval với độ trễ ', delay) const id = setInterval(onTick, delay); return () => { - console.log('❌ Clearing an interval with delay ', delay) + console.log('❌ Xóa một interval với độ trễ ', delay) clearInterval(id); }; }, [onTick, delay]); ``` -Do the logs match what you expect to happen? If some of your Effects seem to re-synchronize unnecessarily, can you guess which dependency is causing that to happen? Is there some way to [remove that dependency](/learn/removing-effect-dependencies) from your Effect? +Các log có khớp với những gì bạn mong đợi xảy ra không? Nếu một vài Effect của bạn có vẻ như đồng bộ hóa lại một cách không cần thiết, bạn có thể đoán dependency nào gây ra điều đó không? Có cách nào để [xóa dependency đó](/learn/removing-effect-dependencies) khỏi Effect của bạn không? -After you fix the issue, you should expect the page background to update every two seconds. +Sau khi bạn sửa lỗi, bạn nên mong đợi màu nền trang cập nhật mỗi hai giây. <Hint> -It looks like your `useInterval` Hook accepts an event listener as an argument. Can you think of some way to wrap that event listener so that it doesn't need to be a dependency of your Effect? +Có vẻ như Hook `useInterval` của bạn chấp nhận một trình xử lý sự kiện làm đối số. Bạn có thể nghĩ ra cách nào để bọc trình xử lý sự kiện đó để nó không cần phải là một dependency của Effect của bạn không? </Hint> @@ -2250,11 +2250,11 @@ export function useInterval(onTick, delay) { <Solution> -Inside `useInterval`, wrap the tick callback into an Effect Event, as you did [earlier on this page.](/learn/reusing-logic-with-custom-hooks#passing-event-handlers-to-custom-hooks) +Bên trong `useInterval`, bọc callback tick vào một Effect Event, như bạn đã làm [trước đó trên trang này.](/learn/reusing-logic-with-custom-hooks#passing-event-handlers-to-custom-hooks) -This will allow you to omit `onTick` from dependencies of your Effect. The Effect won't re-synchronize on every re-render of the component, so the page background color change interval won't get reset every second before it has a chance to fire. +Điều này sẽ cho phép bạn bỏ qua `onTick` khỏi các dependency của Effect của bạn. Effect sẽ không đồng bộ hóa lại trên mỗi lần re-render của component, vì vậy interval thay đổi màu nền trang sẽ không bị reset mỗi giây trước khi nó có cơ hội kích hoạt. -With this change, both intervals work as expected and don't interfere with each other: +Với thay đổi này, cả hai interval hoạt động như mong đợi và không can thiệp lẫn nhau: <Sandpack> @@ -2321,21 +2321,21 @@ export function useInterval(callback, delay) { </Solution> -#### Implement a staggering movement {/*implement-a-staggering-movement*/} +#### Triển khai một chuyển động so le {/*implement-a-staggering-movement*/} -In this example, the `usePointerPosition()` Hook tracks the current pointer position. Try moving your cursor or your finger over the preview area and see the red dot follow your movement. Its position is saved in the `pos1` variable. +Trong ví dụ này, Hook `usePointerPosition()` theo dõi vị trí con trỏ hiện tại. Hãy thử di chuyển con trỏ hoặc ngón tay của bạn trên khu vực xem trước và xem dấu chấm màu đỏ theo dõi chuyển động của bạn. Vị trí của nó được lưu trong biến `pos1`. -In fact, there are five (!) different red dots being rendered. You don't see them because currently they all appear at the same position. This is what you need to fix. What you want to implement instead is a "staggered" movement: each dot should "follow" the previous dot's path. For example, if you quickly move your cursor, the first dot should follow it immediately, the second dot should follow the first dot with a small delay, the third dot should follow the second dot, and so on. +Trên thực tế, có năm (!) dấu chấm màu đỏ khác nhau đang được hiển thị. Bạn không nhìn thấy chúng vì hiện tại tất cả chúng đều xuất hiện ở cùng một vị trí. Đây là những gì bạn cần sửa. Thay vào đó, những gì bạn muốn triển khai là một chuyển động "so le": mỗi dấu chấm sẽ "theo dõi" đường đi của dấu chấm trước đó. Ví dụ: nếu bạn nhanh chóng di chuyển con trỏ, dấu chấm đầu tiên sẽ theo dõi nó ngay lập tức, dấu chấm thứ hai sẽ theo dõi dấu chấm đầu tiên với một độ trễ nhỏ, dấu chấm thứ ba sẽ theo dõi dấu chấm thứ hai, v.v. -You need to implement the `useDelayedValue` custom Hook. Its current implementation returns the `value` provided to it. Instead, you want to return the value back from `delay` milliseconds ago. You might need some state and an Effect to do this. +Bạn cần triển khai Hook tùy chỉnh `useDelayedValue`. Triển khai hiện tại của nó trả về `value` được cung cấp cho nó. Thay vào đó, bạn muốn trả về giá trị từ `delay` mili giây trước đó. Bạn có thể cần một số state và Effect để làm điều này. -After you implement `useDelayedValue`, you should see the dots move following one another. +Sau khi bạn triển khai `useDelayedValue`, bạn sẽ thấy các dấu chấm di chuyển theo nhau. <Hint> -You'll need to store the `delayedValue` as a state variable inside your custom Hook. When the `value` changes, you'll want to run an Effect. This Effect should update `delayedValue` after the `delay`. You might find it helpful to call `setTimeout`. +Bạn sẽ cần lưu trữ `delayedValue` làm một biến state bên trong Hook tùy chỉnh của bạn. Khi `value` thay đổi, bạn sẽ muốn chạy một Effect. Effect này sẽ cập nhật `delayedValue` sau `delay`. Bạn có thể thấy hữu ích khi gọi `setTimeout`. -Does this Effect need cleanup? Why or why not? +Effect này có cần cleanup không? Tại sao có và tại sao không? </Hint> @@ -2345,7 +2345,7 @@ Does this Effect need cleanup? Why or why not? import { usePointerPosition } from './usePointerPosition.js'; function useDelayedValue(value, delay) { - // TODO: Implement this Hook + // TODO: Triển khai Hook này return value; } @@ -2408,7 +2408,7 @@ body { min-height: 300px; } <Solution> -Here is a working version. You keep the `delayedValue` as a state variable. When `value` updates, your Effect schedules a timeout to update the `delayedValue`. This is why the `delayedValue` always "lags behind" the actual `value`. +Đây là một phiên bản đang hoạt động. Bạn giữ `delayedValue` làm một biến state. Khi `value` cập nhật, Effect của bạn lên lịch một timeout để cập nhật `delayedValue`. Đây là lý do tại sao `delayedValue` luôn "tụt hậu" so với `value` thực tế. <Sandpack> @@ -2485,7 +2485,7 @@ body { min-height: 300px; } </Sandpack> -Note that this Effect *does not* need cleanup. If you called `clearTimeout` in the cleanup function, then each time the `value` changes, it would reset the already scheduled timeout. To keep the movement continuous, you want all the timeouts to fire. +Lưu ý rằng Effect này *không* cần cleanup. Nếu bạn gọi `clearTimeout` trong hàm cleanup, thì mỗi khi `value` thay đổi, nó sẽ reset timeout đã được lên lịch. Để giữ cho chuyển động liên tục, bạn muốn tất cả các timeout được kích hoạt. </Solution> diff --git a/src/content/learn/setup.md b/src/content/learn/setup.md index 2c46ee148..690759dcb 100644 --- a/src/content/learn/setup.md +++ b/src/content/learn/setup.md @@ -1,28 +1,28 @@ --- -title: Setup +title: Thiết lập --- <Intro> -React integrates with tools like editors, TypeScript, browser extensions, and compilers. This section will help you get your environment set up. +React tích hợp với các công cụ như trình soạn thảo, TypeScript, tiện ích mở rộng trình duyệt và trình biên dịch. Phần này sẽ giúp bạn thiết lập môi trường của mình. </Intro> -## Editor Setup {/*editor-setup*/} +## Thiết lập trình soạn thảo {/*editor-setup*/} -See our [recommended editors](/learn/editor-setup) and learn how to set them up to work with React. +Xem [các trình soạn thảo được đề xuất](/learn/editor-setup) của chúng tôi và tìm hiểu cách thiết lập chúng để làm việc với React. -## Using TypeScript {/*using-typescript*/} +## Sử dụng TypeScript {/*using-typescript*/} -TypeScript is a popular way to add type definitions to JavaScript codebases. [Learn how to integrate TypeScript into your React projects](/learn/typescript). +TypeScript là một cách phổ biến để thêm định nghĩa kiểu vào các codebase JavaScript. [Tìm hiểu cách tích hợp TypeScript vào các dự án React của bạn](/learn/typescript). -## React Developer Tools {/*react-developer-tools*/} +## Công cụ dành cho nhà phát triển React {/*react-developer-tools*/} -React Developer Tools is a browser extension that can inspect React components, edit props and state, and identify performance problems. Learn how to install it [here](learn/react-developer-tools). +Công cụ dành cho nhà phát triển React là một tiện ích mở rộng của trình duyệt có thể kiểm tra các thành phần React, chỉnh sửa đạo cụ và trạng thái, đồng thời xác định các vấn đề về hiệu suất. Tìm hiểu cách cài đặt nó [tại đây](learn/react-developer-tools). -## React Compiler {/*react-compiler*/} +## Trình biên dịch React {/*react-compiler*/} -React Compiler is a tool that automatically optimizes your React app. [Learn more](/learn/react-compiler). +Trình biên dịch React là một công cụ tự động tối ưu hóa ứng dụng React của bạn. [Tìm hiểu thêm](/learn/react-compiler). -## Next steps {/*next-steps*/} +## Các bước tiếp theo {/*next-steps*/} -Head to the [Quick Start](/learn) guide for a tour of the most important React concepts you will encounter every day. +Đi tới hướng dẫn [Bắt đầu nhanh](/learn) để tham quan các khái niệm React quan trọng nhất mà bạn sẽ gặp hàng ngày. diff --git a/src/content/learn/sharing-state-between-components.md b/src/content/learn/sharing-state-between-components.md index 52eaf28f8..4885e3440 100644 --- a/src/content/learn/sharing-state-between-components.md +++ b/src/content/learn/sharing-state-between-components.md @@ -1,31 +1,31 @@ --- -title: Sharing State Between Components +title: Chia sẻ State giữa các Component --- <Intro> -Sometimes, you want the state of two components to always change together. To do it, remove state from both of them, move it to their closest common parent, and then pass it down to them via props. This is known as *lifting state up,* and it's one of the most common things you will do writing React code. +Đôi khi, bạn muốn state của hai component luôn thay đổi cùng nhau. Để làm điều đó, hãy loại bỏ state khỏi cả hai component, di chuyển nó lên component cha chung gần nhất của chúng, và sau đó truyền nó xuống cho chúng thông qua props. Điều này được gọi là *nâng state lên,* và nó là một trong những điều phổ biến nhất bạn sẽ làm khi viết code React. </Intro> <YouWillLearn> -- How to share state between components by lifting it up -- What are controlled and uncontrolled components +- Cách chia sẻ state giữa các component bằng cách nâng nó lên +- Component được kiểm soát và không được kiểm soát là gì </YouWillLearn> -## Lifting state up by example {/*lifting-state-up-by-example*/} +## Nâng state lên bằng ví dụ {/*lifting-state-up-by-example*/} -In this example, a parent `Accordion` component renders two separate `Panel`s: +Trong ví dụ này, một component `Accordion` cha hiển thị hai `Panel` riêng biệt: * `Accordion` - `Panel` - `Panel` -Each `Panel` component has a boolean `isActive` state that determines whether its content is visible. +Mỗi component `Panel` có một state `isActive` kiểu boolean để xác định xem nội dung của nó có hiển thị hay không. -Press the Show button for both panels: +Nhấn nút Show cho cả hai panel: <Sandpack> @@ -73,59 +73,59 @@ h3, p { margin: 5px 0px; } </Sandpack> -Notice how pressing one panel's button does not affect the other panel--they are independent. +Lưu ý rằng việc nhấn nút của một panel không ảnh hưởng đến panel còn lại - chúng độc lập với nhau. <DiagramGroup> -<Diagram name="sharing_state_child" height={367} width={477} alt="Diagram showing a tree of three components, one parent labeled Accordion and two children labeled Panel. Both Panel components contain isActive with value false."> +<Diagram name="sharing_state_child" height={367} width={477} alt="Sơ đồ hiển thị một cây gồm ba component, một component cha được gắn nhãn Accordion và hai component con được gắn nhãn Panel. Cả hai component Panel đều chứa isActive với giá trị false."> -Initially, each `Panel`'s `isActive` state is `false`, so they both appear collapsed +Ban đầu, state `isActive` của mỗi `Panel` là `false`, vì vậy cả hai đều xuất hiện ở trạng thái thu gọn </Diagram> -<Diagram name="sharing_state_child_clicked" height={367} width={480} alt="The same diagram as the previous, with the isActive of the first child Panel component highlighted indicating a click with the isActive value set to true. The second Panel component still contains value false." > +<Diagram name="sharing_state_child_clicked" height={367} width={480} alt="Cùng sơ đồ như trên, với isActive của component Panel con đầu tiên được tô sáng cho biết một cú nhấp với giá trị isActive được đặt thành true. Component Panel thứ hai vẫn chứa giá trị false." > -Clicking either `Panel`'s button will only update that `Panel`'s `isActive` state alone +Nhấp vào nút của `Panel` nào sẽ chỉ cập nhật state `isActive` của riêng `Panel` đó </Diagram> </DiagramGroup> -**But now let's say you want to change it so that only one panel is expanded at any given time.** With that design, expanding the second panel should collapse the first one. How would you do that? +**Nhưng bây giờ giả sử bạn muốn thay đổi nó sao cho chỉ một panel được mở rộng tại bất kỳ thời điểm nào.** Với thiết kế đó, việc mở rộng panel thứ hai sẽ thu gọn panel đầu tiên. Bạn sẽ làm điều đó như thế nào? -To coordinate these two panels, you need to "lift their state up" to a parent component in three steps: +Để phối hợp hai panel này, bạn cần "nâng state của chúng lên" một component cha theo ba bước: -1. **Remove** state from the child components. -2. **Pass** hardcoded data from the common parent. -3. **Add** state to the common parent and pass it down together with the event handlers. +1. **Loại bỏ** state khỏi các component con. +2. **Truyền** dữ liệu được mã hóa cứng từ component cha chung. +3. **Thêm** state vào component cha chung và truyền nó xuống cùng với các trình xử lý sự kiện. -This will allow the `Accordion` component to coordinate both `Panel`s and only expand one at a time. +Điều này sẽ cho phép component `Accordion` điều phối cả hai `Panel` và chỉ mở rộng một `Panel` tại một thời điểm. -### Step 1: Remove state from the child components {/*step-1-remove-state-from-the-child-components*/} +### Bước 1: Loại bỏ state khỏi các component con {/*step-1-remove-state-from-the-child-components*/} -You will give control of the `Panel`'s `isActive` to its parent component. This means that the parent component will pass `isActive` to `Panel` as a prop instead. Start by **removing this line** from the `Panel` component: +Bạn sẽ cung cấp quyền kiểm soát `isActive` của `Panel` cho component cha của nó. Điều này có nghĩa là component cha sẽ truyền `isActive` cho `Panel` dưới dạng một prop. Bắt đầu bằng cách **xóa dòng này** khỏi component `Panel`: ```js const [isActive, setIsActive] = useState(false); ``` -And instead, add `isActive` to the `Panel`'s list of props: +Và thay vào đó, hãy thêm `isActive` vào danh sách các prop của `Panel`: ```js function Panel({ title, children, isActive }) { ``` -Now the `Panel`'s parent component can *control* `isActive` by [passing it down as a prop.](/learn/passing-props-to-a-component) Conversely, the `Panel` component now has *no control* over the value of `isActive`--it's now up to the parent component! +Bây giờ component cha của `Panel` có thể *kiểm soát* `isActive` bằng cách [truyền nó xuống dưới dạng một prop.](/learn/passing-props-to-a-component) Ngược lại, component `Panel` bây giờ *không có quyền kiểm soát* đối với giá trị của `isActive` - bây giờ nó phụ thuộc vào component cha! -### Step 2: Pass hardcoded data from the common parent {/*step-2-pass-hardcoded-data-from-the-common-parent*/} +### Bước 2: Truyền dữ liệu được mã hóa cứng từ component cha chung {/*step-2-pass-hardcoded-data-from-the-common-parent*/} -To lift state up, you must locate the closest common parent component of *both* of the child components that you want to coordinate: +Để nâng state lên, bạn phải xác định vị trí component cha chung gần nhất của *cả hai* component con mà bạn muốn phối hợp: -* `Accordion` *(closest common parent)* +* `Accordion` *(component cha chung gần nhất)* - `Panel` - `Panel` -In this example, it's the `Accordion` component. Since it's above both panels and can control their props, it will become the "source of truth" for which panel is currently active. Make the `Accordion` component pass a hardcoded value of `isActive` (for example, `true`) to both panels: +Trong ví dụ này, đó là component `Accordion`. Vì nó nằm trên cả hai panel và có thể kiểm soát các prop của chúng, nó sẽ trở thành "nguồn sự thật" cho panel nào hiện đang hoạt động. Làm cho component `Accordion` truyền một giá trị được mã hóa cứng của `isActive` (ví dụ: `true`) cho cả hai panel: <Sandpack> @@ -172,21 +172,21 @@ h3, p { margin: 5px 0px; } </Sandpack> -Try editing the hardcoded `isActive` values in the `Accordion` component and see the result on the screen. +Hãy thử chỉnh sửa các giá trị `isActive` được mã hóa cứng trong component `Accordion` và xem kết quả trên màn hình. -### Step 3: Add state to the common parent {/*step-3-add-state-to-the-common-parent*/} +### Bước 3: Thêm state vào component cha chung {/*step-3-add-state-to-the-common-parent*/} -Lifting state up often changes the nature of what you're storing as state. +Việc nâng state lên thường thay đổi bản chất của những gì bạn đang lưu trữ dưới dạng state. -In this case, only one panel should be active at a time. This means that the `Accordion` common parent component needs to keep track of *which* panel is the active one. Instead of a `boolean` value, it could use a number as the index of the active `Panel` for the state variable: +Trong trường hợp này, chỉ một panel sẽ hoạt động tại một thời điểm. Điều này có nghĩa là component cha chung `Accordion` cần theo dõi *panel nào* đang hoạt động. Thay vì một giá trị `boolean`, nó có thể sử dụng một số làm chỉ mục của `Panel` đang hoạt động cho biến state: ```js const [activeIndex, setActiveIndex] = useState(0); ``` -When the `activeIndex` is `0`, the first panel is active, and when it's `1`, it's the second one. +Khi `activeIndex` là `0`, panel đầu tiên đang hoạt động và khi nó là `1`, panel thứ hai đang hoạt động. -Clicking the "Show" button in either `Panel` needs to change the active index in `Accordion`. A `Panel` can't set the `activeIndex` state directly because it's defined inside the `Accordion`. The `Accordion` component needs to *explicitly allow* the `Panel` component to change its state by [passing an event handler down as a prop](/learn/responding-to-events#passing-event-handlers-as-props): +Việc nhấp vào nút "Show" trong một trong hai `Panel` cần thay đổi chỉ mục hoạt động trong `Accordion`. Một `Panel` không thể đặt state `activeIndex` trực tiếp vì nó được xác định bên trong `Accordion`. Component `Accordion` cần *cho phép rõ ràng* component `Panel` thay đổi state của nó bằng cách [truyền một trình xử lý sự kiện xuống dưới dạng một prop](/learn/responding-to-events#passing-event-handlers-as-props): ```js <> @@ -205,7 +205,7 @@ Clicking the "Show" button in either `Panel` needs to change the active index in </> ``` -The `<button>` inside the `Panel` will now use the `onShow` prop as its click event handler: +`<button>` bên trong `Panel` bây giờ sẽ sử dụng prop `onShow` làm trình xử lý sự kiện nhấp chuột của nó: <Sandpack> @@ -266,19 +266,19 @@ h3, p { margin: 5px 0px; } </Sandpack> -This completes lifting state up! Moving state into the common parent component allowed you to coordinate the two panels. Using the active index instead of two "is shown" flags ensured that only one panel is active at a given time. And passing down the event handler to the child allowed the child to change the parent's state. +Điều này hoàn thành việc nâng state lên! Việc di chuyển state vào component cha chung cho phép bạn điều phối hai panel. Sử dụng chỉ mục hoạt động thay vì hai cờ "is shown" đảm bảo rằng chỉ một panel hoạt động tại một thời điểm. Và việc truyền trình xử lý sự kiện xuống cho con cho phép con thay đổi state của cha. <DiagramGroup> -<Diagram name="sharing_state_parent" height={385} width={487} alt="Diagram showing a tree of three components, one parent labeled Accordion and two children labeled Panel. Accordion contains an activeIndex value of zero which turns into isActive value of true passed to the first Panel, and isActive value of false passed to the second Panel." > +<Diagram name="sharing_state_parent" height={385} width={487} alt="Sơ đồ hiển thị một cây gồm ba component, một component cha được gắn nhãn Accordion và hai component con được gắn nhãn Panel. Accordion chứa một giá trị activeIndex bằng không, giá trị này biến thành giá trị isActive bằng true được truyền cho Panel đầu tiên và giá trị isActive bằng false được truyền cho Panel thứ hai." > -Initially, `Accordion`'s `activeIndex` is `0`, so the first `Panel` receives `isActive = true` +Ban đầu, `activeIndex` của `Accordion` là `0`, vì vậy `Panel` đầu tiên nhận được `isActive = true` </Diagram> -<Diagram name="sharing_state_parent_clicked" height={385} width={521} alt="The same diagram as the previous, with the activeIndex value of the parent Accordion component highlighted indicating a click with the value changed to one. The flow to both of the children Panel components is also highlighted, and the isActive value passed to each child is set to the opposite: false for the first Panel and true for the second one." > +<Diagram name="sharing_state_parent_clicked" height={385} width={521} alt="Cùng sơ đồ như trên, với giá trị activeIndex của component Accordion cha được tô sáng cho biết một cú nhấp với giá trị được thay đổi thành một. Luồng đến cả hai component Panel con cũng được tô sáng và giá trị isActive được truyền cho mỗi con được đặt thành ngược lại: false cho Panel đầu tiên và true cho Panel thứ hai." > -When `Accordion`'s `activeIndex` state changes to `1`, the second `Panel` receives `isActive = true` instead +Khi state `activeIndex` của `Accordion` thay đổi thành `1`, `Panel` thứ hai sẽ nhận được `isActive = true` thay thế </Diagram> @@ -286,48 +286,48 @@ When `Accordion`'s `activeIndex` state changes to `1`, the second `Panel` receiv <DeepDive> -#### Controlled and uncontrolled components {/*controlled-and-uncontrolled-components*/} +#### Component được kiểm soát và không được kiểm soát {/*controlled-and-uncontrolled-components*/} -It is common to call a component with some local state "uncontrolled". For example, the original `Panel` component with an `isActive` state variable is uncontrolled because its parent cannot influence whether the panel is active or not. +Người ta thường gọi một component có một số state cục bộ là "không được kiểm soát". Ví dụ: component `Panel` ban đầu với một biến state `isActive` là không được kiểm soát vì cha của nó không thể ảnh hưởng đến việc panel có hoạt động hay không. -In contrast, you might say a component is "controlled" when the important information in it is driven by props rather than its own local state. This lets the parent component fully specify its behavior. The final `Panel` component with the `isActive` prop is controlled by the `Accordion` component. +Ngược lại, bạn có thể nói một component là "được kiểm soát" khi thông tin quan trọng trong đó được điều khiển bởi props thay vì state cục bộ của chính nó. Điều này cho phép component cha chỉ định đầy đủ hành vi của nó. Component `Panel` cuối cùng với prop `isActive` được kiểm soát bởi component `Accordion`. -Uncontrolled components are easier to use within their parents because they require less configuration. But they're less flexible when you want to coordinate them together. Controlled components are maximally flexible, but they require the parent components to fully configure them with props. +Các component không được kiểm soát dễ sử dụng hơn trong cha của chúng vì chúng yêu cầu ít cấu hình hơn. Nhưng chúng kém linh hoạt hơn khi bạn muốn phối hợp chúng với nhau. Các component được kiểm soát có tính linh hoạt tối đa, nhưng chúng yêu cầu các component cha định cấu hình chúng đầy đủ bằng props. -In practice, "controlled" and "uncontrolled" aren't strict technical terms--each component usually has some mix of both local state and props. However, this is a useful way to talk about how components are designed and what capabilities they offer. +Trong thực tế, "được kiểm soát" và "không được kiểm soát" không phải là các thuật ngữ kỹ thuật nghiêm ngặt - mỗi component thường có một số kết hợp giữa state cục bộ và props. Tuy nhiên, đây là một cách hữu ích để nói về cách các component được thiết kế và những khả năng mà chúng cung cấp. -When writing a component, consider which information in it should be controlled (via props), and which information should be uncontrolled (via state). But you can always change your mind and refactor later. +Khi viết một component, hãy xem xét thông tin nào trong đó nên được kiểm soát (thông qua props) và thông tin nào nên không được kiểm soát (thông qua state). Nhưng bạn luôn có thể thay đổi ý định và tái cấu trúc sau này. </DeepDive> -## A single source of truth for each state {/*a-single-source-of-truth-for-each-state*/} +## Một nguồn sự thật duy nhất cho mỗi state {/*a-single-source-of-truth-for-each-state*/} -In a React application, many components will have their own state. Some state may "live" close to the leaf components (components at the bottom of the tree) like inputs. Other state may "live" closer to the top of the app. For example, even client-side routing libraries are usually implemented by storing the current route in the React state, and passing it down by props! +Trong một ứng dụng React, nhiều component sẽ có state riêng của chúng. Một số state có thể "sống" gần các component lá (các component ở dưới cùng của cây) như các input. Các state khác có thể "sống" gần đầu ứng dụng hơn. Ví dụ: ngay cả các thư viện định tuyến phía máy khách thường được triển khai bằng cách lưu trữ tuyến đường hiện tại trong state React và truyền nó xuống bằng props! -**For each unique piece of state, you will choose the component that "owns" it.** This principle is also known as having a ["single source of truth".](https://en.wikipedia.org/wiki/Single_source_of_truth) It doesn't mean that all state lives in one place--but that for _each_ piece of state, there is a _specific_ component that holds that piece of information. Instead of duplicating shared state between components, *lift it up* to their common shared parent, and *pass it down* to the children that need it. +**Đối với mỗi phần state duy nhất, bạn sẽ chọn component "sở hữu" nó.** Nguyên tắc này còn được gọi là có một ["nguồn sự thật duy nhất".](https://en.wikipedia.org/wiki/Single_source_of_truth) Nó không có nghĩa là tất cả state đều sống ở một nơi - nhưng đối với _mỗi_ phần state, có một component _cụ thể_ giữ phần thông tin đó. Thay vì sao chép state được chia sẻ giữa các component, hãy *nâng nó lên* component cha được chia sẻ chung của chúng và *truyền nó xuống* cho các con cần nó. -Your app will change as you work on it. It is common that you will move state down or back up while you're still figuring out where each piece of the state "lives". This is all part of the process! +Ứng dụng của bạn sẽ thay đổi khi bạn làm việc trên nó. Thông thường, bạn sẽ di chuyển state xuống hoặc trở lại khi bạn vẫn đang tìm hiểu xem mỗi phần state "sống" ở đâu. Đây là tất cả một phần của quá trình! -To see what this feels like in practice with a few more components, read [Thinking in React.](/learn/thinking-in-react) +Để xem điều này cảm thấy như thế nào trong thực tế với một vài component khác, hãy đọc [Tư duy trong React.](/learn/thinking-in-react) <Recap> -* When you want to coordinate two components, move their state to their common parent. -* Then pass the information down through props from their common parent. -* Finally, pass the event handlers down so that the children can change the parent's state. -* It's useful to consider components as "controlled" (driven by props) or "uncontrolled" (driven by state). +* Khi bạn muốn điều phối hai component, hãy di chuyển state của chúng đến cha chung của chúng. +* Sau đó, truyền thông tin xuống thông qua props từ cha chung của chúng. +* Cuối cùng, truyền các trình xử lý sự kiện xuống để các con có thể thay đổi state của cha. +* Thật hữu ích khi xem xét các component là "được kiểm soát" (được điều khiển bởi props) hoặc "không được kiểm soát" (được điều khiển bởi state). </Recap> <Challenges> -#### Synced inputs {/*synced-inputs*/} +#### Các input được đồng bộ hóa {/*synced-inputs*/} -These two inputs are independent. Make them stay in sync: editing one input should update the other input with the same text, and vice versa. +Hai input này độc lập với nhau. Làm cho chúng luôn đồng bộ: việc chỉnh sửa một input sẽ cập nhật input còn lại bằng cùng một văn bản và ngược lại. <Hint> -You'll need to lift their state up into the parent component. +Bạn sẽ cần nâng state của chúng lên component cha. </Hint> @@ -374,7 +374,7 @@ label { display: block; } <Solution> -Move the `text` state variable into the parent component along with the `handleChange` handler. Then pass them down as props to both of the `Input` components. This will keep them in sync. +Di chuyển biến state `text` vào component cha cùng với trình xử lý `handleChange`. Sau đó, truyền chúng xuống dưới dạng props cho cả hai component `Input`. Điều này sẽ giữ cho chúng đồng bộ. <Sandpack> @@ -427,17 +427,17 @@ label { display: block; } </Solution> -#### Filtering a list {/*filtering-a-list*/} +#### Lọc một danh sách {/*filtering-a-list*/} -In this example, the `SearchBar` has its own `query` state that controls the text input. Its parent `FilterableList` component displays a `List` of items, but it doesn't take the search query into account. +Trong ví dụ này, `SearchBar` có state `query` riêng để kiểm soát input văn bản. Component `FilterableList` cha của nó hiển thị một `List` các mục, nhưng nó không tính đến truy vấn tìm kiếm. -Use the `filterItems(foods, query)` function to filter the list according to the search query. To test your changes, verify that typing "s" into the input filters down the list to "Sushi", "Shish kebab", and "Dim sum". +Sử dụng hàm `filterItems(foods, query)` để lọc danh sách theo truy vấn tìm kiếm. Để kiểm tra các thay đổi của bạn, hãy xác minh rằng việc nhập "s" vào input sẽ lọc danh sách xuống còn "Sushi", "Shish kebab" và "Dim sum". -Note that `filterItems` is already implemented and imported so you don't need to write it yourself! +Lưu ý rằng `filterItems` đã được triển khai và nhập, vì vậy bạn không cần phải tự viết nó! <Hint> -You will want to remove the `query` state and the `handleChange` handler from the `SearchBar`, and move them to the `FilterableList`. Then pass them down to `SearchBar` as `query` and `onChange` props. +Bạn sẽ muốn xóa state `query` và trình xử lý `handleChange` khỏi `SearchBar` và di chuyển chúng đến `FilterableList`. Sau đó, truyền chúng xuống `SearchBar` dưới dạng các prop `query` và `onChange`. </Hint> @@ -528,7 +528,7 @@ export const foods = [{ <Solution> -Lift the `query` state up into the `FilterableList` component. Call `filterItems(foods, query)` to get the filtered list and pass it down to the `List`. Now changing the query input is reflected in the list: +Nâng state `query` lên component `FilterableList`. Gọi `filterItems(foods, query)` để lấy danh sách đã lọc và truyền nó xuống `List`. Bây giờ, việc thay đổi input truy vấn được phản ánh trong danh sách: <Sandpack> diff --git a/src/content/learn/typescript.md b/src/content/learn/typescript.md index 7edf8eb6e..713927b04 100644 --- a/src/content/learn/typescript.md +++ b/src/content/learn/typescript.md @@ -1,73 +1,73 @@ --- -title: Using TypeScript +title: Sử dụng TypeScript re: https://github.com/reactjs/react.dev/issues/5960 --- <Intro> -TypeScript is a popular way to add type definitions to JavaScript codebases. Out of the box, TypeScript [supports JSX](/learn/writing-markup-with-jsx) and you can get full React Web support by adding [`@types/react`](https://www.npmjs.com/package/@types/react) and [`@types/react-dom`](https://www.npmjs.com/package/@types/react-dom) to your project. +TypeScript là một cách phổ biến để thêm định nghĩa kiểu vào các codebase JavaScript. Ngay khi xuất xưởng, TypeScript [hỗ trợ JSX](/learn/writing-markup-with-jsx) và bạn có thể nhận được hỗ trợ đầy đủ cho React Web bằng cách thêm [`@types/react`](https://www.npmjs.com/package/@types/react) và [`@types/react-dom`](https://www.npmjs.com/package/@types/react-dom) vào dự án của bạn. </Intro> <YouWillLearn> -* [TypeScript with React Components](/learn/typescript#typescript-with-react-components) -* [Examples of typing with Hooks](/learn/typescript#example-hooks) -* [Common types from `@types/react`](/learn/typescript/#useful-types) -* [Further learning locations](/learn/typescript/#further-learning) +* [TypeScript với React Components](/learn/typescript#typescript-with-react-components) +* [Ví dụ về cách gõ với Hooks](/learn/typescript#example-hooks) +* [Các kiểu phổ biến từ `@types/react`](/learn/typescript/#useful-types) +* [Các địa điểm học tập thêm](/learn/typescript/#further-learning) </YouWillLearn> -## Installation {/*installation*/} +## Cài đặt {/*installation*/} -All [production-grade React frameworks](/learn/start-a-new-react-project#production-grade-react-frameworks) offer support for using TypeScript. Follow the framework specific guide for installation: +Tất cả [các framework React cấp production](/learn/start-a-new-react-project#production-grade-react-frameworks) đều cung cấp hỗ trợ sử dụng TypeScript. Làm theo hướng dẫn cụ thể của framework để cài đặt: - [Next.js](https://nextjs.org/docs/app/building-your-application/configuring/typescript) - [Remix](https://remix.run/docs/en/1.19.2/guides/typescript) - [Gatsby](https://www.gatsbyjs.com/docs/how-to/custom-configuration/typescript/) - [Expo](https://docs.expo.dev/guides/typescript/) -### Adding TypeScript to an existing React project {/*adding-typescript-to-an-existing-react-project*/} +### Thêm TypeScript vào một dự án React hiện có {/*adding-typescript-to-an-existing-react-project*/} -To install the latest version of React's type definitions: +Để cài đặt phiên bản mới nhất của các định nghĩa kiểu React: <TerminalBlock> npm install @types/react @types/react-dom </TerminalBlock> -The following compiler options need to be set in your `tsconfig.json`: +Các tùy chọn trình biên dịch sau đây cần được đặt trong `tsconfig.json` của bạn: -1. `dom` must be included in [`lib`](https://www.typescriptlang.org/tsconfig/#lib) (Note: If no `lib` option is specified, `dom` is included by default). -1. [`jsx`](https://www.typescriptlang.org/tsconfig/#jsx) must be set to one of the valid options. `preserve` should suffice for most applications. - If you're publishing a library, consult the [`jsx` documentation](https://www.typescriptlang.org/tsconfig/#jsx) on what value to choose. +1. `dom` phải được bao gồm trong [`lib`](https://www.typescriptlang.org/tsconfig/#lib) (Lưu ý: Nếu không có tùy chọn `lib` nào được chỉ định, `dom` sẽ được bao gồm theo mặc định). +2. [`jsx`](https://www.typescriptlang.org/tsconfig/#jsx) phải được đặt thành một trong các tùy chọn hợp lệ. `preserve` sẽ đủ cho hầu hết các ứng dụng. + Nếu bạn đang xuất bản một thư viện, hãy tham khảo [`jsx` documentation](https://www.typescriptlang.org/tsconfig/#jsx) về giá trị cần chọn. -## TypeScript with React Components {/*typescript-with-react-components*/} +## TypeScript với React Components {/*typescript-with-react-components*/} <Note> -Every file containing JSX must use the `.tsx` file extension. This is a TypeScript-specific extension that tells TypeScript that this file contains JSX. +Mọi tệp chứa JSX phải sử dụng phần mở rộng tệp `.tsx`. Đây là một phần mở rộng dành riêng cho TypeScript cho TypeScript biết rằng tệp này chứa JSX. </Note> -Writing TypeScript with React is very similar to writing JavaScript with React. The key difference when working with a component is that you can provide types for your component's props. These types can be used for correctness checking and providing inline documentation in editors. +Viết TypeScript với React rất giống với viết JavaScript với React. Sự khác biệt chính khi làm việc với một component là bạn có thể cung cấp các kiểu cho các props của component đó. Các kiểu này có thể được sử dụng để kiểm tra tính chính xác và cung cấp tài liệu nội tuyến trong trình soạn thảo. -Taking the [`MyButton` component](/learn#components) from the [Quick Start](/learn) guide, we can add a type describing the `title` for the button: +Lấy [`MyButton` component](/learn#components) từ hướng dẫn [Quick Start](/learn), chúng ta có thể thêm một kiểu mô tả `title` cho nút: <Sandpack> ```tsx src/App.tsx active function MyButton({ title }: { title: string }) { return ( - <button>{title}</button> + <button>{title}</button> ); } export default function MyApp() { return ( - <div> - <h1>Welcome to my app</h1> - <MyButton title="I'm a button" /> - </div> + <div> + <h1>Chào mừng đến với ứng dụng của tôi</h1> + <MyButton title="Tôi là một nút" /> + </div> ); } ``` @@ -80,34 +80,34 @@ export default App = AppTSX; <Note> -These sandboxes can handle TypeScript code, but they do not run the type-checker. This means you can amend the TypeScript sandboxes to learn, but you won't get any type errors or warnings. To get type-checking, you can use the [TypeScript Playground](https://www.typescriptlang.org/play) or use a more fully-featured online sandbox. +Các sandbox này có thể xử lý mã TypeScript, nhưng chúng không chạy trình kiểm tra kiểu. Điều này có nghĩa là bạn có thể sửa đổi các sandbox TypeScript để học, nhưng bạn sẽ không nhận được bất kỳ lỗi hoặc cảnh báo kiểu nào. Để nhận được kiểm tra kiểu, bạn có thể sử dụng [TypeScript Playground](https://www.typescriptlang.org/play) hoặc sử dụng một sandbox trực tuyến đầy đủ tính năng hơn. </Note> -This inline syntax is the simplest way to provide types for a component, though once you start to have a few fields to describe it can become unwieldy. Instead, you can use an `interface` or `type` to describe the component's props: +Cú pháp nội tuyến này là cách đơn giản nhất để cung cấp các kiểu cho một component, mặc dù khi bạn bắt đầu có một vài trường để mô tả, nó có thể trở nên khó sử dụng. Thay vào đó, bạn có thể sử dụng `interface` hoặc `type` để mô tả các props của component: <Sandpack> ```tsx src/App.tsx active interface MyButtonProps { - /** The text to display inside the button */ + /** Văn bản để hiển thị bên trong nút */ title: string; - /** Whether the button can be interacted with */ + /** Cho dù nút có thể tương tác được hay không */ disabled: boolean; } function MyButton({ title, disabled }: MyButtonProps) { return ( - <button disabled={disabled}>{title}</button> + <button disabled={disabled}>{title}</button> ); } export default function MyApp() { return ( - <div> - <h1>Welcome to my app</h1> - <MyButton title="I'm a disabled button" disabled={true}/> - </div> + <div> + <h1>Chào mừng đến với ứng dụng của tôi</h1> + <MyButton title="Tôi là một nút bị vô hiệu hóa" disabled={true}/> + </div> ); } ``` @@ -119,32 +119,31 @@ export default App = AppTSX; </Sandpack> -The type describing your component's props can be as simple or as complex as you need, though they should be an object type described with either a `type` or `interface`. You can learn about how TypeScript describes objects in [Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html) but you may also be interested in using [Union Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) to describe a prop that can be one of a few different types and the [Creating Types from Types](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html) guide for more advanced use cases. +Kiểu mô tả các props của component của bạn có thể đơn giản hoặc phức tạp tùy theo nhu cầu của bạn, mặc dù chúng phải là một kiểu đối tượng được mô tả bằng `type` hoặc `interface`. Bạn có thể tìm hiểu về cách TypeScript mô tả các đối tượng trong [Object Types](https://www.typescriptlang.org/docs/handbook/2/objects.html) nhưng bạn cũng có thể quan tâm đến việc sử dụng [Union Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) để mô tả một prop có thể là một trong một vài kiểu khác nhau và hướng dẫn [Creating Types from Types](https://www.typescriptlang.org/docs/handbook/2/types-from-types.html) cho các trường hợp sử dụng nâng cao hơn. +## Ví dụ Hooks {/*example-hooks*/} -## Example Hooks {/*example-hooks*/} +Các định nghĩa kiểu từ `@types/react` bao gồm các kiểu cho các Hook tích hợp, vì vậy bạn có thể sử dụng chúng trong các component của mình mà không cần bất kỳ thiết lập bổ sung nào. Chúng được xây dựng để tính đến mã bạn viết trong component của mình, vì vậy bạn sẽ nhận được [các kiểu được suy luận](https://www.typescriptlang.org/docs/handbook/type-inference.html) rất nhiều và lý tưởng nhất là không cần xử lý các chi tiết nhỏ của việc cung cấp các kiểu. -The type definitions from `@types/react` include types for the built-in Hooks, so you can use them in your components without any additional setup. They are built to take into account the code you write in your component, so you will get [inferred types](https://www.typescriptlang.org/docs/handbook/type-inference.html) a lot of the time and ideally do not need to handle the minutiae of providing the types. - -However, we can look at a few examples of how to provide types for Hooks. +Tuy nhiên, chúng ta có thể xem xét một vài ví dụ về cách cung cấp các kiểu cho Hooks. ### `useState` {/*typing-usestate*/} -The [`useState` Hook](/reference/react/useState) will re-use the value passed in as the initial state to determine what the type of the value should be. For example: +[`useState` Hook](/reference/react/useState) sẽ sử dụng lại giá trị được truyền vào làm trạng thái ban đầu để xác định kiểu của giá trị đó. Ví dụ: ```ts -// Infer the type as "boolean" +// Suy luận kiểu là "boolean" const [enabled, setEnabled] = useState(false); ``` -This will assign the type of `boolean` to `enabled`, and `setEnabled` will be a function accepting either a `boolean` argument, or a function that returns a `boolean`. If you want to explicitly provide a type for the state, you can do so by providing a type argument to the `useState` call: +Điều này sẽ gán kiểu `boolean` cho `enabled` và `setEnabled` sẽ là một hàm chấp nhận một đối số `boolean` hoặc một hàm trả về một `boolean`. Nếu bạn muốn cung cấp rõ ràng một kiểu cho trạng thái, bạn có thể làm như vậy bằng cách cung cấp một đối số kiểu cho lệnh gọi `useState`: -```ts -// Explicitly set the type to "boolean" +```ts +// Đặt rõ ràng kiểu thành "boolean" const [enabled, setEnabled] = useState<boolean>(false); ``` -This isn't very useful in this case, but a common case where you may want to provide a type is when you have a union type. For example, `status` here can be one of a few different strings: +Điều này không hữu ích lắm trong trường hợp này, nhưng một trường hợp phổ biến mà bạn có thể muốn cung cấp một kiểu là khi bạn có một kiểu union. Ví dụ: `status` ở đây có thể là một trong một vài chuỗi khác nhau: ```ts type Status = "idle" | "loading" | "success" | "error"; @@ -152,7 +151,7 @@ type Status = "idle" | "loading" | "success" | "error"; const [status, setStatus] = useState<Status>("idle"); ``` -Or, as recommended in [Principles for structuring state](/learn/choosing-the-state-structure#principles-for-structuring-state), you can group related state as an object and describe the different possibilities via object types: +Hoặc, như được khuyến nghị trong [Principles for structuring state](/learn/choosing-the-state-structure#principles-for-structuring-state), bạn có thể nhóm trạng thái liên quan thành một đối tượng và mô tả các khả năng khác nhau thông qua các kiểu đối tượng: ```ts type RequestState = @@ -166,7 +165,7 @@ const [requestState, setRequestState] = useState<RequestState>({ status: 'idle' ### `useReducer` {/*typing-usereducer*/} -The [`useReducer` Hook](/reference/react/useReducer) is a more complex Hook that takes a reducer function and an initial state. The types for the reducer function are inferred from the initial state. You can optionally provide a type argument to the `useReducer` call to provide a type for the state, but it is often better to set the type on the initial state instead: +[`useReducer` Hook](/reference/react/useReducer) là một Hook phức tạp hơn, lấy một hàm reducer và một trạng thái ban đầu. Các kiểu cho hàm reducer được suy luận từ trạng thái ban đầu. Bạn có thể tùy chọn cung cấp một đối số kiểu cho lệnh gọi `useReducer` để cung cấp một kiểu cho trạng thái, nhưng thường tốt hơn là đặt kiểu trên trạng thái ban đầu thay thế: <Sandpack> @@ -174,7 +173,7 @@ The [`useReducer` Hook](/reference/react/useReducer) is a more complex Hook that import {useReducer} from 'react'; interface State { - count: number + count: number }; type CounterAction = @@ -185,12 +184,12 @@ const initialState: State = { count: 0 }; function stateReducer(state: State, action: CounterAction): State { switch (action.type) { - case "reset": - return initialState; - case "setCount": - return { ...state, count: action.value }; - default: - throw new Error("Unknown action"); + case "reset": + return initialState; + case "setCount": + return { ...state, count: action.value }; + default: + throw new Error("Unknown action"); } } @@ -201,13 +200,13 @@ export default function App() { const reset = () => dispatch({ type: "reset" }); return ( - <div> - <h1>Welcome to my counter</h1> + <div> + <h1>Chào mừng đến với bộ đếm của tôi</h1> - <p>Count: {state.count}</p> - <button onClick={addFive}>Add 5</button> - <button onClick={reset}>Reset</button> - </div> + <p>Đếm: {state.count}</p> + <button onClick={addFive}>Thêm 5</button> + <button onClick={reset}>Đặt lại</button> + </div> ); } @@ -220,15 +219,14 @@ export default App = AppTSX; </Sandpack> +Chúng ta đang sử dụng TypeScript ở một vài nơi quan trọng: -We are using TypeScript in a few key places: - - - `interface State` describes the shape of the reducer's state. - - `type CounterAction` describes the different actions which can be dispatched to the reducer. - - `const initialState: State` provides a type for the initial state, and also the type which is used by `useReducer` by default. - - `stateReducer(state: State, action: CounterAction): State` sets the types for the reducer function's arguments and return value. + - `interface State` mô tả hình dạng của trạng thái của reducer. + - `type CounterAction` mô tả các hành động khác nhau có thể được gửi đến reducer. + - `const initialState: State` cung cấp một kiểu cho trạng thái ban đầu và cũng là kiểu được sử dụng bởi `useReducer` theo mặc định. + - `stateReducer(state: State, action: CounterAction): State` đặt các kiểu cho các đối số và giá trị trả về của hàm reducer. -A more explicit alternative to setting the type on `initialState` is to provide a type argument to `useReducer`: +Một giải pháp thay thế rõ ràng hơn cho việc đặt kiểu trên `initialState` là cung cấp một đối số kiểu cho `useReducer`: ```ts import { stateReducer, State } from './your-reducer-implementation'; @@ -242,9 +240,9 @@ export default function App() { ### `useContext` {/*typing-usecontext*/} -The [`useContext` Hook](/reference/react/useContext) is a technique for passing data down the component tree without having to pass props through components. It is used by creating a provider component and often by creating a Hook to consume the value in a child component. +[`useContext` Hook](/reference/react/useContext) là một kỹ thuật để truyền dữ liệu xuống cây component mà không cần phải truyền các props thông qua các component. Nó được sử dụng bằng cách tạo một component provider và thường bằng cách tạo một Hook để sử dụng giá trị trong một component con. -The type of the value provided by the context is inferred from the value passed to the `createContext` call: +Kiểu của giá trị được cung cấp bởi context được suy luận từ giá trị được truyền cho lệnh gọi `createContext`: <Sandpack> @@ -260,9 +258,9 @@ export default function MyApp() { const [theme, setTheme] = useState<Theme>('light'); return ( - <ThemeContext.Provider value={theme}> - <MyComponent /> - </ThemeContext.Provider> + <ThemeContext.Provider value={theme}> + <MyComponent /> + </ThemeContext.Provider> ) } @@ -270,9 +268,9 @@ function MyComponent() { const theme = useGetTheme(); return ( - <div> - <p>Current theme: {theme}</p> - </div> + <div> + <p>Chủ đề hiện tại: {theme}</p> + </div> ) } ``` @@ -284,25 +282,25 @@ export default App = AppTSX; </Sandpack> -This technique works when you have a default value which makes sense - but there are occasionally cases when you do not, and in those cases `null` can feel reasonable as a default value. However, to allow the type-system to understand your code, you need to explicitly set `ContextShape | null` on the `createContext`. +Kỹ thuật này hoạt động khi bạn có một giá trị mặc định có ý nghĩa - nhưng đôi khi có những trường hợp bạn không có và trong những trường hợp đó, `null` có thể cảm thấy hợp lý như một giá trị mặc định. Tuy nhiên, để cho phép hệ thống kiểu hiểu mã của bạn, bạn cần đặt rõ ràng `ContextShape | null` trên `createContext`. -This causes the issue that you need to eliminate the `| null` in the type for context consumers. Our recommendation is to have the Hook do a runtime check for it's existence and throw an error when not present: +Điều này gây ra vấn đề là bạn cần loại bỏ `| null` trong kiểu cho người tiêu dùng context. Đề xuất của chúng tôi là để Hook thực hiện kiểm tra thời gian chạy về sự tồn tại của nó và đưa ra lỗi khi không có: ```js {5, 16-20} import { createContext, useContext, useState, useMemo } from 'react'; -// This is a simpler example, but you can imagine a more complex object here +// Đây là một ví dụ đơn giản hơn, nhưng bạn có thể tưởng tượng một đối tượng phức tạp hơn ở đây type ComplexObject = { kind: string }; -// The context is created with `| null` in the type, to accurately reflect the default value. +// Context được tạo với `| null` trong kiểu, để phản ánh chính xác giá trị mặc định. const Context = createContext<ComplexObject | null>(null); -// The `| null` will be removed via the check in the Hook. +// `| null` sẽ bị xóa thông qua kiểm tra trong Hook. const useGetComplexObject = () => { const object = useContext(Context); - if (!object) { throw new Error("useGetComplexObject must be used within a Provider") } + if (!object) { throw new Error("useGetComplexObject phải được sử dụng trong một Provider") } return object; } @@ -310,9 +308,9 @@ export default function MyApp() { const object = useMemo(() => ({ kind: "complex" }), []); return ( - <Context.Provider value={object}> - <MyComponent /> - </Context.Provider> + <Context.Provider value={object}> + <MyComponent /> + </Context.Provider> ) } @@ -320,27 +318,25 @@ function MyComponent() { const object = useGetComplexObject(); return ( - <div> - <p>Current object: {object.kind}</p> - </div> + <div> + <p>Đối tượng hiện tại: {object.kind}</p> + </div> ) } ``` ### `useMemo` {/*typing-usememo*/} -The [`useMemo`](/reference/react/useMemo) Hooks will create/re-access a memorized value from a function call, re-running the function only when dependencies passed as the 2nd parameter are changed. The result of calling the Hook is inferred from the return value from the function in the first parameter. You can be more explicit by providing a type argument to the Hook. +[`useMemo`](/reference/react/useMemo) Hooks sẽ tạo/truy cập lại một giá trị được ghi nhớ từ một lệnh gọi hàm, chạy lại hàm chỉ khi các phụ thuộc được truyền làm tham số thứ 2 bị thay đổi. Kết quả của việc gọi Hook được suy luận từ giá trị trả về từ hàm trong tham số đầu tiên. Bạn có thể rõ ràng hơn bằng cách cung cấp một đối số kiểu cho Hook. ```ts -// The type of visibleTodos is inferred from the return value of filterTodos +// Kiểu của visibleTodos được suy luận từ giá trị trả về của filterTodos const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); ``` - ### `useCallback` {/*typing-usecallback*/} -The [`useCallback`](/reference/react/useCallback) provide a stable reference to a function as long as the dependencies passed into the second parameter are the same. Like `useMemo`, the function's type is inferred from the return value of the function in the first parameter, and you can be more explicit by providing a type argument to the Hook. - +[`useCallback`](/reference/react/useCallback) cung cấp một tham chiếu ổn định đến một hàm miễn là các phụ thuộc được truyền vào tham số thứ hai giống nhau. Giống như `useMemo`, kiểu của hàm được suy luận từ giá trị trả về của hàm trong tham số đầu tiên và bạn có thể rõ ràng hơn bằng cách cung cấp một đối số kiểu cho Hook. ```ts const handleClick = useCallback(() => { @@ -348,36 +344,36 @@ const handleClick = useCallback(() => { }, [todos]); ``` -When working in TypeScript strict mode `useCallback` requires adding types for the parameters in your callback. This is because the type of the callback is inferred from the return value of the function, and without parameters the type cannot be fully understood. +Khi làm việc ở chế độ nghiêm ngặt của TypeScript, `useCallback` yêu cầu thêm các kiểu cho các tham số trong callback của bạn. Điều này là do kiểu của callback được suy luận từ giá trị trả về của hàm và nếu không có tham số, kiểu không thể được hiểu đầy đủ. -Depending on your code-style preferences, you could use the `*EventHandler` functions from the React types to provide the type for the event handler at the same time as defining the callback: +Tùy thuộc vào tùy chọn kiểu mã của bạn, bạn có thể sử dụng các hàm `*EventHandler` từ các kiểu React để cung cấp kiểu cho trình xử lý sự kiện đồng thời xác định callback: ```ts import { useState, useCallback } from 'react'; export default function Form() { - const [value, setValue] = useState("Change me"); + const [value, setValue] = useState("Thay đổi tôi"); const handleChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((event) => { - setValue(event.currentTarget.value); + setValue(event.currentTarget.value); }, [setValue]) return ( - <> - <input value={value} onChange={handleChange} /> - <p>Value: {value}</p> - </> + <> + <input value={value} onChange={handleChange} /> + <p>Giá trị: {value}</p> + </> ); } ``` -## Useful Types {/*useful-types*/} +## Các kiểu hữu ích {/*useful-types*/} -There is quite an expansive set of types which come from the `@types/react` package, it is worth a read when you feel comfortable with how React and TypeScript interact. You can find them [in React's folder in DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts). We will cover a few of the more common types here. +Có một tập hợp các kiểu khá mở rộng đến từ gói `@types/react`, rất đáng để đọc khi bạn cảm thấy thoải mái với cách React và TypeScript tương tác. Bạn có thể tìm thấy chúng [trong thư mục React trong DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts). Chúng ta sẽ đề cập đến một vài kiểu phổ biến hơn ở đây. -### DOM Events {/*typing-dom-events*/} +### Sự kiện DOM {/*typing-dom-events*/} -When working with DOM events in React, the type of the event can often be inferred from the event handler. However, when you want to extract a function to be passed to an event handler, you will need to explicitly set the type of the event. +Khi làm việc với các sự kiện DOM trong React, kiểu của sự kiện thường có thể được suy luận từ trình xử lý sự kiện. Tuy nhiên, khi bạn muốn trích xuất một hàm để được truyền cho một trình xử lý sự kiện, bạn sẽ cần đặt rõ ràng kiểu của sự kiện. <Sandpack> @@ -385,17 +381,17 @@ When working with DOM events in React, the type of the event can often be inferr import { useState } from 'react'; export default function Form() { - const [value, setValue] = useState("Change me"); + const [value, setValue] = useState("Thay đổi tôi"); function handleChange(event: React.ChangeEvent<HTMLInputElement>) { - setValue(event.currentTarget.value); + setValue(event.currentTarget.value); } return ( - <> - <input value={value} onChange={handleChange} /> - <p>Value: {value}</p> - </> + <> + <input value={value} onChange={handleChange} /> + <p>Giá trị: {value}</p> + </> ); } ``` @@ -407,15 +403,15 @@ export default App = AppTSX; </Sandpack> -There are many types of events provided in the React types - the full list can be found [here](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b580df54c0819ec9df62b0835a315dd48b8594a9/types/react/index.d.ts#L1247C1-L1373) which is based on the [most popular events from the DOM](https://developer.mozilla.org/en-US/docs/Web/Events). +Có nhiều loại sự kiện được cung cấp trong các kiểu React - danh sách đầy đủ có thể được tìm thấy [ở đây](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b580df54c0819ec9df62b0835a315dd48b8594a9/types/react/index.d.ts#L1247C1-L1373) dựa trên [các sự kiện phổ biến nhất từ DOM](https://developer.mozilla.org/en-US/docs/Web/Events). -When determining the type you are looking for you can first look at the hover information for the event handler you are using, which will show the type of the event. +Khi xác định kiểu bạn đang tìm kiếm, trước tiên bạn có thể xem thông tin di chuột cho trình xử lý sự kiện bạn đang sử dụng, thông tin này sẽ hiển thị kiểu của sự kiện. -If you need to use an event that is not included in this list, you can use the `React.SyntheticEvent` type, which is the base type for all events. +Nếu bạn cần sử dụng một sự kiện không có trong danh sách này, bạn có thể sử dụng kiểu `React.SyntheticEvent`, đây là kiểu cơ sở cho tất cả các sự kiện. ### Children {/*typing-children*/} -There are two common paths to describing the children of a component. The first is to use the `React.ReactNode` type, which is a union of all the possible types that can be passed as children in JSX: +Có hai đường dẫn phổ biến để mô tả các children của một component. Đầu tiên là sử dụng kiểu `React.ReactNode`, đây là một union của tất cả các kiểu có thể được truyền làm children trong JSX: ```ts interface ModalRendererProps { @@ -424,7 +420,7 @@ interface ModalRendererProps { } ``` -This is a very broad definition of children. The second is to use the `React.ReactElement` type, which is only JSX elements and not JavaScript primitives like strings or numbers: +Đây là một định nghĩa rất rộng về children. Thứ hai là sử dụng kiểu `React.ReactElement`, đây chỉ là các phần tử JSX và không phải là các nguyên thủy JavaScript như chuỗi hoặc số: ```ts interface ModalRendererProps { @@ -433,13 +429,13 @@ interface ModalRendererProps { } ``` -Note, that you cannot use TypeScript to describe that the children are a certain type of JSX elements, so you cannot use the type-system to describe a component which only accepts `<li>` children. +Lưu ý rằng bạn không thể sử dụng TypeScript để mô tả rằng các children là một loại phần tử JSX nhất định, vì vậy bạn không thể sử dụng hệ thống kiểu để mô tả một component chỉ chấp nhận các children `<li>`. -You can see an example of both `React.ReactNode` and `React.ReactElement` with the type-checker in [this TypeScript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSB6CxYmAOmXRgDkIATJOdNJMGAZzgwAFpxAR+8YADswAVwGkZMJFEzpOjDKw4AFHGEEBvUnDhphwADZsi0gFw0mDWjqQBuUgF9yaCNMlENzgAXjgACjADfkctFnYkfQhDAEpQgD44AB42YAA3dKMo5P46C2tbJGkvLIpcgt9-QLi3AEEwMFCItJDMrPTTbIQ3dKywdIB5aU4kKyQQKpha8drhhIGzLLWODbNs3b3s8YAxKBQAcwXpAThMaGWDvbH0gFloGbmrgQfBzYpd1YjQZbEYARkB6zMwO2SHSAAlZlYIBCdtCRkZpHIrFYahQYQD8UYYFA5EhcfjyGYqHAXnJAsIUHlOOUbHYhMIIHJzsI0Qk4P9SLUBuRqXEXEwAKKfRZcNA8PiCfxWACecAAUgBlAAacFm80W-CU11U6h4TgwUv11yShjgJjMLMqDnN9Dilq+nh8pD8AXgCHdMrCkWisVoAet0R6fXqhWKhjKllZVVxMcavpd4Zg7U6Qaj+2hmdG4zeRF10uu-Aeq0LBfLMEe-V+T2L7zLVu+FBWLdLeq+lc7DYFf39deFVOotMCACNOCh1dq219a+30uC8YWoZsRyuEdjkevR8uvoVMdjyTWt4WiSSydXD4NqZP4AymeZE072ZzuUeZQKheQgA). +Bạn có thể xem một ví dụ về cả `React.ReactNode` và `React.ReactElement` với trình kiểm tra kiểu trong [TypeScript playground này](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSB6CxYmAOmXRgDkIATJOdNJMGAZzgwAFpxAR+8YADswAVwGkZMJFEzpOjDKw4AFHGEEBvUnDhphwADZsi0gFw0mDWjqQBuUgF9yaCNMlENzgAXjgACjADfkctFnYkfQhDAEpQgD44AB42YAA3dKMo5P46C2tbJGkvLIpcgt9-QLi3AEEwMFCItJDMrPTTbIQ3dKywdIB5aU4kKyQQKpha8drhhIGzLLWODbNs3b3s8YAxKBQAcwXpAThMaGWDvbH0gFloGbmrgQfBzYpd1YjQZbEYARkB6zMwO2SHSAAlZlYIBCdtCRkZpHIrFYahQYQD8UYYFA5EhcfjyGYqHAXnJAsIUHlOOUbHYhMIIHJzsI0Qk4P9SLUBuRqXEXEwAKKfRZcNA8PiCfxWACecAAUgBlAAacFm80W-CU11U6h4TgwUv11yShjgJjMLMqDnN9Dilq+nh8pD8AXgCHdMrCkWisVoAet0R6fXqhWKhjKllZVVxMcavpd4Zg7U6Qaj+2hmdG4zeRF10uu-Aeq0LBfLMEe-V+T2L7zLVu+FBWLdLeq+lc7DYFf39deFVOotMCACNOCh1dq219a+30uC8YWoZsRyuEdjkevR8uvoVMdjyTWt4WiSSydXD4NqZP4AymeZE072ZzuUeZQKheQgA). ### Style Props {/*typing-style-props*/} -When using inline styles in React, you can use `React.CSSProperties` to describe the object passed to the `style` prop. This type is a union of all the possible CSS properties, and is a good way to ensure you are passing valid CSS properties to the `style` prop, and to get auto-complete in your editor. +Khi sử dụng các kiểu nội tuyến trong React, bạn có thể sử dụng `React.CSSProperties` để mô tả đối tượng được truyền cho prop `style`. Kiểu này là một union của tất cả các thuộc tính CSS có thể và là một cách tốt để đảm bảo bạn đang truyền các thuộc tính CSS hợp lệ cho prop `style` và để nhận được tự động hoàn thành trong trình soạn thảo của bạn. ```ts interface MyComponentProps { @@ -447,17 +443,17 @@ interface MyComponentProps { } ``` -## Further learning {/*further-learning*/} +## Học thêm {/*further-learning*/} -This guide has covered the basics of using TypeScript with React, but there is a lot more to learn. -Individual API pages on the docs may contain more in-depth documentation on how to use them with TypeScript. +Hướng dẫn này đã đề cập đến những điều cơ bản về sử dụng TypeScript với React, nhưng vẫn còn rất nhiều điều để học. +Các trang API riêng lẻ trên tài liệu có thể chứa tài liệu chuyên sâu hơn về cách sử dụng chúng với TypeScript. -We recommend the following resources: +Chúng tôi khuyên dùng các tài nguyên sau: - - [The TypeScript handbook](https://www.typescriptlang.org/docs/handbook/) is the official documentation for TypeScript, and covers most key language features. + - [Sổ tay TypeScript](https://www.typescriptlang.org/docs/handbook/) là tài liệu chính thức cho TypeScript và bao gồm hầu hết các tính năng ngôn ngữ chính. - - [The TypeScript release notes](https://devblogs.microsoft.com/typescript/) cover new features in depth. + - [Các ghi chú phát hành TypeScript](https://devblogs.microsoft.com/typescript/) bao gồm các tính năng mới một cách chuyên sâu. - - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) is a community-maintained cheatsheet for using TypeScript with React, covering a lot of useful edge cases and providing more breadth than this document. + - [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) là một cheatsheet do cộng đồng duy trì để sử dụng TypeScript với React, bao gồm rất nhiều trường hợp hữu ích và cung cấp độ rộng hơn tài liệu này. - - [TypeScript Community Discord](https://discord.com/invite/typescript) is a great place to ask questions and get help with TypeScript and React issues. + - [TypeScript Community Discord](https://discord.com/invite/typescript) là một nơi tuyệt vời để đặt câu hỏi và nhận trợ giúp về các vấn đề TypeScript và React. diff --git a/src/content/learn/understanding-your-ui-as-a-tree.md b/src/content/learn/understanding-your-ui-as-a-tree.md index 2abf7affc..7bf6e7e36 100644 --- a/src/content/learn/understanding-your-ui-as-a-tree.md +++ b/src/content/learn/understanding-your-ui-as-a-tree.md @@ -1,41 +1,41 @@ --- -title: Understanding Your UI as a Tree +title: Hiểu Giao Diện Người Dùng của Bạn như một Cây --- <Intro> -Your React app is taking shape with many components being nested within each other. How does React keep track of your app's component structure? +Ứng dụng React của bạn đang hình thành với nhiều component được lồng vào nhau. Làm thế nào React theo dõi cấu trúc component của ứng dụng của bạn? -React, and many other UI libraries, model UI as a tree. Thinking of your app as a tree is useful for understanding the relationship between components. This understanding will help you debug future concepts like performance and state management. +React, và nhiều thư viện UI khác, mô hình hóa UI như một cây. Suy nghĩ về ứng dụng của bạn như một cây rất hữu ích để hiểu mối quan hệ giữa các component. Sự hiểu biết này sẽ giúp bạn gỡ lỗi các khái niệm trong tương lai như hiệu suất và quản lý trạng thái. </Intro> <YouWillLearn> -* How React "sees" component structures -* What a render tree is and what it is useful for -* What a module dependency tree is and what it is useful for +* Cách React "nhìn thấy" cấu trúc component +* Cây render là gì và nó hữu ích cho việc gì +* Cây phụ thuộc module là gì và nó hữu ích cho việc gì </YouWillLearn> -## Your UI as a tree {/*your-ui-as-a-tree*/} +## Giao Diện Người Dùng của Bạn như một Cây {/*your-ui-as-a-tree*/} -Trees are a relationship model between items and UI is often represented using tree structures. For example, browsers use tree structures to model HTML ([DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction)) and CSS ([CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model)). Mobile platforms also use trees to represent their view hierarchy. +Cây là một mô hình quan hệ giữa các mục và UI thường được biểu diễn bằng cấu trúc cây. Ví dụ: trình duyệt sử dụng cấu trúc cây để mô hình hóa HTML ([DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction)) và CSS ([CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model)). Các nền tảng di động cũng sử dụng cây để biểu diễn hệ thống phân cấp chế độ xem của chúng. <Diagram name="preserving_state_dom_tree" height={193} width={864} alt="Diagram with three sections arranged horizontally. In the first section, there are three rectangles stacked vertically, with labels 'Component A', 'Component B', and 'Component C'. Transitioning to the next pane is an arrow with the React logo on top labeled 'React'. The middle section contains a tree of components, with the root labeled 'A' and two children labeled 'B' and 'C'. The next section is again transitioned using an arrow with the React logo on top labeled 'React DOM'. The third and final section is a wireframe of a browser, containing a tree of 8 nodes, which has only a subset highlighted (indicating the subtree from the middle section)."> -React creates a UI tree from your components. In this example, the UI tree is then used to render to the DOM. +React tạo một cây UI từ các component của bạn. Trong ví dụ này, cây UI sau đó được sử dụng để render ra DOM. </Diagram> -Like browsers and mobile platforms, React also uses tree structures to manage and model the relationship between components in a React app. These trees are useful tools to understand how data flows through a React app and how to optimize rendering and app size. +Giống như trình duyệt và nền tảng di động, React cũng sử dụng cấu trúc cây để quản lý và mô hình hóa mối quan hệ giữa các component trong một ứng dụng React. Những cây này là công cụ hữu ích để hiểu cách dữ liệu chảy qua một ứng dụng React và cách tối ưu hóa việc render và kích thước ứng dụng. -## The Render Tree {/*the-render-tree*/} +## Cây Render {/*the-render-tree*/} -A major feature of components is the ability to compose components of other components. As we [nest components](/learn/your-first-component#nesting-and-organizing-components), we have the concept of parent and child components, where each parent component may itself be a child of another component. +Một tính năng chính của component là khả năng kết hợp các component của các component khác. Khi chúng ta [lồng các component](/learn/your-first-component#nesting-and-organizing-components), chúng ta có khái niệm về component cha và component con, trong đó mỗi component cha có thể là một component con của một component khác. -When we render a React app, we can model this relationship in a tree, known as the render tree. +Khi chúng ta render một ứng dụng React, chúng ta có thể mô hình hóa mối quan hệ này trong một cây, được gọi là cây render. -Here is a React app that renders inspirational quotes. +Đây là một ứng dụng React render các câu trích dẫn truyền cảm hứng. <Sandpack> @@ -120,32 +120,31 @@ export default [ <Diagram name="render_tree" height={250} width={500} alt="Tree graph with five nodes. Each node represents a component. The root of the tree is App, with two arrows extending from it to 'InspirationGenerator' and 'FancyText'. The arrows are labelled with the word 'renders'. 'InspirationGenerator' node also has two arrows pointing to nodes 'FancyText' and 'Copyright'."> -React creates a *render tree*, a UI tree, composed of the rendered components. - +React tạo ra một *cây render*, một cây UI, bao gồm các component được render. </Diagram> -From the example app, we can construct the above render tree. +Từ ứng dụng ví dụ, chúng ta có thể xây dựng cây render ở trên. -The tree is composed of nodes, each of which represents a component. `App`, `FancyText`, `Copyright`, to name a few, are all nodes in our tree. +Cây bao gồm các node, mỗi node đại diện cho một component. `App`, `FancyText`, `Copyright`, là một vài node trong cây của chúng ta. -The root node in a React render tree is the [root component](/learn/importing-and-exporting-components#the-root-component-file) of the app. In this case, the root component is `App` and it is the first component React renders. Each arrow in the tree points from a parent component to a child component. +Node gốc trong một cây render React là [component gốc](/learn/importing-and-exporting-components#the-root-component-file) của ứng dụng. Trong trường hợp này, component gốc là `App` và nó là component đầu tiên React render. Mỗi mũi tên trong cây trỏ từ một component cha đến một component con. <DeepDive> -#### Where are the HTML tags in the render tree? {/*where-are-the-html-elements-in-the-render-tree*/} +#### Các thẻ HTML ở đâu trong cây render? {/*where-are-the-html-elements-in-the-render-tree*/} -You'll notice in the above render tree, there is no mention of the HTML tags that each component renders. This is because the render tree is only composed of React [components](learn/your-first-component#components-ui-building-blocks). +Bạn sẽ nhận thấy trong cây render ở trên, không có đề cập đến các thẻ HTML mà mỗi component render. Điều này là do cây render chỉ bao gồm các [component](learn/your-first-component#components-ui-building-blocks) React. -React, as a UI framework, is platform agnostic. On react.dev, we showcase examples that render to the web, which uses HTML markup as its UI primitives. But a React app could just as likely render to a mobile or desktop platform, which may use different UI primitives like [UIView](https://developer.apple.com/documentation/uikit/uiview) or [FrameworkElement](https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement?view=windowsdesktop-7.0). +React, với tư cách là một framework UI, không phụ thuộc vào nền tảng. Trên react.dev, chúng tôi giới thiệu các ví dụ render lên web, sử dụng đánh dấu HTML làm các primitive UI của nó. Nhưng một ứng dụng React cũng có thể render lên một nền tảng di động hoặc máy tính để bàn, có thể sử dụng các primitive UI khác nhau như [UIView](https://developer.apple.com/documentation/uikit/uiview) hoặc [FrameworkElement](https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement?view=windowsdesktop-7.0). -These platform UI primitives are not a part of React. React render trees can provide insight to our React app regardless of what platform your app renders to. +Các primitive UI nền tảng này không phải là một phần của React. Cây render React có thể cung cấp cái nhìn sâu sắc về ứng dụng React của chúng ta bất kể ứng dụng của bạn render lên nền tảng nào. </DeepDive> -A render tree represents a single render pass of a React application. With [conditional rendering](/learn/conditional-rendering), a parent component may render different children depending on the data passed. +Một cây render đại diện cho một lần render duy nhất của một ứng dụng React. Với [render có điều kiện](/learn/conditional-rendering), một component cha có thể render các component con khác nhau tùy thuộc vào dữ liệu được truyền vào. -We can update the app to conditionally render either an inspirational quote or color. +Chúng ta có thể cập nhật ứng dụng để render có điều kiện một câu trích dẫn truyền cảm hứng hoặc màu sắc. <Sandpack> @@ -247,53 +246,53 @@ export default [ <Diagram name="conditional_render_tree" height={250} width={561} alt="Tree graph with six nodes. The top node of the tree is labelled 'App' with two arrows extending to nodes labelled 'InspirationGenerator' and 'FancyText'. The arrows are solid lines and are labelled with the word 'renders'. 'InspirationGenerator' node also has three arrows. The arrows to nodes 'FancyText' and 'Color' are dashed and labelled with 'renders?'. The last arrow points to the node labelled 'Copyright' and is solid and labelled with 'renders'."> -With conditional rendering, across different renders, the render tree may render different components. +Với render có điều kiện, trên các lần render khác nhau, cây render có thể render các component khác nhau. </Diagram> -In this example, depending on what `inspiration.type` is, we may render `<FancyText>` or `<Color>`. The render tree may be different for each render pass. +Trong ví dụ này, tùy thuộc vào `inspiration.type` là gì, chúng ta có thể render `<FancyText>` hoặc `<Color>`. Cây render có thể khác nhau cho mỗi lần render. -Although render trees may differ across render passes, these trees are generally helpful for identifying what the *top-level* and *leaf components* are in a React app. Top-level components are the components nearest to the root component and affect the rendering performance of all the components beneath them and often contain the most complexity. Leaf components are near the bottom of the tree and have no child components and are often frequently re-rendered. +Mặc dù cây render có thể khác nhau giữa các lần render, nhưng những cây này thường hữu ích để xác định *component cấp cao nhất* và *component lá* trong một ứng dụng React. Các component cấp cao nhất là các component gần component gốc nhất và ảnh hưởng đến hiệu suất render của tất cả các component bên dưới chúng và thường chứa độ phức tạp cao nhất. Các component lá nằm gần cuối cây và không có component con và thường được render lại thường xuyên. -Identifying these categories of components are useful for understanding data flow and performance of your app. +Xác định các loại component này rất hữu ích để hiểu luồng dữ liệu và hiệu suất của ứng dụng của bạn. -## The Module Dependency Tree {/*the-module-dependency-tree*/} +## Cây Phụ Thuộc Module {/*the-module-dependency-tree*/} -Another relationship in a React app that can be modeled with a tree are an app's module dependencies. As we [break up our components](/learn/importing-and-exporting-components#exporting-and-importing-a-component) and logic into separate files, we create [JS modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) where we may export components, functions, or constants. +Một mối quan hệ khác trong một ứng dụng React có thể được mô hình hóa bằng một cây là các phụ thuộc module của ứng dụng. Khi chúng ta [chia nhỏ các component](/learn/importing-and-exporting-components#exporting-and-importing-a-component) và logic của chúng ta thành các tệp riêng biệt, chúng ta tạo ra [module JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) nơi chúng ta có thể xuất các component, hàm hoặc hằng số. -Each node in a module dependency tree is a module and each branch represents an `import` statement in that module. +Mỗi node trong một cây phụ thuộc module là một module và mỗi nhánh đại diện cho một câu lệnh `import` trong module đó. -If we take the previous Inspirations app, we can build a module dependency tree, or dependency tree for short. +Nếu chúng ta lấy ứng dụng Inspirations trước đó, chúng ta có thể xây dựng một cây phụ thuộc module, hoặc cây phụ thuộc cho ngắn gọn. <Diagram name="module_dependency_tree" height={250} width={658} alt="A tree graph with seven nodes. Each node is labelled with a module name. The top level node of the tree is labelled 'App.js'. There are three arrows pointing to the modules 'InspirationGenerator.js', 'FancyText.js' and 'Copyright.js' and the arrows are labelled with 'imports'. From the 'InspirationGenerator.js' node, there are three arrows that extend to three modules: 'FancyText.js', 'Color.js', and 'inspirations.js'. The arrows are labelled with 'imports'."> -The module dependency tree for the Inspirations app. +Cây phụ thuộc module cho ứng dụng Inspirations. </Diagram> -The root node of the tree is the root module, also known as the entrypoint file. It often is the module that contains the root component. +Node gốc của cây là module gốc, còn được gọi là tệp điểm vào. Nó thường là module chứa component gốc. -Comparing to the render tree of the same app, there are similar structures but some notable differences: +So sánh với cây render của cùng một ứng dụng, có các cấu trúc tương tự nhưng một số khác biệt đáng chú ý: -* The nodes that make-up the tree represent modules, not components. -* Non-component modules, like `inspirations.js`, are also represented in this tree. The render tree only encapsulates components. -* `Copyright.js` appears under `App.js` but in the render tree, `Copyright`, the component, appears as a child of `InspirationGenerator`. This is because `InspirationGenerator` accepts JSX as [children props](/learn/passing-props-to-a-component#passing-jsx-as-children), so it renders `Copyright` as a child component but does not import the module. +* Các node tạo nên cây đại diện cho các module, không phải component. +* Các module không phải component, như `inspirations.js`, cũng được biểu diễn trong cây này. Cây render chỉ bao gồm các component. +* `Copyright.js` xuất hiện bên dưới `App.js` nhưng trong cây render, `Copyright`, component, xuất hiện như một component con của `InspirationGenerator`. Điều này là do `InspirationGenerator` chấp nhận JSX làm [children props](/learn/passing-props-to-a-component#passing-jsx-as-children), vì vậy nó render `Copyright` như một component con nhưng không import module. -Dependency trees are useful to determine what modules are necessary to run your React app. When building a React app for production, there is typically a build step that will bundle all the necessary JavaScript to ship to the client. The tool responsible for this is called a [bundler](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview#the_modern_tooling_ecosystem), and bundlers will use the dependency tree to determine what modules should be included. +Cây phụ thuộc rất hữu ích để xác định những module nào là cần thiết để chạy ứng dụng React của bạn. Khi xây dựng một ứng dụng React cho production, thường có một bước build sẽ gói tất cả JavaScript cần thiết để gửi đến client. Công cụ chịu trách nhiệm cho việc này được gọi là [bundler](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview#the_modern_tooling_ecosystem), và bundler sẽ sử dụng cây phụ thuộc để xác định những module nào nên được bao gồm. -As your app grows, often the bundle size does too. Large bundle sizes are expensive for a client to download and run. Large bundle sizes can delay the time for your UI to get drawn. Getting a sense of your app's dependency tree may help with debugging these issues. +Khi ứng dụng của bạn phát triển, kích thước bundle thường cũng tăng lên. Kích thước bundle lớn tốn kém cho client để tải xuống và chạy. Kích thước bundle lớn có thể trì hoãn thời gian để UI của bạn được vẽ. Nhận biết về cây phụ thuộc của ứng dụng của bạn có thể giúp gỡ lỗi các vấn đề này. [comment]: <> (perhaps we should also deep dive on conditional imports) <Recap> -* Trees are a common way to represent the relationship between entities. They are often used to model UI. -* Render trees represent the nested relationship between React components across a single render. -* With conditional rendering, the render tree may change across different renders. With different prop values, components may render different children components. -* Render trees help identify what the top-level and leaf components are. Top-level components affect the rendering performance of all components beneath them and leaf components are often re-rendered frequently. Identifying them is useful for understanding and debugging rendering performance. -* Dependency trees represent the module dependencies in a React app. -* Dependency trees are used by build tools to bundle the necessary code to ship an app. -* Dependency trees are useful for debugging large bundle sizes that slow time to paint and expose opportunities for optimizing what code is bundled. +* Cây là một cách phổ biến để biểu diễn mối quan hệ giữa các thực thể. Chúng thường được sử dụng để mô hình hóa UI. +* Cây render biểu diễn mối quan hệ lồng nhau giữa các component React trên một lần render duy nhất. +* Với render có điều kiện, cây render có thể thay đổi trên các lần render khác nhau. Với các giá trị prop khác nhau, các component có thể render các component con khác nhau. +* Cây render giúp xác định component cấp cao nhất và component lá là gì. Các component cấp cao nhất ảnh hưởng đến hiệu suất render của tất cả các component bên dưới chúng và các component lá thường được render lại thường xuyên. Xác định chúng rất hữu ích để hiểu và gỡ lỗi hiệu suất render. +* Cây phụ thuộc biểu diễn các phụ thuộc module trong một ứng dụng React. +* Cây phụ thuộc được sử dụng bởi các công cụ build để gói mã cần thiết để gửi một ứng dụng. +* Cây phụ thuộc rất hữu ích để gỡ lỗi kích thước bundle lớn làm chậm thời gian vẽ và phơi bày các cơ hội để tối ưu hóa những gì mã được gói. </Recap> 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..afc596b5d 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -1,45 +1,45 @@ --- -title: 'You Might Not Need an Effect' +title: 'Có Thể Bạn Không Cần Effect' --- <Intro> -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 mô hình 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 số hệ thống bên ngoài như một widget không phải React, mạng hoặc DOM của 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 một component khi một số prop hoặc state thay đổi), bạn không nên cần đến Effect. Loại bỏ các Effect không cần thiết sẽ giúp code của bạn dễ theo dõi hơn, chạy nhanh hơn và ít bị lỗi hơn. </Intro> <YouWillLearn> -* 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à làm thế nào để loại bỏ các Effect không cần thiết khỏi component của bạn +* Cách lưu trữ các phép tính tốn kém mà không cần Effect +* Cách đặt lại và điều chỉnh state của component mà không cần Effect +* Cách chia sẻ logic giữa các trình xử lý sự kiện +* Logic nào nên được chuyển sang trình xử lý sự kiện +* Cách thông báo cho các component cha về các thay đổi </YouWillLearn> -## How to remove unnecessary Effects {/*how-to-remove-unnecessary-effects*/} +## Làm thế nào để loại bỏ các 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 để chuyển đổi dữ liệu để hiển thị.** 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ẽ gọi các hàm component của bạn để tính toán những gì sẽ 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 sẽ khởi động lại toàn bộ quá trình từ đầu! Để tránh các lần render không cần thiết, hãy chuyển đổi tất cả dữ liệu ở cấp cao nhất của component của bạn. Code đó sẽ tự động chạy lại bất cứ khi nào prop hoặc state của bạn thay đổi. +* **Bạn không cần Effect để xử lý các sự kiện của người dùng.** Ví dụ: giả sử bạn muốn gửi một yêu cầu POST `/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 trình xử lý sự kiện click của nút Mua, bạn biết chính xác những gì đã xảy ra. Vào thời điểm Effect chạy, bạn không biết *người dùng* đã làm gì (ví dụ: nút nào đã được click). Đây là lý do tại sao bạn thường xử lý các sự kiện của người dùng trong các trình xử lý sự kiện 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ầ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 một widget jQuery được đồng bộ hóa với state của React. Bạn cũng có thể tìm nạp dữ liệu bằng 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ế tìm nạp 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 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 đắn, 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 prop 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 với nhau. 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() { const [firstName, setFirstName] = useState('Taylor'); const [lastName, setLastName] = useState('Swift'); - // 🔴 Avoid: redundant state and unnecessary Effect + // 🔴 Tránh: state dư thừa và Effect không cần thiết const [fullName, setFullName] = useState(''); useEffect(() => { setFullName(firstName + ' ' + lastName); @@ -48,29 +48,29 @@ 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 hoàn chỉnh với một giá trị cũ cho `fullName`, sau đó ngay lập tức render lại với giá trị đã cập nhật. Loại bỏ biến state và Effect: ```js {4-5} function Form() { const [firstName, setFirstName] = useState('Taylor'); const [lastName, setLastName] = useState('Swift'); - // ✅ Good: calculated during rendering + // ✅ Tốt: được tính toán trong quá trình render const fullName = firstName + ' ' + lastName; // ... } ``` -**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ừ các prop hoặc state hiện có, [đừng đưa 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 "xếp tầng" bổ sung), đơn giản hơn (bạn loại bỏ một số code) và ít bị lỗi hơn (bạn tránh được các lỗi do các biến state khác nhau bị lệch pha với nhau). Nếu cách tiếp cận này có vẻ mới đối 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*/} +### Lưu trữ các phép tính tốn kém {/*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 được bằng prop và lọc chúng theo prop `filter`. 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 }) { const [newTodo, setNewTodo] = useState(''); - // 🔴 Avoid: redundant state and unnecessary Effect + // 🔴 Tránh: state dư thừa và Effect không cần thiết const [visibleTodos, setVisibleTodos] = useState([]); useEffect(() => { setVisibleTodos(getFilteredTodos(todos, filter)); @@ -80,20 +80,20 @@ 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ả. Đầu tiên, loại bỏ state và Effect: ```js {3-4} function TodoList({ todos, filter }) { const [newTodo, setNewTodo] = useState(''); - // ✅ This is fine if getFilteredTodos() is not slow. + // ✅ Điều này ổn nếu getFilteredTodos() không chậm. const visibleTodos = getFilteredTodos(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ường, đoạn code này vẫn ổn! Nhưng có thể `getFilteredTodos()` chạy 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ể lưu vào bộ nhớ cache (hoặc ["ghi nhớ"](https://en.wikipedia.org/wiki/Memoization)) một phép tính tốn kém bằng cách bọc nó trong một Hook [`useMemo`](/reference/react/useMemo): ```js {5-8} import { useMemo, useState } from 'react'; @@ -101,35 +101,35 @@ import { useMemo, useState } from 'react'; function TodoList({ todos, filter }) { const [newTodo, setNewTodo] = useState(''); const visibleTodos = useMemo(() => { - // ✅ Does not re-run unless todos or filter change + // ✅ Không chạy lại trừ khi todos hoặc filter thay đổi return getFilteredTodos(todos, filter); }, [todos, filter]); // ... } ``` -Or, written as a single line: +Hoặc, viết dưới dạng một dòng duy nhất: ```js {5-6} import { useMemo, useState } from 'react'; function TodoList({ todos, filter }) { const [newTodo, setNewTodo] = useState(''); - // ✅ Does not re-run getFilteredTodos() unless todos or filter change + // ✅ Không chạy lại getFilteredTodos() trừ khi todos hoặc filter thay đổi const visibleTodos = useMemo(() => getFilteredTodos(todos, filter), [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 cho React biết rằng bạn không muốn hàm bên trong chạy lại trừ khi `todos` hoặc `filter` đã thay đổi.** React sẽ ghi nhớ giá trị trả về của `getFilteredTodos()` trong quá trình render ban đầu. Trong quá trình render tiếp theo, nó sẽ kiểm tra xem `todos` hoặc `filter` có khác nhau hay 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 nhau, React sẽ gọi lại hàm bên trong (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) +Hà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 đối với [các phép tính thuần túy.](/learn/keeping-components-pure) <DeepDive> -#### How to tell if a calculation is expensive? {/*how-to-tell-if-a-calculation-is-expensive*/} +#### Làm thế nào để biết một phép tính có tốn kém hay 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, có lẽ nó không tốn kém. Nếu bạn muốn tự tin hơn, bạn có thể thêm một bản ghi console để đo thời gian dành cho một đoạn code: ```js {1,3} console.time('filter array'); @@ -137,33 +137,33 @@ 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 bạn đang đo (ví dụ: nhập vào đầu vào). Sau đó, bạn sẽ thấy các bản ghi như `filter array: 0.15ms` trong bảng điều khiển của mình. Nếu tổng thời gian được ghi lại cộng lại thành một lượng đáng kể (ví dụ: `1ms` trở lên), thì có thể có ý nghĩa khi ghi nhớ 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 được ghi lại 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 quá trình render *đầu tiên* nhanh hơn. Nó chỉ giúp bạn bỏ qua các công việc không cần thiết khi 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 bạn nên kiểm tra hiệu suất với một sự chậm lại nhân tạo. Ví dụ: Chrome cung cấp tùy chọn [Điều chỉnh CPU](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 việc đo hiệu suất trong quá trình phát triển sẽ không cung cấp cho bạn kết quả chính xác nhất. (Ví dụ: khi [Chế độ nghiêm ngặt](/reference/react/StrictMode) được bật, bạn sẽ thấy mỗi thành phần render hai lần thay vì một lần.) Để có được thời gian chính xác nhất, hãy xây dựng ứng dụng của bạn để sản xuất và kiểm tra nó trên một thiết bị như người dùng của bạn có. </DeepDive> -### Resetting all state when a prop changes {/*resetting-all-state-when-a-prop-changes*/} +### Đặt lại tất cả trạng thái 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: +Thành phần `ProfilePage` này nhận một prop `userId`. Trang này chứa một đầu vào nhận xét và bạn sử dụng một biến state `comment` để giữ giá trị của nó. Một ngày nọ, bạn nhận thấy một vấn đề: khi bạn điều hướng từ hồ sơ này sang hồ sơ khác, trạng thái `comment` không được đặt lại. Do đó, rất dễ vô tình đăng nhận xét trên hồ sơ 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 }) { const [comment, setComment] = useState(''); - // 🔴 Avoid: Resetting state on prop change in an Effect + // 🔴 Tránh: Đặt lại trạng thái khi thay đổi prop trong một Effect useEffect(() => { setComment(''); }, [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 thành phần con của nó sẽ render trước với giá trị cũ, sau đó render lại. Nó cũng phức tạp vì bạn cần phải làm điều này trong *mọi* thành phần có một số state bên trong `ProfilePage`. Ví dụ: nếu giao diện người dùng nhận xét được lồng nhau, bạn cũng muốn xóa state nhận xét 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ể cho React biết rằng hồ sơ của mỗi người dùng về mặt khái niệm là một hồ sơ _khác nhau_ bằng cách cung cấp cho nó một khóa rõ ràng. Chia component của bạn thành hai và chuyển một thuộc tính `key` từ component bên ngoài sang component bên trong: ```js {5,11-12} export default function ProfilePage({ userId }) { @@ -186,28 +186,28 @@ export default function ProfilePage({ userId }) { } function Profile({ userId }) { - // ✅ This and any other state below will reset on key change automatically + // ✅ Trạng thái này và bất kỳ trạng thái nào khác bên dưới sẽ tự động đặt lại khi thay đổi khóa const [comment, setComment] = useState(''); // ... } ``` -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 giữ nguyên state khi cùng một component được render ở cùng một vị trí. **Bằng cách chuyển `userId` làm `key` cho component `Profile`, bạn đang yêu cầu React coi hai component `Profile` có `userId` khác nhau là hai component khác nhau không được chia sẻ bất kỳ state nào.** Bất cứ khi nào khóa (mà bạn đã đặt thành `userId`) thay đổi, React sẽ tạo lại DOM và [đặt lại state](/learn/preserving-and-resetting-state#option-2-resetting-state-with-a-key) của component `Profile` và tất cả các component con của nó. Bây giờ trường `comment` sẽ tự động xóa khi điều hướng giữa các hồ sơ. -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 xuất và hiển thị cho các tệp khác trong dự án. Các component render `ProfilePage` không cần phải chuyển khóa cho nó: chúng chuyển `userId` làm một prop thông thường. Việc `ProfilePage` chuyển nó làm `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ố trạng thái 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 đặt lại hoặc điều chỉnh một phần của state khi một 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 danh sách `items` làm một prop và duy trì mục đã chọn trong biến state `selection`. Bạn muốn đặt lại `selection` thành `null` bất cứ khi nào ```js {5-8} function List({ items }) { const [isReverse, setIsReverse] = useState(false); const [selection, setSelection] = useState(null); - // 🔴 Avoid: Adjusting state on prop change in an Effect + // 🔴 Tránh: Điều chỉnh trạng thái khi thay đổi prop trong một Effect useEffect(() => { setSelection(null); }, [items]); @@ -215,16 +215,16 @@ 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 thành phần con của nó sẽ render với giá trị `selection` cũ trước. Sau đó, React sẽ cập nhật DOM và chạy các Effect. Cuối cùng, lệnh gọi `setSelection(null)` sẽ gây ra một lần render lại `List` và các thành phần con của nó, khởi động lại toàn bộ quá trình này. -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 đó, hãy điều chỉnh trạng thái trực tiếp trong quá trình render: ```js {5-11} function List({ items }) { const [isReverse, setIsReverse] = useState(false); const [selection, setSelection] = useState(null); - // Better: Adjust the state while rendering + // Tốt hơn: Điều chỉnh trạng thái trong khi render const [prevItems, setPrevItems] = useState(items); if (items !== prevItems) { setPrevItems(items); @@ -234,34 +234,34 @@ function List({ items }) { } ``` -[Storing information from previous renders](/reference/react/useState#storing-information-from-previous-renders) like this can be hard to understand, but it’s better than updating the same state in an Effect. In the above example, `setSelection` is called directly during a render. React will re-render the `List` *immediately* after it exits with a `return` statement. React has not rendered the `List` children or updated the DOM yet, so this lets the `List` children skip rendering the stale `selection` value. +[Lưu trữ thông tin từ các lần render trước](/reference/react/useState#storing-information-from-previous-renders) như thế này có thể khó hiểu, nhưng nó tốt hơn là cập nhật cùng một trạng thái trong một Effect. Trong ví dụ trên, `setSelection` được gọi trực tiếp trong quá trình render. React sẽ render lại `List` *ngay lập tức* sau khi nó thoát bằng một câu lệnh `return`. React chưa render các thành phần con `List` hoặc cập nhật DOM, vì vậy điều này cho phép các thành phần con `List` bỏ qua việc render giá trị `selection` cũ. -When you update a component during rendering, React throws away the returned JSX and immediately retries rendering. To avoid very slow cascading retries, React only lets you update the *same* component's state during a render. If you update another component's state during a render, you'll see an error. A condition like `items !== prevItems` is necessary to avoid loops. You may adjust state like this, but any other side effects (like changing the DOM or setting timeouts) should stay in event handlers or Effects to [keep components pure.](/learn/keeping-components-pure) +Khi bạn cập nhật một thành phần trong quá trình render, React sẽ loại bỏ JSX được trả về và thử lại render ngay lập tức. Để tránh các lần thử lại xếp tầng rất chậm, React chỉ cho phép bạn cập nhật trạng thái của *cùng* một thành phần trong quá trình render. Nếu bạn cập nhật trạng thái của một thành phần khác trong quá trình render, bạn sẽ thấy lỗi. Một điều kiện như `items !== prevItems` là cần thiết để tránh các vòng lặp. Bạn có thể điều chỉnh trạng thái như thế này, nhưng bất kỳ tác dụng phụ nào khác (như thay đổi DOM hoặc đặt thời gian chờ) nên ở trong các trình xử lý sự kiện hoặc Effect để [giữ cho các thành phần thuần túy.](/learn/keeping-components-pure) -**Although this pattern is more efficient than an Effect, most components shouldn't need it either.** No matter how you do it, adjusting state based on props or other state makes your data flow more difficult to understand and debug. Always check whether you can [reset all state with a key](#resetting-all-state-when-a-prop-changes) or [calculate everything during rendering](#updating-state-based-on-props-or-state) instead. For example, instead of storing (and resetting) the selected *item*, you can store the selected *item ID:* +**Mặc dù mẫu này hiệu quả hơn một Effect, nhưng hầu hết các thành phần cũng không cần nó.** Bất kể bạn làm điều đó như thế nào, việc điều chỉnh trạng thái dựa trên các prop hoặc trạng thái khác sẽ làm cho luồng dữ liệu của bạn khó hiểu và gỡ lỗi hơn. Luôn kiểm tra xem bạn có thể [đặt lại tất cả trạng thái bằng một khóa](#resetting-all-state-when-a-prop-changes) hoặc [tính toán mọi thứ trong quá trình render](#updating-state-based-on-props-or-state) hay không. Ví dụ: thay vì lưu trữ (và đặt lại) *mục* đã chọn, bạn có thể lưu trữ *ID mục* đã chọn: ```js {3-5} function List({ items }) { const [isReverse, setIsReverse] = useState(false); const [selectedId, setSelectedId] = useState(null); - // ✅ Best: Calculate everything during rendering + // ✅ Tốt nhất: Tính toán mọi thứ trong quá trình render const selection = items.find(item => item.id === selectedId) ?? null; // ... } ``` -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 phải "điều chỉnh" trạng thái nữa. Nếu mục có ID đã chọn nằm 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 mục phù hợ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` đều giữ nguyên lựa chọn. -### Sharing logic between event handlers {/*sharing-logic-between-event-handlers*/} +### Chia sẻ logic giữa các trình xử lý sự kiện {/*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 (Mua và Thanh toán) cho phép bạn mua sản phẩm đó. Bạn muốn hiển thị 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 cả hai trình xử lý nhấp của nút có vẻ lặp đi lặp lại, vì vậy bạn có thể muốn đặt logic này trong một Effect: ```js {2-7} function ProductPage({ product, addToCart }) { - // 🔴 Avoid: Event-specific logic inside an Effect + // 🔴 Tránh: Logic dành riêng cho sự kiện bên trong một Effect useEffect(() => { if (product.isInCart) { - showNotification(`Added ${product.name} to the shopping cart!`); + showNotification(`Đã thêm ${product.name} vào giỏ hàng!`); } }, [product]); @@ -277,16 +277,16 @@ 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 là không cần thiết. Nó cũng rất có thể gây ra lỗi. Ví dụ: giả sử ứng dụng của bạn "ghi nhớ" giỏ hàng giữa các lần tải lại 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 sản phẩm đó. Điều này là do `product.isInCart` sẽ đã là `true` khi tải trang, 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 chắn liệu một số mã nên nằm trong một Effect hay trong một trình xử lý sự kiện, hãy tự hỏi *tại sao* mã này cần chạy. Chỉ sử dụng Effect cho mã nên chạy *vì* thành phần đã được hiển thị cho người dùng.** Trong ví dụ này, thông báo sẽ 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 hàm được gọi từ cả hai trình xử lý sự kiện: ```js {2-6,9,13} function ProductPage({ product, addToCart }) { - // ✅ Good: Event-specific logic is called from event handlers + // ✅ Tốt: Logic dành riêng cho sự kiện được gọi từ các trình xử lý sự kiện function buyProduct() { addToCart(product); - showNotification(`Added ${product.name} to the shopping cart!`); + showNotification(`Đã thêm ${product.name} vào giỏ hàng!`); } function handleBuyClick() { @@ -301,23 +301,23 @@ 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ừa sửa lỗi. -### Sending a POST request {/*sending-a-post-request*/} +### Gửi một yêu cầu POST {/*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: +Thành phần `Form` này gửi hai loại yêu cầu POST. Nó gửi một sự kiện phân tích khi nó được gắn kết. Khi bạn điền vào biểu mẫu và nhấp vào nút Gửi, nó sẽ gửi một yêu cầu POST đến điểm cuối `/api/register`: ```js {5-8,10-16} function Form() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); - // ✅ Good: This logic should run because the component was displayed + // ✅ Tốt: Logic này sẽ chạy vì thành phần đã được hiển thị useEffect(() => { post('/analytics/event', { eventName: 'visit_form' }); }, []); - // 🔴 Avoid: Event-specific logic inside an Effect + // 🔴 Tránh: Logic dành riêng cho sự kiện bên trong một Effect const [jsonToSubmit, setJsonToSubmit] = useState(null); useEffect(() => { if (jsonToSubmit !== null) { @@ -333,36 +333,36 @@ function Form() { } ``` -Let's apply the same criteria as in the example before. +Hãy áp dụng các tiêu chí tương tự 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.) +Yêu cầu POST phân tích nên vẫn còn trong một Effect. Điều này là do _lý do_ để gửi sự kiện phân tích là biểu mẫu đã được hiển thị. (Nó sẽ kích hoạt hai lần trong quá trình phát triển, nhưng [xem tại đâ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, yêu cầu POST `/api/register` không phải do biểu mẫu được _hiển thị_. Bạn chỉ muốn gửi yêu cầu vào một thời điểm cụ thể: khi người dùng nhấn nút. Nó sẽ chỉ xảy ra _trong tương tác cụ thể đó_. Xóa Effect thứ hai và di chuyển yêu cầu POST đó vào trình xử lý sự kiện: ```js {12-13} function Form() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); - // ✅ Good: This logic runs because the component was displayed + // ✅ Tốt: Logic này chạy vì thành phần đã được hiển thị useEffect(() => { post('/analytics/event', { eventName: 'visit_form' }); }, []); function handleSubmit(e) { e.preventDefault(); - // ✅ Good: Event-specific logic is in the event handler + // ✅ Tốt: Logic dành riêng cho sự kiện nằm trong trình xử lý sự kiện post('/api/register', { firstName, lastName }); } // ... } ``` -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 số logic vào một trình xử lý sự kiện hay một Effect, câu hỏi chính bạn cần trả lời là _loại logic_ đó là gì từ quan điểm của 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 trình xử lý sự kiện. Nếu nó được gây ra bởi người dùng _nhìn thấy_ thành phần 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 xâu chuỗi các Effect mà mỗi Effect điều chỉnh một phần của trạng thái dựa trên trạng thái khác: ```js {7-29} function Game() { @@ -371,7 +371,7 @@ function Game() { const [round, setRound] = useState(1); const [isGameOver, setIsGameOver] = useState(false); - // 🔴 Avoid: Chains of Effects that adjust the state solely to trigger each other + // 🔴 Tránh: Chuỗi các Effect điều chỉnh trạng thái chỉ để kích hoạt lẫn nhau useEffect(() => { if (card !== null && card.gold) { setGoldCardCount(c => c + 1); @@ -406,13 +406,13 @@ function Game() { // ... ``` -There are two problems with this code. +Có hai vấn đề với đoạn 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 kém hiệu quả: thành phần (và các thành phần con của nó) phải render lại giữa mỗi lệnh gọi `set` trong chuỗi. Trong ví dụ trên, trong trường hợp xấu nhất (`setCard` → render → `setGoldCardCount` → render → `setRound` → render → `setIsGameOver` → render) có ba lần render lại cây không cần thiết 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 các trường hợp mà "chuỗi" bạn đã viết không phù hợp với các yêu cầu mới. Hãy tưởng tượng bạn đang thêm một cách để xem qua lịch sử các bước di chuyển của trò chơi. Bạn sẽ làm điều đó bằng cách cập nhật từng biến trạng thái thành một giá trị từ quá khứ. Tuy nhiên, việc đặt trạng thái `card` thành một giá trị từ quá khứ sẽ kích hoạt lại chuỗi Effect 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 trạng thái trong trình xử lý sự kiện: ```js {6-7,14-26} function Game() { @@ -420,7 +420,7 @@ function Game() { const [goldCardCount, setGoldCardCount] = useState(0); const [round, setRound] = useState(1); - // ✅ Calculate what you can during rendering + // ✅ Tính toán những gì bạn có thể trong quá trình render const isGameOver = round > 5; function handlePlaceCard(nextCard) { @@ -428,7 +428,7 @@ function Game() { throw Error('Game already ended.'); } - // ✅ Calculate all the next state in the event handler + // ✅ Tính toán tất cả trạng thái tiếp theo trong trình xử lý sự kiện setCard(nextCard); if (nextCard.gold) { if (goldCardCount <= 3) { @@ -446,21 +446,21 @@ 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 một cách để xem lịch sử trò chơi, giờ đây bạn sẽ có thể đặt từng biến trạng thái thành một bước di chuyển 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 sử dụng lại logic giữa một số trình xử lý sự kiện, bạn có thể [trích xuất một hàm](#sharing-logic-between-event-handlers) và gọi nó từ các trình xử lý đó. -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 các trình xử lý sự kiện, [trạng thái hoạt động như một ảnh chụp nhanh.](/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 nhấp vào 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 xác định nó theo cách 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 trạng thái tiếp theo trực tiếp trong trình xử lý sự kiện. Ví dụ: hãy tưởng tượng một biểu mẫu có nhiều danh sách thả xuống, trong đó các tùy chọn của danh sách thả xuống tiếp theo phụ thuộc vào giá trị đã chọn của danh sách thả xuống trước đó. Sau đó, một chuỗi các 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 tải. -You might be tempted to place it in an Effect in the top-level component: +Bạn có thể muốn đặt nó trong một Effect trong thành phần cấp cao nhất: ```js {2-6} function App() { - // 🔴 Avoid: Effects with logic that should only ever run once + // 🔴 Tránh: Effect với logic chỉ nên chạy một lần useEffect(() => { loadDataFromLocalStorage(); checkAuthToken(); @@ -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 sự cố--ví dụ: có thể nó làm mất hiệu lực mã thông báo xác thực vì hàm không được thiết kế để được gọi hai lần. Nói chung, các thành phần của bạn nên có khả năng phục hồi khi được gắn lại. Điều này bao gồm thành phần `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 gắn lại trong thực tế trong quá trình sản xuất, nhưng việc tuân theo các ràng buộc tương tự trong tất cả các thành phần giúp bạn dễ dàng di chuyển và sử dụng lại code hơn. Nếu một số logic phải chạy *một lần cho mỗi lần tải ứng dụng* thay vì *một lần cho mỗi lần gắn kết thành phần*, hãy thêm một biến cấp cao nhất để theo dõi xem nó đã được thực thi hay chưa: ```js {1,5-6,10} let didInit = false; @@ -480,7 +480,7 @@ function App() { useEffect(() => { if (!didInit) { didInit = true; - // ✅ Only runs once per app load + // ✅ Chỉ chạy một lần cho mỗi lần tải ứng dụng loadDataFromLocalStorage(); checkAuthToken(); } @@ -489,11 +489,11 @@ 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 mô-đun và trước khi ứng dụng render: ```js {1,5} -if (typeof window !== 'undefined') { // Check if we're running in the browser. - // ✅ Only runs once per app load +if (typeof window !== 'undefined') { // Kiểm tra xem chúng ta có đang chạy trong trình duyệt hay không. + // ✅ Chỉ chạy một lần cho mỗi lần tải ứng dụng checkAuthToken(); loadDataFromLocalStorage(); } @@ -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 thành phần của bạn được nhập--ngay cả khi nó không được render. Để tránh chậm trễ hoặc hành vi đáng ngạc nhiên khi nhập các thành phần tùy ý, đừng lạm dụng mẫu này. Giữ logic khởi tạo trên toàn ứng dụng cho các mô-đun thành phần gốc như `App.js` hoặc trong điểm nhập 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 các thành phần cha về các thay đổi trạng thái {/*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 thành phần `Toggle` với trạng thái `isOn` bên trong có thể là `true` hoặc `false`. Có một vài cách khác nhau để chuyển đổi nó (bằng cách nhấp hoặc kéo). Bạn muốn thông báo cho thành phần cha bất cứ khi nào trạng thái bên trong `Toggle` thay đổi, vì vậy bạn hiển thị một sự kiện `onChange` và gọi nó từ một Effect: ```js {4-7} function Toggle({ onChange }) { @@ -533,17 +533,16 @@ function Toggle({ onChange }) { // ... } ``` +Giống như trước đây, điều này không lý tưởng. `Toggle` cập nhật trạng thái của nó trước, và React cập nhật màn hình. Sau đó, React chạy Effect, gọi hàm `onChange` được truyền từ một thành phần cha. Bây giờ thành phần cha sẽ cập nhật trạng thái của chính nó, bắt đầu một lượt render khác. Sẽ tốt hơn nếu thực hiện mọi thứ trong một lượt duy nhất. -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. - -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 trạng thái của *cả hai* thành phần trong cùng một trình xử lý sự kiện: ```js {5-7,11,16,18} function Toggle({ onChange }) { const [isOn, setIsOn] = useState(false); function updateToggle(nextIsOn) { - // ✅ Good: Perform all updates during the event that caused them + // ✅ Tốt: Thực hiện tất cả các cập nhật trong sự kiện gây ra chúng setIsOn(nextIsOn); onChange(nextIsOn); } @@ -564,12 +563,12 @@ 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ả thành phần `Toggle` và thành phần cha của nó đều cập nhật trạng thái của chúng trong sự kiện. React [gom các cập nhật](/learn/queueing-a-series-of-state-updates) từ các thành phần khác nhau lại với nhau, vì vậy sẽ chỉ có một lượt render. -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 trạng thái và thay vào đó nhận `isOn` từ thành phần cha: ```js {1,2} -// ✅ Also good: the component is fully controlled by its parent +// ✅ Cũng tốt: thành phần được kiểm soát hoàn toàn bởi thành phần cha function Toggle({ isOn, onChange }) { function handleClick() { onChange(!isOn); @@ -587,11 +586,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 trạng thái lên"](/learn/sharing-state-between-components) cho phép thành phần cha kiểm soát hoàn toàn `Toggle` bằng cách chuyển đổi trạng thái của chính thành phần cha. Điều này có nghĩa là thành phần cha sẽ phải chứa nhiều logic hơn, nhưng sẽ có ít trạng thái tổng thể hơn để lo lắng. Bất cứ khi nào bạn cố gắng giữ cho hai biến trạng thái khác nhau được đồng bộ hóa, hãy thử nâng trạng thái lên thay thế! -### Passing data to the parent {/*passing-data-to-the-parent*/} +### Truyền dữ liệu cho thành phần cha {/*passing-data-to-the-parent*/} -This `Child` component fetches some data and then passes it to the `Parent` component in an Effect: +Thành phần `Child` này tìm nạp một số dữ liệu và sau đó truyền nó cho thành phần `Parent` trong một Effect: ```js {9-14} function Parent() { @@ -602,7 +601,7 @@ function Parent() { function Child({ onFetched }) { const data = useSomeAPI(); - // 🔴 Avoid: Passing data to the parent in an Effect + // 🔴 Tránh: Truyền dữ liệu cho thành phần cha trong một Effect useEffect(() => { if (data) { onFetched(data); @@ -612,13 +611,13 @@ 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 thành phần cha xuống các thành phần con của chúng. Khi bạn thấy điều gì đó không đúng 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 thành phần cho đến khi bạn tìm thấy thành phần nào truyền sai prop hoặc có trạng thái sai. Khi các thành phần con cập nhật trạng thái của các thành phần cha của chúng trong Effects, luồng dữ liệu trở nên rất khó theo dõi. Vì cả thành phần con và thành phần cha đều cần cùng một dữ liệu, hãy để thành phần cha tìm nạp dữ liệu đó và *truyền nó xuống* cho thành phần con thay thế: ```js {4-5} function Parent() { const data = useSomeAPI(); // ... - // ✅ Good: Passing data down to the child + // ✅ Tốt: Truyền dữ liệu xuống cho thành phần con return <Child data={data} />; } @@ -627,15 +626,15 @@ 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 luồng dữ liệu có thể dự đoán được: dữ liệu chảy xuống từ thành phần cha đến thành phần con. -### Subscribing to an external store {/*subscribing-to-an-external-store*/} +### Đăng ký vào một kho bên ngoài {/*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 thành phần của bạn có thể cần đăng ký vào một số dữ liệu bên ngoài trạng thái React. Dữ liệu này có thể đến từ một thư viện của bên thứ ba hoặc một API trình duyệt tích hợp. Vì dữ liệu này có thể thay đổi mà React không hề hay biết, bạn cần đăng ký thủ công các thành phần của mình 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() { - // Not ideal: Manual store subscription in an Effect + // Không lý tưởng: Đăng ký kho thủ công trong một Effect const [isOnline, setIsOnline] = useState(true); useEffect(() => { function updateState() { @@ -660,9 +659,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, thành phần đăng ký vào một kho dữ liệu bên ngoài (trong trường hợp này, API `navigator.onLine` của trình duyệt). Vì API này không tồn tại trên máy chủ (vì vậy nó không thể được sử dụng cho HTML ban đầu), ban đầu trạng thái được đặt thành `true`. Bất cứ khi nào giá trị của kho dữ liệu đó thay đổi trong trình duyệt, thành phần sẽ cập nhật trạng thái 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 Effects cho việc này là phổ biến, nhưng React có một Hook được xây dựng có mục đích để đăng ký vào một kho bên ngoài được ưu tiên hơn. Xóa Effect và thay thế nó bằng một lệnh gọi đến [`useSyncExternalStore`](/reference/react/useSyncExternalStore): ```js {11-16} function subscribe(callback) { @@ -675,11 +674,11 @@ function subscribe(callback) { } function useOnlineStatus() { - // ✅ Good: Subscribing to an external store with a built-in Hook + // ✅ Tốt: Đăng ký vào một kho bên ngoài với một Hook tích hợp return useSyncExternalStore( - subscribe, // React won't resubscribe for as long as you pass the same function - () => navigator.onLine, // How to get the value on the client - () => true // How to get the value on the server + subscribe, // React sẽ không đăng ký lại miễn là bạn truyền cùng một hàm + () => navigator.onLine, // Cách lấy giá trị trên máy khách + () => true // Cách lấy giá trị trên máy chủ ); } @@ -689,11 +688,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 gây ra lỗi hơn so với việc đồng bộ hóa thủ công dữ liệu có thể thay đổi với trạng thái React bằng một Effect. Thông thường, bạn sẽ viết một Hook tùy chỉnh như `useOnlineStatus()` ở trên để bạn không cần lặp lại mã này trong các thành phần riêng lẻ. [Đọc thêm về đăng ký vào các kho bên ngoài từ các thành phần React.](/reference/react/useSyncExternalStore) -### Fetching data {/*fetching-data*/} +### Tìm nạp 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 Effects để bắt đầu tìm nạp dữ liệu. Việc viết một Effect tìm nạp dữ liệu như thế này là khá phổ biến: ```js {5-10} function SearchResults({ query }) { @@ -701,7 +700,7 @@ function SearchResults({ query }) { const [page, setPage] = useState(1); useEffect(() => { - // 🔴 Avoid: Fetching without cleanup logic + // 🔴 Tránh: Tìm nạp mà không có logic dọn dẹp fetchResults(query, page).then(json => { setResults(json); }); @@ -714,15 +713,15 @@ function SearchResults({ query }) { } ``` -You *don't* need to move this fetch to an event handler. +Bạn *không* cần phải di chuyển quá trình tìm nạp này sang một trình xử lý sự kiện. -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ó vẻ như một mâu thuẫn với các ví dụ trước đó, nơi bạn cần đặt logic vào các trình xử lý sự kiện! Tuy nhiên, hãy xem xét rằng không phải *sự kiện gõ* là lý do chính để tìm nạp. Các đầu vào tìm kiếm thường được điền trước từ URL và người dùng có thể điều hướng Quay lại và Chuyển tiếp mà không cần chạm vào đầu vào. -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 thành phần này hiển thị, bạn muốn giữ cho `results` được [đồng bộ hóa](/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, mã trên có một lỗi. Hãy tưởng tượng bạn gõ `"hello"` nhanh. Sau đó, `query` sẽ thay đổi từ `"h"`, thành `"he"`, `"hel"`, `"hell"`, và `"hello"`. Điều này sẽ bắt đầu các quá trình tìm nạp riêng biệt, nhưng không có gì đảm bảo về thứ tự các phản hồi sẽ đến. Ví dụ: phản hồi `hell"` có thể đến *sau* phản hồi `"hello"`. Vì nó sẽ gọi `setResults()` cuối cùng, bạn sẽ hiển thị sai kết quả tìm kiếm. Điều này được gọi là một ["điều kiện cuộc đua"](https://en.wikipedia.org/wiki/Race_condition): hai yêu cầu khác nhau "chạy đua" với nhau và đến theo một thứ tự khác với những gì bạn mong đợi. -**To fix the race condition, you need to [add a cleanup function](/learn/synchronizing-with-effects#fetching-data) to ignore stale responses:** +**Để khắc phục điều kiện cuộc đua, bạn cần [thêm một hàm dọn dẹp](/learn/synchronizing-with-effects#fetching-data) để bỏ qua các phản hồi cũ:** ```js {5,7,9,11-13} function SearchResults({ query }) { @@ -747,13 +746,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 tìm nạp dữ liệu, tất cả các phản hồi ngoại trừ phản hồi được yêu cầu cuối cùng 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ý các điều kiện cuộc đua không phải là khó khăn duy nhất khi triển khai tìm nạp dữ liệu. Bạn cũng có thể muốn nghĩ về việc lưu vào bộ nhớ cache các phản hồi (để người dùng có thể nhấp vào Quay lại và xem màn hình trước đó ngay lập tức), cách tìm nạp dữ liệu trên máy chủ (để HTML được hiển thị ban đầu trên máy chủ chứa nội dung đã tìm nạp thay vì một trình quay), và cách tránh các thác nước mạng (để một thành phần con có thể tìm nạp dữ liệu mà không cần chờ đợi mọi thành phần 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 giao diện người dùng nào, không chỉ React. Giải quyết chúng không phải là điều tầm thường, đó là lý do tại sao các [khung](/learn/start-a-new-react-project#production-grade-react-frameworks) hiện đại cung cấp các cơ chế tìm nạp dữ liệu tích hợp hiệu quả hơn so với việc tìm nạp dữ liệu trong Effects.** -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 một khung (và không muốn xây dựng khung của riêng bạn) nhưng muốn làm cho việc tìm nạp dữ liệu từ Effects trở nên tiện dụng hơn, hãy cân nhắc trích xuất logic tìm nạp của bạn vào một Hook tùy chỉnh như trong ví dụ này: ```js {4} function SearchResults({ query }) { @@ -786,30 +785,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 muốn thêm một số logic để xử lý lỗi và theo dõi xem nội dung có đang tải hay không. Bạn có thể xây dựng một Hook như thế này cho chính mình hoặc sử dụng một trong nhiều giải pháp đã có sẵn trong hệ sinh thái React. **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ế tìm nạp dữ liệu tích hợp của một khung, nhưng việc di chuyển logic tìm nạp dữ liệu vào một Hook tùy chỉnh sẽ giúp bạn dễ dàng áp dụng một chiến lược tìm nạp dữ liệu hiệu quả hơn sau này.** -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 dùng đến việc viết Effects, hãy để ý đến khi nào bạn có thể trích xuất một phần chức năng vào một Hook tùy chỉnh với một API khai báo và có mục đích xây dựng hơn như `useData` ở trên. Càng ít lệnh gọi `useEffect` thô mà bạn có trong các thành phần của mình, bạn sẽ càng thấy dễ dàng hơn để bảo trì ứng dụng của mình. <Recap> -- 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 một cái gì đó trong quá trình render, bạn không cần một Effect. +- Để lưu vào bộ nhớ cache các phép tính tốn kém, hãy thêm `useMemo` thay vì `useEffect`. +- Để đặt lại trạng thái của toàn bộ cây thành phần, hãy truyền một `key` khác cho nó. +- Để đặt lại một bit trạng thái cụ thể để đáp ứng với một thay đổi prop, hãy đặt nó trong quá trình render. +- Mã chạy vì một thành phần đã được *hiển thị* nên nằm trong Effects, phần còn lại nên nằm trong các sự kiện. +- Nếu bạn cần cập nhật trạng thái của một số thành phần, tốt hơn là thực hiện nó 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 trạng thái trong các thành phần khác nhau, hãy cân nhắc nâng trạng thái lên. +- Bạn có thể tìm nạp dữ liệu với Effects, nhưng bạn cần triển khai dọn dẹp để tránh các điều kiện cuộc đua. </Recap> <Challenges> -#### Transform data without Effects {/*transform-data-without-effects*/} +#### Chuyển đổi dữ liệu mà không cần Effects {/*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 hộp kiểm "Chỉ hiển thị các todo đang hoạt động" được đánh dấu, các todo đã hoàn thành sẽ không được hiển thị trong danh sách. Bất kể todo nào hiển thị, chân trang 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 thành phần này bằng cách loại bỏ tất cả các trạng thái và Effects không cần thiết. <Sandpack> @@ -909,15 +908,15 @@ input { margin-top: 10px; } <Hint> -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 một cái gì đó trong quá trình render, bạn không cần trạng thái hoặc một Effect để cập nhật nó. </Hint> <Solution> -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 trạng thái thiết yếu trong ví dụ này: danh sách `todos` và biến trạng thái `showActive` đại diện cho việc hộp kiểm có được đánh dấu hay không. Tất cả các biến trạng thái 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 cả `footer` mà bạn có thể di 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 sẽ trông như thế này: <Sandpack> @@ -1002,15 +1001,15 @@ input { margin-top: 10px; } </Solution> -#### Cache a calculation without Effects {/*cache-a-calculation-without-effects*/} +#### Cache một phép tính mà không cần Effects {/*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 các todo đã được trích xuất thành một hàm riêng biệt có tên là `getVisibleTodos()`. Hàm này chứa một lệnh gọi `console.log()` bên trong nó giúp bạn nhận thấy khi nào nó đang được gọi. Chuyển đổi "Chỉ hiển thị các todo đang hoạt động" và nhận thấy rằng nó khiến `getVisibleTodos()` chạy lại. Điều này là dự kiến vì các todo hiển thị thay đổi khi bạn chuyển đổi những todo nào sẽ 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 thành phần `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ỳ nhật ký nào) khi bạn nhập vào đầu vào. <Hint> -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ệnh gọi `useMemo` để lưu vào bộ nhớ cache các todo hiển thị. Ngoài ra còn có một giải pháp khác, ít rõ ràng hơn. </Hint> @@ -1096,7 +1095,7 @@ input { margin-top: 10px; } <Solution> -Remove the state variable and the Effect, and instead add a `useMemo` call to cache the result of calling `getVisibleTodos()`: +Xóa biến trạng thái và Effect, và thay vào đó thêm một lệnh gọi `useMemo` để lưu vào bộ nhớ cache kết quả của việc gọi `getVisibleTodos()`: <Sandpack> @@ -1177,9 +1176,9 @@ input { margin-top: 10px; } </Sandpack> -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. Nhập vào đầu vào chỉ thay đổi biến trạng thái `text`, vì vậy nó không kích hoạt một lệnh 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: +Ngoài ra còn có một giải pháp khác không cần `useMemo`. Vì biến trạng thái `text` không thể ảnh hưởng đến danh sách các todo, bạn có thể trích xuất biểu mẫu `NewTodo` thành một thành phần riêng biệt và di chuyển biến trạng thái `text` vào bên trong nó: <Sandpack> @@ -1266,15 +1265,16 @@ input { margin-top: 10px; } </Sandpack> -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 đáp ứng các yêu cầu. Khi bạn nhập vào đầu vào, chỉ biến trạng thái `text` được cập nhật. Vì biến trạng thái `text` nằm trong thành phần con `NewTodo`, thành phần `TodoList` cha sẽ không được render lại. Đây là lý do tại sao `getVisibleTodos()` không được gọi khi bạn nhập. (Nó vẫn sẽ được gọi nếu `TodoList` render lại vì một lý do khác.) </Solution> -#### Reset state without Effects {/*reset-state-without-effects*/} +#### Đặt lại trạng thái mà không cần Effects {/*reset-state-without-effects*/} + +Thành phần `EditContact` này nhận một đối tượng liên hệ có dạng như `{ id, name, email }` làm prop `savedContact`. Hãy thử chỉnh sửa các trường nhập tên và email. Khi bạn nhấn Lưu, nút liên hệ phía trên biểu mẫu sẽ cập nhật theo tên đã chỉnh sửa. Khi bạn nhấn Đặt lại, mọi thay đổi đang chờ xử lý trong biểu mẫu sẽ bị loại bỏ. Hãy chơi với giao diện người dùng này để làm quen với nó. -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. +Khi bạn chọn một liên hệ bằng các nút ở trên cùng, biểu mẫu sẽ đặt lại để phản ánh chi tiết của liên hệ đó. Điều này được thực hiện bằng một Effect bên trong `EditContact.js`. Loại bỏ Effect này. Tìm một cách khác để đặt lại biểu mẫu khi `savedContact.id` thay đổi. -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. <Sandpack> @@ -1432,13 +1432,12 @@ button { <Hint> -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? - +Nếu có cách nào để báo cho React biết rằng khi `savedContact.id` khác, thì biểu mẫu `EditContact` về mặt khái niệm là _biểu mẫu của một liên hệ khác_ và không nên giữ lại trạng thái. Bạn có nhớ cách nào như vậy không? </Hint> <Solution> -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 thành phần `EditContact` thành hai. Chuyển tất cả trạng thái biểu mẫu vào thành phần `EditForm` bên trong. Xuất thành phần `EditContact` bên ngoài và làm cho nó truyền `savedContact.id` làm `key` cho thành phần `EditForm` bên trong. Do đó, thành phần `EditForm` bên trong sẽ đặt lại tất cả trạng thái biểu mẫu và tạo lại DOM bất cứ khi nào bạn chọn một liên hệ khác. <Sandpack> @@ -1600,17 +1599,17 @@ button { </Solution> -#### Submit a form without Effects {/*submit-a-form-without-effects*/} +#### Gửi biểu mẫu mà không cần Hiệu ứng {/*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. +Thành phần `Form` này cho phép bạn gửi tin nhắn cho một người bạn. Khi bạn gửi biểu mẫu, biến trạng thái `showForm` được đặt thành `false`. Điều này kích hoạt một Hiệu ứng gọi `sendMessage(message)`, để gửi tin nhắn (bạn có thể thấy nó trong bảng điều khiển). Sau khi tin nhắn được gửi, bạn sẽ thấy một hộp thoại "Cảm ơn" với nút "Mở trò chuyện" cho phép bạn quay lại biểu mẫu. -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 trò chuyện trở nên khó khăn hơn một chút, bạn đã quyết định hiển thị hộp thoại "Cảm ơn" *trước* thay vì biểu mẫu. Thay đổi biến trạng thái `showForm` để khởi tạo thành `false` thay vì `true`. Ngay sau khi bạn thực hiện thay đổi đó, bảng điều khiển sẽ hiển thị rằng một tin nhắn trống đã được gửi. Có gì đó không đúng trong logic này! -What's the root cause of this problem? And how can you fix it? +Đâu là nguyên nhân gốc rễ của vấn đề này? Và làm thế nào bạn có thể sửa nó? <Hint> -Should the message be sent _because_ the user saw the "Thank you" dialog? Or is it the other way around? +Có phải tin nhắn được gửi _vì_ người dùng đã thấy hộp thoại "Cảm ơn"? Hay là ngược lại? </Hint> @@ -1675,7 +1674,7 @@ label, textarea { margin-bottom: 10px; display: block; } <Solution> -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 trạng thái `showForm` xác định xem có hiển thị biểu mẫu hay hộp thoại "Cảm ơn". Tuy nhiên, bạn không gửi tin nhắn vì hộp thoại "Cảm ơn" đã được _hiển thị_. Bạn muốn gửi tin nhắn vì người dùng đã _gửi biểu mẫu_. Xóa Hiệu ứng gây hiểu lầm và di chuyển lệnh gọi `sendMessage` vào bên trong trình xử lý sự kiện `handleSubmit`: <Sandpack> @@ -1731,7 +1730,7 @@ label, textarea { margin-bottom: 10px; display: block; } </Sandpack> -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 ý cách trong phiên bản này, chỉ _gửi biểu mẫu_ (đó là một sự kiện) mới khiến tin nhắn được gửi. Nó hoạt động tốt như nhau bất kể `showForm` ban đầu được đặt thành `true` hay `false`. (Đặt nó thành `false` và lưu ý không có thêm tin nhắn bảng điều khiển.) </Solution> diff --git a/src/content/reference/react-dom/client/createRoot.md b/src/content/reference/react-dom/client/createRoot.md index adc6a8d37..6e33c9ad3 100644 --- a/src/content/reference/react-dom/client/createRoot.md +++ b/src/content/reference/react-dom/client/createRoot.md @@ -4,7 +4,7 @@ title: createRoot <Intro> -`createRoot` lets you create a root to display React components inside a browser DOM node. +`createRoot` cho phép bạn tạo một gốc để hiển thị các thành phần React bên trong một nút DOM của trình duyệt. ```js const root = createRoot(domNode, options?) @@ -16,11 +16,11 @@ const root = createRoot(domNode, options?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `createRoot(domNode, options?)` {/*createroot*/} -Call `createRoot` to create a React root for displaying content inside a browser DOM element. +Gọi `createRoot` để tạo một gốc React để hiển thị nội dung bên trong một phần tử DOM của trình duyệt. ```js import { createRoot } from 'react-dom/client'; @@ -29,73 +29,73 @@ const domNode = document.getElementById('root'); const root = createRoot(domNode); ``` -React will create a root for the `domNode`, and take over managing the DOM inside it. After you've created a root, you need to call [`root.render`](#root-render) to display a React component inside of it: +React sẽ tạo một gốc cho `domNode` và tiếp quản việc quản lý DOM bên trong nó. Sau khi bạn đã tạo một gốc, bạn cần gọi [`root.render`](#root-render) để hiển thị một thành phần React bên trong nó: ```js root.render(<App />); ``` -An app fully built with React will usually only have one `createRoot` call for its root component. A page that uses "sprinkles" of React for parts of the page may have as many separate roots as needed. +Một ứng dụng được xây dựng hoàn toàn bằng React thường chỉ có một lệnh gọi `createRoot` cho thành phần gốc của nó. Một trang sử dụng "rắc" React cho các phần của trang có thể có nhiều gốc riêng biệt khi cần thiết. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `domNode`: A [DOM element.](https://developer.mozilla.org/en-US/docs/Web/API/Element) React will create a root for this DOM element and allow you to call functions on the root, such as `render` to display rendered React content. +* `domNode`: Một [phần tử DOM.](https://developer.mozilla.org/en-US/docs/Web/API/Element) React sẽ tạo một gốc cho phần tử DOM này và cho phép bạn gọi các hàm trên gốc, chẳng hạn như `render` để hiển thị nội dung React đã được render. -* **optional** `options`: An object with options for this React root. +* **tùy chọn** `options`: Một đối tượng với các tùy chọn cho gốc React này. - * **optional** `onCaughtError`: Callback called when React catches an error in an Error Boundary. Called with the `error` caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`. - * **optional** `onUncaughtError`: Callback called when an error is thrown and not caught by an Error Boundary. Called with the `error` that was thrown, and an `errorInfo` object containing the `componentStack`. - * **optional** `onRecoverableError`: Callback called when React automatically recovers from errors. Called with an `error` React throws, and an `errorInfo` object containing the `componentStack`. Some recoverable errors may include the original error cause as `error.cause`. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. + * **tùy chọn** `onCaughtError`: Callback được gọi khi React bắt được lỗi trong một Error Boundary. Được gọi với `error` bị bắt bởi Error Boundary và một đối tượng `errorInfo` chứa `componentStack`. + * **tùy chọn** `onUncaughtError`: Callback được gọi khi một lỗi được ném ra và không bị bắt bởi một Error Boundary. Được gọi với `error` đã được ném ra và một đối tượng `errorInfo` chứa `componentStack`. + * **tùy chọn** `onRecoverableError`: Callback được gọi khi React tự động phục hồi từ các lỗi. Được gọi với một `error` mà React ném ra và một đối tượng `errorInfo` chứa `componentStack`. Một số lỗi có thể phục hồi có thể bao gồm nguyên nhân gây ra lỗi ban đầu là `error.cause`. + * **tùy chọn** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều gốc trên cùng một trang. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`createRoot` returns an object with two methods: [`render`](#root-render) and [`unmount`.](#root-unmount) +`createRoot` trả về một đối tượng với hai phương thức: [`render`](#root-render) và [`unmount`.](#root-unmount) -#### Caveats {/*caveats*/} -* If your app is server-rendered, using `createRoot()` is not supported. Use [`hydrateRoot()`](/reference/react-dom/client/hydrateRoot) instead. -* You'll likely have only one `createRoot` call in your app. If you use a framework, it might do this call for you. -* When you want to render a piece of JSX in a different part of the DOM tree that isn't a child of your component (for example, a modal or a tooltip), use [`createPortal`](/reference/react-dom/createPortal) instead of `createRoot`. +#### Lưu ý {/*caveats*/} +* Nếu ứng dụng của bạn được render phía máy chủ, việc sử dụng `createRoot()` không được hỗ trợ. Sử dụng [`hydrateRoot()`](/reference/react-dom/client/hydrateRoot) thay thế. +* Bạn có thể chỉ có một lệnh gọi `createRoot` trong ứng dụng của mình. Nếu bạn sử dụng một framework, nó có thể thực hiện lệnh gọi này cho bạn. +* Khi bạn muốn render một đoạn JSX ở một phần khác của cây DOM mà không phải là con của thành phần của bạn (ví dụ: một modal hoặc một tooltip), hãy sử dụng [`createPortal`](/reference/react-dom/createPortal) thay vì `createRoot`. --- ### `root.render(reactNode)` {/*root-render*/} -Call `root.render` to display a piece of [JSX](/learn/writing-markup-with-jsx) ("React node") into the React root's browser DOM node. +Gọi `root.render` để hiển thị một đoạn [JSX](/learn/writing-markup-with-jsx) ("React node") vào nút DOM của trình duyệt của gốc React. ```js root.render(<App />); ``` -React will display `<App />` in the `root`, and take over managing the DOM inside it. +React sẽ hiển thị `<App />` trong `root` và tiếp quản việc quản lý DOM bên trong nó. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*root-render-parameters*/} +#### Tham số {/*root-render-parameters*/} -* `reactNode`: A *React node* that you want to display. This will usually be a piece of JSX like `<App />`, but you can also pass a React element constructed with [`createElement()`](/reference/react/createElement), a string, a number, `null`, or `undefined`. +* `reactNode`: Một *React node* mà bạn muốn hiển thị. Đây thường sẽ là một đoạn JSX như `<App />`, nhưng bạn cũng có thể truyền một phần tử React được xây dựng bằng [`createElement()`](/reference/react/createElement), một chuỗi, một số, `null` hoặc `undefined`. -#### Returns {/*root-render-returns*/} +#### Trả về {/*root-render-returns*/} -`root.render` returns `undefined`. +`root.render` trả về `undefined`. -#### Caveats {/*root-render-caveats*/} +#### Lưu ý {/*root-render-caveats*/} -* The first time you call `root.render`, React will clear all the existing HTML content inside the React root before rendering the React component into it. +* Lần đầu tiên bạn gọi `root.render`, React sẽ xóa tất cả nội dung HTML hiện có bên trong gốc React trước khi render thành phần React vào đó. -* If your root's DOM node contains HTML generated by React on the server or during the build, use [`hydrateRoot()`](/reference/react-dom/client/hydrateRoot) instead, which attaches the event handlers to the existing HTML. +* Nếu nút DOM của gốc của bạn chứa HTML được tạo bởi React trên máy chủ hoặc trong quá trình xây dựng, hãy sử dụng [`hydrateRoot()`](/reference/react-dom/client/hydrateRoot) thay thế, nó sẽ đính kèm các trình xử lý sự kiện vào HTML hiện có. -* If you call `render` on the same root more than once, React will update the DOM as necessary to reflect the latest JSX you passed. React will decide which parts of the DOM can be reused and which need to be recreated by ["matching it up"](/learn/preserving-and-resetting-state) with the previously rendered tree. Calling `render` on the same root again is similar to calling the [`set` function](/reference/react/useState#setstate) on the root component: React avoids unnecessary DOM updates. +* Nếu bạn gọi `render` trên cùng một gốc nhiều lần, React sẽ cập nhật DOM khi cần thiết để phản ánh JSX mới nhất mà bạn đã truyền. React sẽ quyết định phần nào của DOM có thể được sử dụng lại và phần nào cần được tạo lại bằng cách ["ghép nó lại"](/learn/preserving-and-resetting-state) với cây đã được render trước đó. Gọi `render` trên cùng một gốc một lần nữa tương tự như gọi hàm [`set`](/reference/react/useState#setstate) trên thành phần gốc: React tránh các cập nhật DOM không cần thiết. -* Although rendering is synchronous once it starts, `root.render(...)` is not. This means code after `root.render()` may run before any effects (`useLayoutEffect`, `useEffect`) of that specific render are fired. This is usually fine and rarely needs adjustment. In rare cases where effect timing matters, you can wrap `root.render(...)` in [`flushSync`](https://react.dev/reference/react-dom/client/flushSync) to ensure the initial render runs fully synchronously. +* Mặc dù việc render là đồng bộ sau khi nó bắt đầu, `root.render(...)` thì không. Điều này có nghĩa là mã sau `root.render()` có thể chạy trước bất kỳ hiệu ứng nào (`useLayoutEffect`, `useEffect`) của quá trình render cụ thể đó được kích hoạt. Điều này thường ổn và hiếm khi cần điều chỉnh. Trong những trường hợp hiếm hoi mà thời gian hiệu ứng quan trọng, bạn có thể bọc `root.render(...)` trong [`flushSync`](https://react.dev/reference/react-dom/client/flushSync) để đảm bảo quá trình render ban đầu chạy hoàn toàn đồng bộ. ```js const root = createRoot(document.getElementById('root')); root.render(<App />); - // 🚩 The HTML will not include the rendered <App /> yet: + // 🚩 HTML sẽ không bao gồm <App /> đã được render: console.log(document.body.innerHTML); ``` @@ -103,41 +103,41 @@ React will display `<App />` in the `root`, and take over managing the DOM insid ### `root.unmount()` {/*root-unmount*/} -Call `root.unmount` to destroy a rendered tree inside a React root. +Gọi `root.unmount` để phá hủy một cây đã được render bên trong một gốc React. ```js root.unmount(); ``` -An app fully built with React will usually not have any calls to `root.unmount`. +Một ứng dụng được xây dựng hoàn toàn bằng React thường sẽ không có bất kỳ lệnh gọi nào đến `root.unmount`. -This is mostly useful if your React root's DOM node (or any of its ancestors) may get removed from the DOM by some other code. For example, imagine a jQuery tab panel that removes inactive tabs from the DOM. If a tab gets removed, everything inside it (including the React roots inside) would get removed from the DOM as well. In that case, you need to tell React to "stop" managing the removed root's content by calling `root.unmount`. Otherwise, the components inside the removed root won't know to clean up and free up global resources like subscriptions. +Điều này chủ yếu hữu ích nếu nút DOM của gốc React của bạn (hoặc bất kỳ tổ tiên nào của nó) có thể bị xóa khỏi DOM bởi một số mã khác. Ví dụ: hãy tưởng tượng một bảng điều khiển tab jQuery loại bỏ các tab không hoạt động khỏi DOM. Nếu một tab bị xóa, mọi thứ bên trong nó (bao gồm cả các gốc React bên trong) cũng sẽ bị xóa khỏi DOM. Trong trường hợp đó, bạn cần báo cho React "dừng" quản lý nội dung của gốc đã bị xóa bằng cách gọi `root.unmount`. Nếu không, các thành phần bên trong gốc đã bị xóa sẽ không biết cách dọn dẹp và giải phóng các tài nguyên toàn cục như đăng ký. -Calling `root.unmount` will unmount all the components in the root and "detach" React from the root DOM node, including removing any event handlers or state in the tree. +Gọi `root.unmount` sẽ unmount tất cả các thành phần trong gốc và "tách" React khỏi nút DOM gốc, bao gồm cả việc xóa bất kỳ trình xử lý sự kiện hoặc trạng thái nào trong cây. -#### Parameters {/*root-unmount-parameters*/} +#### Tham số {/*root-unmount-parameters*/} -`root.unmount` does not accept any parameters. +`root.unmount` không chấp nhận bất kỳ tham số nào. -#### Returns {/*root-unmount-returns*/} +#### Trả về {/*root-unmount-returns*/} -`root.unmount` returns `undefined`. +`root.unmount` trả về `undefined`. -#### Caveats {/*root-unmount-caveats*/} +#### Lưu ý {/*root-unmount-caveats*/} -* Calling `root.unmount` will unmount all the components in the tree and "detach" React from the root DOM node. +* Gọi `root.unmount` sẽ unmount tất cả các thành phần trong cây và "tách" React khỏi nút DOM gốc. -* Once you call `root.unmount` you cannot call `root.render` again on the same root. Attempting to call `root.render` on an unmounted root will throw a "Cannot update an unmounted root" error. However, you can create a new root for the same DOM node after the previous root for that node has been unmounted. +* Sau khi bạn gọi `root.unmount`, bạn không thể gọi lại `root.render` trên cùng một gốc. Cố gắng gọi `root.render` trên một gốc đã unmount sẽ ném ra lỗi "Không thể cập nhật một gốc đã unmount". Tuy nhiên, bạn có thể tạo một gốc mới cho cùng một nút DOM sau khi gốc trước đó cho nút đó đã được unmount. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering an app fully built with React {/*rendering-an-app-fully-built-with-react*/} +### Render một ứng dụng được xây dựng hoàn toàn bằng React {/*rendering-an-app-fully-built-with-react*/} -If your app is fully built with React, create a single root for your entire app. +Nếu ứng dụng của bạn được xây dựng hoàn toàn bằng React, hãy tạo một gốc duy nhất cho toàn bộ ứng dụng của bạn. ```js [[1, 3, "document.getElementById('root')"], [2, 4, "<App />"]] import { createRoot } from 'react-dom/client'; @@ -146,19 +146,19 @@ const root = createRoot(document.getElementById('root')); root.render(<App />); ``` -Usually, you only need to run this code once at startup. It will: +Thông thường, bạn chỉ cần chạy mã này một lần khi khởi động. Nó sẽ: -1. Find the <CodeStep step={1}>browser DOM node</CodeStep> defined in your HTML. -2. Display the <CodeStep step={2}>React component</CodeStep> for your app inside. +1. Tìm <CodeStep step={1}>nút DOM của trình duyệt</CodeStep> được xác định trong HTML của bạn. +2. Hiển thị <CodeStep step={2}>thành phần React</CodeStep> cho ứng dụng của bạn bên trong. <Sandpack> ```html public/index.html <!DOCTYPE html> <html> - <head><title>My app</title></head> + <head><title>Ứng dụng của tôi</title></head> <body> - <!-- This is the DOM node --> + {/* Đây là nút DOM */} <div id="root"></div> </body> </html> @@ -179,7 +179,7 @@ import { useState } from 'react'; export default function App() { return ( <> - <h1>Hello, world!</h1> + <h1>Xin chào, thế giới!</h1> <Counter /> </> ); @@ -189,7 +189,7 @@ function Counter() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> - You clicked me {count} times + Bạn đã nhấp vào tôi {count} lần </button> ); } @@ -197,46 +197,46 @@ function Counter() { </Sandpack> -**If your app is fully built with React, you shouldn't need to create any more roots, or to call [`root.render`](#root-render) again.** +**Nếu ứng dụng của bạn được xây dựng hoàn toàn bằng React, bạn không cần tạo thêm bất kỳ gốc nào hoặc gọi lại [`root.render`](#root-render).** -From this point on, React will manage the DOM of your entire app. To add more components, [nest them inside the `App` component.](/learn/importing-and-exporting-components) When you need to update the UI, each of your components can do this by [using state.](/reference/react/useState) When you need to display extra content like a modal or a tooltip outside the DOM node, [render it with a portal.](/reference/react-dom/createPortal) +Từ thời điểm này trở đi, React sẽ quản lý DOM của toàn bộ ứng dụng của bạn. Để thêm nhiều thành phần hơn, [lồng chúng bên trong thành phần `App`.](/learn/importing-and-exporting-components) Khi bạn cần cập nhật UI, mỗi thành phần của bạn có thể thực hiện việc này bằng cách [sử dụng state.](/reference/react/useState) Khi bạn cần hiển thị nội dung bổ sung như modal hoặc tooltip bên ngoài nút DOM, [render nó bằng một portal.](/reference/react-dom/createPortal) <Note> -When your HTML is empty, the user sees a blank page until the app's JavaScript code loads and runs: +Khi HTML của bạn trống, người dùng sẽ thấy một trang trống cho đến khi mã JavaScript của ứng dụng tải và chạy: ```html <div id="root"></div> ``` -This can feel very slow! To solve this, you can generate the initial HTML from your components [on the server or during the build.](/reference/react-dom/server) Then your visitors can read text, see images, and click links before any of the JavaScript code loads. We recommend [using a framework](/learn/start-a-new-react-project#production-grade-react-frameworks) that does this optimization out of the box. Depending on when it runs, this is called *server-side rendering (SSR)* or *static site generation (SSG).* +Điều này có thể cảm thấy rất chậm! Để giải quyết vấn đề này, bạn có thể tạo HTML ban đầu từ các thành phần của mình [trên máy chủ hoặc trong quá trình xây dựng.](/reference/react-dom/server) Sau đó, khách truy cập của bạn có thể đọc văn bản, xem hình ảnh và nhấp vào liên kết trước khi bất kỳ mã JavaScript nào tải. Chúng tôi khuyên bạn nên [sử dụng một framework](/learn/start-a-new-react-project#production-grade-react-frameworks) thực hiện tối ưu hóa này ngay lập tức. Tùy thuộc vào thời điểm nó chạy, điều này được gọi là *server-side rendering (SSR)* hoặc *static site generation (SSG).* </Note> <Pitfall> -**Apps using server rendering or static generation must call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) instead of `createRoot`.** React will then *hydrate* (reuse) the DOM nodes from your HTML instead of destroying and re-creating them. +**Các ứng dụng sử dụng server rendering hoặc static generation phải gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) thay vì `createRoot`.** React sau đó sẽ *hydrate* (sử dụng lại) các nút DOM từ HTML của bạn thay vì phá hủy và tạo lại chúng. </Pitfall> --- -### Rendering a page partially built with React {/*rendering-a-page-partially-built-with-react*/} +### Render một trang được xây dựng một phần bằng React {/*rendering-a-page-partially-built-with-react*/} -If your page [isn't fully built with React](/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page), you can call `createRoot` multiple times to create a root for each top-level piece of UI managed by React. You can display different content in each root by calling [`root.render`.](#root-render) +Nếu trang của bạn [không được xây dựng hoàn toàn bằng React](/learn/add-react-to-an-existing-project#using-react-for-a-part-of-your-existing-page), bạn có thể gọi `createRoot` nhiều lần để tạo một gốc cho mỗi phần UI cấp cao nhất được quản lý bởi React. Bạn có thể hiển thị nội dung khác nhau trong mỗi gốc bằng cách gọi [`root.render`.](#root-render) -Here, two different React components are rendered into two DOM nodes defined in the `index.html` file: +Ở đây, hai thành phần React khác nhau được render vào hai nút DOM được xác định trong tệp `index.html`: <Sandpack> ```html public/index.html <!DOCTYPE html> <html> - <head><title>My app</title></head> + <head><title>Ứng dụng của tôi</title></head> <body> <nav id="navigation"></nav> <main> - <p>This paragraph is not rendered by React (open index.html to verify).</p> + <p>Đoạn văn này không được render bởi React (mở index.html để xác minh).</p> <section id="comments"></section> </main> </body> @@ -261,8 +261,8 @@ commentRoot.render(<Comments />); export function Navigation() { return ( <ul> - <NavLink href="/">Home</NavLink> - <NavLink href="/about">About</NavLink> + <NavLink href="/">Trang chủ</NavLink> + <NavLink href="/about">Giới thiệu</NavLink> </ul> ); } @@ -278,9 +278,9 @@ function NavLink({ href, children }) { export function Comments() { return ( <> - <h2>Comments</h2> - <Comment text="Hello!" author="Sophie" /> - <Comment text="How are you?" author="Sunil" /> + <h2>Bình luận</h2> + <Comment text="Xin chào!" author="Sophie" /> + <Comment text="Bạn khỏe không?" author="Sunil" /> </> ); } @@ -299,28 +299,28 @@ nav ul li { display: inline-block; margin-right: 20px; } </Sandpack> -You could also create a new DOM node with [`document.createElement()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) and add it to the document manually. +Bạn cũng có thể tạo một nút DOM mới bằng [`document.createElement()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement) và thêm nó vào tài liệu theo cách thủ công. ```js const domNode = document.createElement('div'); const root = createRoot(domNode); root.render(<Comment />); -document.body.appendChild(domNode); // You can add it anywhere in the document +document.body.appendChild(domNode); // Bạn có thể thêm nó vào bất kỳ đâu trong tài liệu ``` -To remove the React tree from the DOM node and clean up all the resources used by it, call [`root.unmount`.](#root-unmount) +Để xóa cây React khỏi nút DOM và dọn dẹp tất cả các tài nguyên được sử dụng bởi nó, hãy gọi [`root.unmount`.](#root-unmount) ```js root.unmount(); ``` -This is mostly useful if your React components are inside an app written in a different framework. +Điều này chủ yếu hữu ích nếu các thành phần React của bạn nằm bên trong một ứng dụng được viết bằng một framework khác. --- -### Updating a root component {/*updating-a-root-component*/} +### Cập nhật một thành phần gốc {/*updating-a-root-component*/} -You can call `render` more than once on the same root. As long as the component tree structure matches up with what was previously rendered, React will [preserve the state.](/learn/preserving-and-resetting-state) Notice how you can type in the input, which means that the updates from repeated `render` calls every second in this example are not destructive: +Bạn có thể gọi `render` nhiều lần trên cùng một gốc. Miễn là cấu trúc cây thành phần khớp với những gì đã được render trước đó, React sẽ [giữ lại trạng thái.](/learn/preserving-and-resetting-state) Lưu ý cách bạn có thể nhập vào đầu vào, điều đó có nghĩa là các bản cập nhật từ các lệnh gọi `render` lặp đi lặp lại mỗi giây trong ví dụ này không mang tính phá hủy: <Sandpack> @@ -342,8 +342,8 @@ setInterval(() => { export default function App({counter}) { return ( <> - <h1>Hello, world! {counter}</h1> - <input placeholder="Type something here" /> + <h1>Xin chào, thế giới! {counter}</h1> + <input placeholder="Nhập gì đó vào đây" /> </> ); } @@ -351,11 +351,11 @@ export default function App({counter}) { </Sandpack> -It is uncommon to call `render` multiple times. Usually, your components will [update state](/reference/react/useState) instead. +Việc gọi `render` nhiều lần là không phổ biến. Thông thường, các thành phần của bạn sẽ [cập nhật state](/reference/react/useState) thay thế. -### Error logging in production {/*error-logging-in-production*/} +### Ghi nhật ký lỗi trong sản xuất {/*error-logging-in-production*/} -By default, React will log all errors to the console. To implement your own error reporting, you can provide the optional error handler root options `onUncaughtError`, `onCaughtError` and `onRecoverableError`: +Theo mặc định, React sẽ ghi tất cả các lỗi vào bảng điều khiển. Để triển khai báo cáo lỗi của riêng bạn, bạn có thể cung cấp các tùy chọn gốc trình xử lý lỗi tùy chọn `onUncaughtError`, `onCaughtError` và `onRecoverableError`: ```js [[1, 6, "onCaughtError"], [2, 6, "error", 1], [3, 6, "errorInfo"], [4, 10, "componentStack", 15]] import { createRoot } from "react-dom/client"; @@ -374,19 +374,19 @@ const root = createRoot(container, { }); ``` -The <CodeStep step={1}>onCaughtError</CodeStep> option is a function called with two arguments: +Tùy chọn <CodeStep step={1}>onCaughtError</CodeStep> là một hàm được gọi với hai đối số: -1. The <CodeStep step={2}>error</CodeStep> that was thrown. -2. An <CodeStep step={3}>errorInfo</CodeStep> object that contains the <CodeStep step={4}>componentStack</CodeStep> of the error. +1. <CodeStep step={2}>lỗi</CodeStep> đã được ném ra. +2. Một đối tượng <CodeStep step={3}>errorInfo</CodeStep> chứa <CodeStep step={4}>componentStack</CodeStep> của lỗi. -Together with `onUncaughtError` and `onRecoverableError`, you can can implement your own error reporting system: +Cùng với `onUncaughtError` và `onRecoverableError`, bạn có thể triển khai hệ thống báo cáo lỗi của riêng mình: <Sandpack> ```js src/reportError.js function reportError({ type, error, errorInfo }) { - // The specific implementation is up to you. - // `console.error()` is only used for demonstration purposes. + // Việc triển khai cụ thể là tùy thuộc vào bạn. + // `console.error()` chỉ được sử dụng cho mục đích trình diễn. console.error(type, error, "Component Stack: "); console.error("Component Stack: ", errorInfo.componentStack); } @@ -417,9 +417,9 @@ import { const container = document.getElementById("root"); const root = createRoot(container, { - // Keep in mind to remove these options in development to leverage - // React's default handlers or implement your own overlay for development. - // The handlers are only specfied unconditionally here for demonstration purposes. + // Hãy nhớ xóa các tùy chọn này trong quá trình phát triển để tận dụng + // các trình xử lý mặc định của React hoặc triển khai lớp phủ của riêng bạn để phát triển. + // Các trình xử lý chỉ được chỉ định vô điều kiện ở đây cho mục đích trình diễn. onCaughtError: onCaughtErrorProd, onRecoverableError: onRecoverableErrorProd, onUncaughtError: onUncaughtErrorProd, @@ -443,7 +443,7 @@ class ErrorBoundary extends Component { render() { if (this.state.hasError) { - return <h1>Something went wrong.</h1>; + return <h1>Đã xảy ra lỗi.</h1>; } return this.props.children; } @@ -456,11 +456,11 @@ export default function App() { return ( <> <button onClick={() => settriggerUncaughtError(true)}> - Trigger uncaught error + Kích hoạt lỗi không bị bắt </button> {triggerUncaughtError && <Boom />} <button onClick={() => setTriggerCaughtError(true)}> - Trigger caught error + Kích hoạt lỗi bị bắt </button> {triggerCaughtError && ( <ErrorBoundary> @@ -474,11 +474,11 @@ export default function App() { </Sandpack> -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I've created a root, but nothing is displayed {/*ive-created-a-root-but-nothing-is-displayed*/} +### Tôi đã tạo một gốc, nhưng không có gì được hiển thị {/*ive-created-a-root-but-nothing-is-displayed*/} -Make sure you haven't forgotten to actually *render* your app into the root: +Đảm bảo rằng bạn không quên thực sự *render* ứng dụng của mình vào gốc: ```js {5} import { createRoot } from 'react-dom/client'; @@ -488,37 +488,37 @@ const root = createRoot(document.getElementById('root')); root.render(<App />); ``` -Until you do that, nothing is displayed. +Cho đến khi bạn làm điều đó, không có gì được hiển thị. --- -### I'm getting an error: "You passed a second argument to root.render" {/*im-getting-an-error-you-passed-a-second-argument-to-root-render*/} +### Tôi gặp lỗi: "Bạn đã truyền một đối số thứ hai cho root.render" {/*im-getting-an-error-you-passed-a-second-argument-to-root-render*/} -A common mistake is to pass the options for `createRoot` to `root.render(...)`: +Một lỗi phổ biến là truyền các tùy chọn cho `createRoot` cho `root.render(...)`: <ConsoleBlock level="error"> -Warning: You passed a second argument to root.render(...) but it only accepts one argument. +Cảnh báo: Bạn đã truyền một đối số thứ hai cho root.render(...) nhưng nó chỉ chấp nhận một đối số. </ConsoleBlock> -To fix, pass the root options to `createRoot(...)`, not `root.render(...)`: +Để sửa lỗi, hãy truyền các tùy chọn gốc cho `createRoot(...)`, không phải `root.render(...)`: ```js {2,5} -// 🚩 Wrong: root.render only takes one argument. +// 🚩 Sai: root.render chỉ nhận một đối số. root.render(App, {onUncaughtError}); -// ✅ Correct: pass options to createRoot. +// ✅ Đúng: truyền các tùy chọn cho createRoot. const root = createRoot(container, {onUncaughtError}); root.render(<App />); ``` --- -### I'm getting an error: "Target container is not a DOM element" {/*im-getting-an-error-target-container-is-not-a-dom-element*/} +### Tôi gặp lỗi: "Target container is not a DOM element" {/*im-getting-an-error-target-container-is-not-a-dom-element*/} -This error means that whatever you're passing to `createRoot` is not a DOM node. +Lỗi này có nghĩa là bất cứ thứ gì bạn đang truyền cho `createRoot` không phải là một nút DOM. -If you're not sure what's happening, try logging it: +Nếu bạn không chắc chắn điều gì đang xảy ra, hãy thử ghi nhật ký nó: ```js {2} const domNode = document.getElementById('root'); @@ -527,46 +527,46 @@ const root = createRoot(domNode); root.render(<App />); ``` -For example, if `domNode` is `null`, it means that [`getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) returned `null`. This will happen if there is no node in the document with the given ID at the time of your call. There may be a few reasons for it: +Ví dụ: nếu `domNode` là `null`, điều đó có nghĩa là [`getElementById`](https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) trả về `null`. Điều này sẽ xảy ra nếu không có nút nào trong tài liệu có ID đã cho tại thời điểm bạn gọi. Có thể có một vài lý do cho việc này: -1. The ID you're looking for might differ from the ID you used in the HTML file. Check for typos! -2. Your bundle's `<script>` tag cannot "see" any DOM nodes that appear *after* it in the HTML. +1. ID bạn đang tìm kiếm có thể khác với ID bạn đã sử dụng trong tệp HTML. Kiểm tra lỗi chính tả! +2. Thẻ `<script>` của bundle của bạn không thể "nhìn thấy" bất kỳ nút DOM nào xuất hiện *sau* nó trong HTML. -Another common way to get this error is to write `createRoot(<App />)` instead of `createRoot(domNode)`. +Một cách phổ biến khác để gặp lỗi này là viết `createRoot(<App />)` thay vì `createRoot(domNode)`. --- -### I'm getting an error: "Functions are not valid as a React child." {/*im-getting-an-error-functions-are-not-valid-as-a-react-child*/} +### Tôi gặp lỗi: "Functions are not valid as a React child." {/*im-getting-an-error-functions-are-not-valid-as-a-react-child*/} -This error means that whatever you're passing to `root.render` is not a React component. +Lỗi này có nghĩa là bất cứ thứ gì bạn đang truyền cho `root.render` không phải là một thành phần React. -This may happen if you call `root.render` with `Component` instead of `<Component />`: +Điều này có thể xảy ra nếu bạn gọi `root.render` với `Component` thay vì `<Component />`: ```js {2,5} -// 🚩 Wrong: App is a function, not a Component. +// 🚩 Sai: App là một hàm, không phải một thành phần. root.render(App); -// ✅ Correct: <App /> is a component. +// ✅ Đúng: <App /> là một thành phần. root.render(<App />); ``` -Or if you pass a function to `root.render`, instead of the result of calling it: +Hoặc nếu bạn truyền một hàm cho `root.render`, thay vì kết quả của việc gọi nó: ```js {2,5} -// 🚩 Wrong: createApp is a function, not a component. +// 🚩 Sai: createApp là một hàm, không phải một thành phần. root.render(createApp); -// ✅ Correct: call createApp to return a component. +// ✅ Đúng: gọi createApp để trả về một thành phần. root.render(createApp()); ``` --- -### My server-rendered HTML gets re-created from scratch {/*my-server-rendered-html-gets-re-created-from-scratch*/} +### HTML được render phía máy chủ của tôi được tạo lại từ đầu {/*my-server-rendered-html-gets-re-created-from-scratch*/} -If your app is server-rendered and includes the initial HTML generated by React, you might notice that creating a root and calling `root.render` deletes all that HTML, and then re-creates all the DOM nodes from scratch. This can be slower, resets focus and scroll positions, and may lose other user input. +Nếu ứng dụng của bạn được render phía máy chủ và bao gồm HTML ban đầu được tạo bởi React, bạn có thể nhận thấy rằng việc tạo một gốc và gọi `root.render` sẽ xóa tất cả HTML đó và sau đó tạo lại tất cả các nút DOM từ đầu. Điều này có thể chậm hơn, đặt lại vị trí tiêu điểm và cuộn và có thể làm mất các đầu vào khác của người dùng. -Server-rendered apps must use [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) instead of `createRoot`: +Các ứng dụng được render phía máy chủ phải sử dụng [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) thay vì `createRoot`: ```js {1,4-7} import { hydrateRoot } from 'react-dom/client'; @@ -578,4 +578,4 @@ hydrateRoot( ); ``` -Note that its API is different. In particular, usually there will be no further `root.render` call. +Lưu ý rằng API của nó khác. Đặc biệt, thường sẽ không có lệnh gọi `root.render` nào nữa. diff --git a/src/content/reference/react-dom/client/index.md b/src/content/reference/react-dom/client/index.md index 89f48212f..92d176d6f 100644 --- a/src/content/reference/react-dom/client/index.md +++ b/src/content/reference/react-dom/client/index.md @@ -1,22 +1,22 @@ --- -title: Client React DOM APIs +title: Các API React DOM Client --- <Intro> -The `react-dom/client` APIs let you render React components on the client (in the browser). These APIs are typically used at the top level of your app to initialize your React tree. A [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) may call them for you. Most of your components don't need to import or use them. +Các API `react-dom/client` cho phép bạn hiển thị các thành phần React trên client (trong trình duyệt). Các API này thường được sử dụng ở cấp cao nhất của ứng dụng để khởi tạo cây React của bạn. Một [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) có thể gọi chúng cho bạn. Hầu hết các thành phần của bạn không cần nhập hoặc sử dụng chúng. </Intro> --- -## Client APIs {/*client-apis*/} +## Các API Client {/*client-apis*/} -* [`createRoot`](/reference/react-dom/client/createRoot) lets you create a root to display React components inside a browser DOM node. -* [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) lets you display React components inside a browser DOM node whose HTML content was previously generated by [`react-dom/server`.](/reference/react-dom/server) +* [`createRoot`](/reference/react-dom/client/createRoot) cho phép bạn tạo một root để hiển thị các thành phần React bên trong một nút DOM của trình duyệt. +* [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) cho phép bạn hiển thị các thành phần React bên trong một nút DOM của trình duyệt có nội dung HTML đã được tạo trước đó bởi [`react-dom/server`.](/reference/react-dom/server) --- -## Browser support {/*browser-support*/} +## Hỗ trợ trình duyệt {/*browser-support*/} -React supports all popular browsers, including Internet Explorer 9 and above. Some polyfills are required for older browsers such as IE 9 and IE 10. \ No newline at end of file +React hỗ trợ tất cả các trình duyệt phổ biến, bao gồm Internet Explorer 9 trở lên. Một số polyfill là bắt buộc đối với các trình duyệt cũ hơn như IE 9 và IE 10. \ No newline at end of file diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index 9d1533213..6f2618898 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -4,7 +4,7 @@ title: "Common components (e.g. <div>)" <Intro> -All built-in browser components, such as [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div), support some common props and events. +Tất cả các thành phần trình duyệt tích hợp sẵn, chẳng hạn như [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div), đều hỗ trợ một số props và sự kiện chung. </Intro> @@ -12,332 +12,331 @@ All built-in browser components, such as [`<div>`](https://developer.mozilla.org --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} -### Common components (e.g. `<div>`) {/*common*/} +### Các thành phần chung (ví dụ: `<div>`) {/*common*/} ```js -<div className="wrapper">Some content</div> +<div className="wrapper">Một số nội dung</div> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*common-props*/} -These special React props are supported for all built-in components: - -* `children`: A React node (an element, a string, a number, [a portal,](/reference/react-dom/createPortal) an empty node like `null`, `undefined` and booleans, or an array of other React nodes). Specifies the content inside the component. When you use JSX, you will usually specify the `children` prop implicitly by nesting tags like `<div><span /></div>`. - -* `dangerouslySetInnerHTML`: An object of the form `{ __html: '<p>some html</p>' }` with a raw HTML string inside. Overrides the [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) property of the DOM node and displays the passed HTML inside. This should be used with extreme caution! If the HTML inside isn't trusted (for example, if it's based on user data), you risk introducing an [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) vulnerability. [Read more about using `dangerouslySetInnerHTML`.](#dangerously-setting-the-inner-html) - -* `ref`: A ref object from [`useRef`](/reference/react/useRef) or [`createRef`](/reference/react/createRef), or a [`ref` callback function,](#ref-callback) or a string for [legacy refs.](https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs) Your ref will be filled with the DOM element for this node. [Read more about manipulating the DOM with refs.](#manipulating-a-dom-node-with-a-ref) - -* `suppressContentEditableWarning`: A boolean. If `true`, suppresses the warning that React shows for elements that both have `children` and `contentEditable={true}` (which normally do not work together). Use this if you're building a text input library that manages the `contentEditable` content manually. - -* `suppressHydrationWarning`: A boolean. If you use [server rendering,](/reference/react-dom/server) normally there is a warning when the server and the client render different content. In some rare cases (like timestamps), it is very hard or impossible to guarantee an exact match. If you set `suppressHydrationWarning` to `true`, React will not warn you about mismatches in the attributes and the content of that element. It only works one level deep, and is intended to be used as an escape hatch. Don't overuse it. [Read about suppressing hydration errors.](/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors) - -* `style`: An object with CSS styles, for example `{ fontWeight: 'bold', margin: 20 }`. Similarly to the DOM [`style`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) property, the CSS property names need to be written as `camelCase`, for example `fontWeight` instead of `font-weight`. You can pass strings or numbers as values. If you pass a number, like `width: 100`, React will automatically append `px` ("pixels") to the value unless it's a [unitless property.](https://github.com/facebook/react/blob/81d4ee9ca5c405dce62f64e61506b8e155f38d8d/packages/react-dom-bindings/src/shared/CSSProperty.js#L8-L57) We recommend using `style` only for dynamic styles where you don't know the style values ahead of time. In other cases, applying plain CSS classes with `className` is more efficient. [Read more about `className` and `style`.](#applying-css-styles) - -These standard DOM props are also supported for all built-in components: - -* [`accessKey`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey): A string. Specifies a keyboard shortcut for the element. [Not generally recommended.](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey#accessibility_concerns) -* [`aria-*`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes): ARIA attributes let you specify the accessibility tree information for this element. See [ARIA attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes) for a complete reference. In React, all ARIA attribute names are exactly the same as in HTML. -* [`autoCapitalize`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize): A string. Specifies whether and how the user input should be capitalized. -* [`className`](https://developer.mozilla.org/en-US/docs/Web/API/Element/className): A string. Specifies the element's CSS class name. [Read more about applying CSS styles.](#applying-css-styles) -* [`contentEditable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable): A boolean. If `true`, the browser lets the user edit the rendered element directly. This is used to implement rich text input libraries like [Lexical.](https://lexical.dev/) React warns if you try to pass React children to an element with `contentEditable={true}` because React will not be able to update its content after user edits. -* [`data-*`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*): Data attributes let you attach some string data to the element, for example `data-fruit="banana"`. In React, they are not commonly used because you would usually read data from props or state instead. -* [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir): Either `'ltr'` or `'rtl'`. Specifies the text direction of the element. -* [`draggable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable): A boolean. Specifies whether the element is draggable. Part of [HTML Drag and Drop API.](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) -* [`enterKeyHint`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/enterKeyHint): A string. Specifies which action to present for the enter key on virtual keyboards. -* [`htmlFor`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor): A string. For [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) and [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output), lets you [associate the label with some control.](/reference/react-dom/components/input#providing-a-label-for-an-input) Same as [`for` HTML attribute.](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/for) React uses the standard DOM property names (`htmlFor`) instead of HTML attribute names. -* [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden): A boolean or a string. Specifies whether the element should be hidden. -* [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id): A string. Specifies a unique identifier for this element, which can be used to find it later or connect it with other elements. Generate it with [`useId`](/reference/react/useId) to avoid clashes between multiple instances of the same component. -* [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is): A string. If specified, the component will behave like a [custom element.](/reference/react-dom/components#custom-html-elements) -* [`inputMode`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode): A string. Specifies what kind of keyboard to display (for example, text, number or telephone). -* [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop): A string. Specifies which property the element represents for structured data crawlers. -* [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang): A string. Specifies the language of the element. -* [`onAnimationEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationend_event): An [`AnimationEvent` handler](#animationevent-handler) function. Fires when a CSS animation completes. -* `onAnimationEndCapture`: A version of `onAnimationEnd` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onAnimationIteration`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationiteration_event): An [`AnimationEvent` handler](#animationevent-handler) function. Fires when an iteration of a CSS animation ends, and another one begins. -* `onAnimationIterationCapture`: A version of `onAnimationIteration` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onAnimationStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationstart_event): An [`AnimationEvent` handler](#animationevent-handler) function. Fires when a CSS animation starts. -* `onAnimationStartCapture`: `onAnimationStart`, but fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onAuxClick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when a non-primary pointer button was clicked. -* `onAuxClickCapture`: A version of `onAuxClick` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* `onBeforeInput`: An [`InputEvent` handler](#inputevent-handler) function. Fires before the value of an editable element is modified. React does *not* yet use the native [`beforeinput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event) event, and instead attempts to polyfill it using other events. -* `onBeforeInputCapture`: A version of `onBeforeInput` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* `onBlur`: A [`FocusEvent` handler](#focusevent-handler) function. Fires when an element lost focus. Unlike the built-in browser [`blur`](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) event, in React the `onBlur` event bubbles. -* `onBlurCapture`: A version of `onBlur` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onClick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the primary button was clicked on the pointing device. -* `onClickCapture`: A version of `onClick` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCompositionStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionstart_event): A [`CompositionEvent` handler](#compositionevent-handler) function. Fires when an [input method editor](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) starts a new composition session. -* `onCompositionStartCapture`: A version of `onCompositionStart` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCompositionEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionend_event): A [`CompositionEvent` handler](#compositionevent-handler) function. Fires when an [input method editor](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) completes or cancels a composition session. -* `onCompositionEndCapture`: A version of `onCompositionEnd` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCompositionUpdate`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionupdate_event): A [`CompositionEvent` handler](#compositionevent-handler) function. Fires when an [input method editor](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) receives a new character. -* `onCompositionUpdateCapture`: A version of `onCompositionUpdate` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onContextMenu`](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the user tries to open a context menu. -* `onContextMenuCapture`: A version of `onContextMenu` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCopy`](https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event): A [`ClipboardEvent` handler](#clipboardevent-handler) function. Fires when the user tries to copy something into the clipboard. -* `onCopyCapture`: A version of `onCopy` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/cut_event): A [`ClipboardEvent` handler](#clipboardevent-handler) function. Fires when the user tries to cut something into the clipboard. -* `onCutCapture`: A version of `onCut` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* `onDoubleClick`: A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the user clicks twice. Corresponds to the browser [`dblclick` event.](https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event) -* `onDoubleClickCapture`: A version of `onDoubleClick` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDrag`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drag_event): A [`DragEvent` handler](#dragevent-handler) function. Fires while the user is dragging something. -* `onDragCapture`: A version of `onDrag` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDragEnd`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragend_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the user stops dragging something. -* `onDragEndCapture`: A version of `onDragEnd` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDragEnter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the dragged content enters a valid drop target. -* `onDragEnterCapture`: A version of `onDragEnter` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDragOver`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event): A [`DragEvent` handler](#dragevent-handler) function. Fires on a valid drop target while the dragged content is dragged over it. You must call `e.preventDefault()` here to allow dropping. -* `onDragOverCapture`: A version of `onDragOver` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDragStart`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragstart_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when the user starts dragging an element. -* `onDragStartCapture`: A version of `onDragStart` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDrop`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event): A [`DragEvent` handler](#dragevent-handler) function. Fires when something is dropped on a valid drop target. -* `onDropCapture`: A version of `onDrop` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* `onFocus`: A [`FocusEvent` handler](#focusevent-handler) function. Fires when an element receives focus. Unlike the built-in browser [`focus`](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) event, in React the `onFocus` event bubbles. -* `onFocusCapture`: A version of `onFocus` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onGotPointerCapture`](https://developer.mozilla.org/en-US/docs/Web/API/Element/gotpointercapture_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when an element programmatically captures a pointer. -* `onGotPointerCaptureCapture`: A version of `onGotPointerCapture` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onKeyDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event): A [`KeyboardEvent` handler](#keyboardevent-handler) function. Fires when a key is pressed. -* `onKeyDownCapture`: A version of `onKeyDown` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onKeyPress`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event): A [`KeyboardEvent` handler](#keyboardevent-handler) function. Deprecated. Use `onKeyDown` or `onBeforeInput` instead. -* `onKeyPressCapture`: A version of `onKeyPress` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onKeyUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keyup_event): A [`KeyboardEvent` handler](#keyboardevent-handler) function. Fires when a key is released. -* `onKeyUpCapture`: A version of `onKeyUp` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onLostPointerCapture`](https://developer.mozilla.org/en-US/docs/Web/API/Element/lostpointercapture_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when an element stops capturing a pointer. -* `onLostPointerCaptureCapture`: A version of `onLostPointerCapture` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onMouseDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the pointer is pressed down. -* `onMouseDownCapture`: A version of `onMouseDown` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onMouseEnter`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the pointer moves inside an element. Does not have a capture phase. Instead, `onMouseLeave` and `onMouseEnter` propagate from the element being left to the one being entered. -* [`onMouseLeave`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the pointer moves outside an element. Does not have a capture phase. Instead, `onMouseLeave` and `onMouseEnter` propagate from the element being left to the one being entered. -* [`onMouseMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the pointer changes coordinates. -* `onMouseMoveCapture`: A version of `onMouseMove` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onMouseOut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the pointer moves outside an element, or if it moves into a child element. -* `onMouseOutCapture`: A version of `onMouseOut` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onMouseUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event): A [`MouseEvent` handler](#mouseevent-handler) function. Fires when the pointer is released. -* `onMouseUpCapture`: A version of `onMouseUp` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPointerCancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointercancel_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when the browser cancels a pointer interaction. -* `onPointerCancelCapture`: A version of `onPointerCancel` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPointerDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerdown_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when a pointer becomes active. -* `onPointerDownCapture`: A version of `onPointerDown` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPointerEnter`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerenter_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when a pointer moves inside an element. Does not have a capture phase. Instead, `onPointerLeave` and `onPointerEnter` propagate from the element being left to the one being entered. -* [`onPointerLeave`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerleave_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when a pointer moves outside an element. Does not have a capture phase. Instead, `onPointerLeave` and `onPointerEnter` propagate from the element being left to the one being entered. -* [`onPointerMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when a pointer changes coordinates. -* `onPointerMoveCapture`: A version of `onPointerMove` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPointerOut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerout_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when a pointer moves outside an element, if the pointer interaction is cancelled, and [a few other reasons.](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerout_event) -* `onPointerOutCapture`: A version of `onPointerOut` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPointerUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerup_event): A [`PointerEvent` handler](#pointerevent-handler) function. Fires when a pointer is no longer active. -* `onPointerUpCapture`: A version of `onPointerUp` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPaste`](https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event): A [`ClipboardEvent` handler](#clipboardevent-handler) function. Fires when the user tries to paste something from the clipboard. -* `onPasteCapture`: A version of `onPaste` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onScroll`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll_event): An [`Event` handler](#event-handler) function. Fires when an element has been scrolled. This event does not bubble. -* `onScrollCapture`: A version of `onScroll` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select_event): An [`Event` handler](#event-handler) function. Fires after the selection inside an editable element like an input changes. React extends the `onSelect` event to work for `contentEditable={true}` elements as well. In addition, React extends it to fire for empty selection and on edits (which may affect the selection). -* `onSelectCapture`: A version of `onSelect` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onTouchCancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchcancel_event): A [`TouchEvent` handler](#touchevent-handler) function. Fires when the browser cancels a touch interaction. -* `onTouchCancelCapture`: A version of `onTouchCancel` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onTouchEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchend_event): A [`TouchEvent` handler](#touchevent-handler) function. Fires when one or more touch points are removed. -* `onTouchEndCapture`: A version of `onTouchEnd` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onTouchMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchmove_event): A [`TouchEvent` handler](#touchevent-handler) function. Fires one or more touch points are moved. -* `onTouchMoveCapture`: A version of `onTouchMove` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onTouchStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event): A [`TouchEvent` handler](#touchevent-handler) function. Fires when one or more touch points are placed. -* `onTouchStartCapture`: A version of `onTouchStart` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onTransitionEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/transitionend_event): A [`TransitionEvent` handler](#transitionevent-handler) function. Fires when a CSS transition completes. -* `onTransitionEndCapture`: A version of `onTransitionEnd` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onWheel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event): A [`WheelEvent` handler](#wheelevent-handler) function. Fires when the user rotates a wheel button. -* `onWheelCapture`: A version of `onWheel` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`role`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles): A string. Specifies the element role explicitly for assistive technologies. -* [`slot`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles): A string. Specifies the slot name when using shadow DOM. In React, an equivalent pattern is typically achieved by passing JSX as props, for example `<Layout left={<Sidebar />} right={<Content />} />`. -* [`spellCheck`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck): A boolean or null. If explicitly set to `true` or `false`, enables or disables spellchecking. -* [`tabIndex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex): A number. Overrides the default Tab button behavior. [Avoid using values other than `-1` and `0`.](https://www.tpgi.com/using-the-tabindex-attribute/) -* [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title): A string. Specifies the tooltip text for the element. -* [`translate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/translate): Either `'yes'` or `'no'`. Passing `'no'` excludes the element content from being translated. - -You can also pass custom attributes as props, for example `mycustomprop="someValue"`. This can be useful when integrating with third-party libraries. The custom attribute name must be lowercase and must not start with `on`. The value will be converted to a string. If you pass `null` or `undefined`, the custom attribute will be removed. - -These events fire only for the [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) elements: - -* [`onReset`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset_event): An [`Event` handler](#event-handler) function. Fires when a form gets reset. -* `onResetCapture`: A version of `onReset` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSubmit`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event): An [`Event` handler](#event-handler) function. Fires when a form gets submitted. -* `onSubmitCapture`: A version of `onSubmit` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) - -These events fire only for the [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) elements. Unlike browser events, they bubble in React: - -* [`onCancel`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/cancel_event): An [`Event` handler](#event-handler) function. Fires when the user tries to dismiss the dialog. -* `onCancelCapture`: A version of `onCancel` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onClose`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close_event): An [`Event` handler](#event-handler) function. Fires when a dialog has been closed. -* `onCloseCapture`: A version of `onClose` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) - -These events fire only for the [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) elements. Unlike browser events, they bubble in React: - -* [`onToggle`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDetailsElement/toggle_event): An [`Event` handler](#event-handler) function. Fires when the user toggles the details. -* `onToggleCapture`: A version of `onToggle` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) - -These events fire for [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img), [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe), [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object), [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed), [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link), and [SVG `<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_Image_Tag) elements. Unlike browser events, they bubble in React: - -* `onLoad`: An [`Event` handler](#event-handler) function. Fires when the resource has loaded. -* `onLoadCapture`: A version of `onLoad` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onError`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event): An [`Event` handler](#event-handler) function. Fires when the resource could not be loaded. -* `onErrorCapture`: A version of `onError` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) - -These events fire for resources like [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio) and [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video). Unlike browser events, they bubble in React: - -* [`onAbort`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/abort_event): An [`Event` handler](#event-handler) function. Fires when the resource has not fully loaded, but not due to an error. -* `onAbortCapture`: A version of `onAbort` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCanPlay`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event): An [`Event` handler](#event-handler) function. Fires when there's enough data to start playing, but not enough to play to the end without buffering. -* `onCanPlayCapture`: A version of `onCanPlay` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onCanPlayThrough`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplaythrough_event): An [`Event` handler](#event-handler) function. Fires when there's enough data that it's likely possible to start playing without buffering until the end. -* `onCanPlayThroughCapture`: A version of `onCanPlayThrough` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onDurationChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/durationchange_event): An [`Event` handler](#event-handler) function. Fires when the media duration has updated. -* `onDurationChangeCapture`: A version of `onDurationChange` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onEmptied`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event): An [`Event` handler](#event-handler) function. Fires when the media has become empty. -* `onEmptiedCapture`: A version of `onEmptied` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onEncrypted`](https://w3c.github.io/encrypted-media/#dom-evt-encrypted): An [`Event` handler](#event-handler) function. Fires when the browser encounters encrypted media. -* `onEncryptedCapture`: A version of `onEncrypted` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onEnded`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event): An [`Event` handler](#event-handler) function. Fires when the playback stops because there's nothing left to play. -* `onEndedCapture`: A version of `onEnded` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onError`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event): An [`Event` handler](#event-handler) function. Fires when the resource could not be loaded. -* `onErrorCapture`: A version of `onError` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onLoadedData`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event): An [`Event` handler](#event-handler) function. Fires when the current playback frame has loaded. -* `onLoadedDataCapture`: A version of `onLoadedData` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onLoadedMetadata`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event): An [`Event` handler](#event-handler) function. Fires when metadata has loaded. -* `onLoadedMetadataCapture`: A version of `onLoadedMetadata` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onLoadStart`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadstart_event): An [`Event` handler](#event-handler) function. Fires when the browser started loading the resource. -* `onLoadStartCapture`: A version of `onLoadStart` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPause`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause_event): An [`Event` handler](#event-handler) function. Fires when the media was paused. -* `onPauseCapture`: A version of `onPause` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPlay`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play_event): An [`Event` handler](#event-handler) function. Fires when the media is no longer paused. -* `onPlayCapture`: A version of `onPlay` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onPlaying`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playing_event): An [`Event` handler](#event-handler) function. Fires when the media starts or restarts playing. -* `onPlayingCapture`: A version of `onPlaying` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onProgress`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/progress_event): An [`Event` handler](#event-handler) function. Fires periodically while the resource is loading. -* `onProgressCapture`: A version of `onProgress` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onRateChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ratechange_event): An [`Event` handler](#event-handler) function. Fires when playback rate changes. -* `onRateChangeCapture`: A version of `onRateChange` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* `onResize`: An [`Event` handler](#event-handler) function. Fires when video changes size. -* `onResizeCapture`: A version of `onResize` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSeeked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeked_event): An [`Event` handler](#event-handler) function. Fires when a seek operation completes. -* `onSeekedCapture`: A version of `onSeeked` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSeeking`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event): An [`Event` handler](#event-handler) function. Fires when a seek operation starts. -* `onSeekingCapture`: A version of `onSeeking` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onStalled`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/stalled_event): An [`Event` handler](#event-handler) function. Fires when the browser is waiting for data but it keeps not loading. -* `onStalledCapture`: A version of `onStalled` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSuspend`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/suspend_event): An [`Event` handler](#event-handler) function. Fires when loading the resource was suspended. -* `onSuspendCapture`: A version of `onSuspend` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onTimeUpdate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/timeupdate_event): An [`Event` handler](#event-handler) function. Fires when the current playback time updates. -* `onTimeUpdateCapture`: A version of `onTimeUpdate` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onVolumeChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volumechange_event): An [`Event` handler](#event-handler) function. Fires when the volume has changed. -* `onVolumeChangeCapture`: A version of `onVolumeChange` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onWaiting`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/waiting_event): An [`Event` handler](#event-handler) function. Fires when the playback stopped due to temporary lack of data. -* `onWaitingCapture`: A version of `onWaiting` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) - -#### Caveats {/*common-caveats*/} - -- You cannot pass both `children` and `dangerouslySetInnerHTML` at the same time. -- Some events (like `onAbort` and `onLoad`) don't bubble in the browser, but bubble in React. +Các React props đặc biệt này được hỗ trợ cho tất cả các thành phần tích hợp sẵn: + +* `children`: Một React node (một phần tử, một chuỗi, một số, [một portal,](/reference/react-dom/createPortal) một node trống như `null`, `undefined` và boolean, hoặc một mảng các React node khác). Chỉ định nội dung bên trong thành phần. Khi bạn sử dụng JSX, bạn thường chỉ định prop `children` một cách ngầm định bằng cách lồng các thẻ như `<div><span /></div>`. + +* `dangerouslySetInnerHTML`: Một đối tượng có dạng `{ __html: '<p>some html</p>' }` với một chuỗi HTML thô bên trong. Ghi đè thuộc tính [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) của DOM node và hiển thị HTML đã truyền bên trong. Điều này nên được sử dụng hết sức thận trọng! Nếu HTML bên trong không đáng tin cậy (ví dụ: nếu nó dựa trên dữ liệu người dùng), bạn có nguy cơ đưa vào lỗ hổng [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting). [Đọc thêm về cách sử dụng `dangerouslySetInnerHTML`.](#dangerously-setting-the-inner-html) + +* `ref`: Một đối tượng ref từ [`useRef`](/reference/react/useRef) hoặc [`createRef`](/reference/react/createRef), hoặc một hàm callback [`ref`,](#ref-callback) hoặc một chuỗi cho [legacy refs.](https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs) Ref của bạn sẽ được điền vào DOM element cho node này. [Đọc thêm về thao tác DOM với ref.](#manipulating-a-dom-node-with-a-ref) + +* `suppressContentEditableWarning`: Một boolean. Nếu `true`, sẽ ngăn chặn cảnh báo mà React hiển thị cho các phần tử vừa có `children` vừa có `contentEditable={true}` (thường không hoạt động cùng nhau). Sử dụng điều này nếu bạn đang xây dựng một thư viện nhập văn bản quản lý nội dung `contentEditable` theo cách thủ công. + +* `suppressHydrationWarning`: Một boolean. Nếu bạn sử dụng [server rendering,](/reference/react-dom/server) thông thường sẽ có một cảnh báo khi server và client hiển thị nội dung khác nhau. Trong một số trường hợp hiếm hoi (như dấu thời gian), rất khó hoặc không thể đảm bảo sự khớp chính xác. Nếu bạn đặt `suppressHydrationWarning` thành `true`, React sẽ không cảnh báo bạn về sự không khớp trong các thuộc tính và nội dung của phần tử đó. Nó chỉ hoạt động ở một cấp độ sâu và được dự định sử dụng như một lối thoát hiểm. Không nên lạm dụng nó. [Đọc về cách ngăn chặn các lỗi không khớp hydration không thể tránh khỏi.](/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors) + +* `style`: Một đối tượng với các kiểu CSS, ví dụ: `{ fontWeight: 'bold', margin: 20 }`. Tương tự như thuộc tính [`style`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) của DOM, tên thuộc tính CSS cần được viết dưới dạng `camelCase`, ví dụ: `fontWeight` thay vì `font-weight`. Bạn có thể truyền chuỗi hoặc số làm giá trị. Nếu bạn truyền một số, như `width: 100`, React sẽ tự động thêm `px` ("pixel") vào giá trị trừ khi đó là một [unitless property.](https://github.com/facebook/react/blob/81d4ee9ca5c405dce62f64e61506b8e155f38d8d/packages/react-dom-bindings/src/shared/CSSProperty.js#L8-L57) Chúng tôi khuyên bạn chỉ nên sử dụng `style` cho các kiểu động, nơi bạn không biết trước các giá trị kiểu. Trong các trường hợp khác, việc áp dụng các lớp CSS đơn giản với `className` sẽ hiệu quả hơn. [Đọc thêm về `className` và `style`.](#applying-css-styles) + +Các DOM props tiêu chuẩn này cũng được hỗ trợ cho tất cả các thành phần tích hợp sẵn: + +* [`accessKey`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey): Một chuỗi. Chỉ định một phím tắt cho phần tử. [Không được khuyến nghị chung.](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey#accessibility_concerns) +* [`aria-*`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes): Các thuộc tính ARIA cho phép bạn chỉ định thông tin cây trợ năng cho phần tử này. Xem [ARIA attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes) để có tài liệu tham khảo đầy đủ. Trong React, tất cả các tên thuộc tính ARIA đều giống hệt như trong HTML. +* [`autoCapitalize`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize): Một chuỗi. Chỉ định xem và cách nhập liệu của người dùng nên được viết hoa. +* [`className`](https://developer.mozilla.org/en-US/docs/Web/API/Element/className): Một chuỗi. Chỉ định tên lớp CSS của phần tử. [Đọc thêm về cách áp dụng các kiểu CSS.](#applying-css-styles) +* [`contentEditable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable): Một boolean. Nếu `true`, trình duyệt cho phép người dùng chỉnh sửa trực tiếp phần tử được hiển thị. Điều này được sử dụng để triển khai các thư viện nhập văn bản đa dạng thức như [Lexical.](https://lexical.dev/) React cảnh báo nếu bạn cố gắng truyền React children cho một phần tử có `contentEditable={true}` vì React sẽ không thể cập nhật nội dung của nó sau khi người dùng chỉnh sửa. +* [`data-*`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*): Các thuộc tính dữ liệu cho phép bạn đính kèm một số dữ liệu chuỗi vào phần tử, ví dụ: `data-fruit="banana"`. Trong React, chúng không được sử dụng phổ biến vì bạn thường sẽ đọc dữ liệu từ props hoặc state thay thế. +* [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir): Hoặc `'ltr'` hoặc `'rtl'`. Chỉ định hướng văn bản của phần tử. +* [`draggable`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable): Một boolean. Chỉ định xem phần tử có thể kéo được hay không. Một phần của [HTML Drag and Drop API.](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) +* [`enterKeyHint`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/enterKeyHint): Một chuỗi. Chỉ định hành động nào sẽ hiển thị cho phím enter trên bàn phím ảo. +* [`htmlFor`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor): Một chuỗi. Đối với [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) và [`<output>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output), cho phép bạn [liên kết nhãn với một số điều khiển.](/reference/react-dom/components/input#providing-a-label-for-an-input) Tương tự như thuộc tính HTML [`for`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/for). React sử dụng tên thuộc tính DOM tiêu chuẩn (`htmlFor`) thay vì tên thuộc tính HTML. +* [`hidden`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden): Một boolean hoặc một chuỗi. Chỉ định xem phần tử có nên bị ẩn hay không. +* [`id`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id): Một chuỗi. Chỉ định một mã định danh duy nhất cho phần tử này, có thể được sử dụng để tìm nó sau này hoặc kết nối nó với các phần tử khác. Tạo nó bằng [`useId`](/reference/react/useId) để tránh xung đột giữa nhiều phiên bản của cùng một thành phần. +* [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is): Một chuỗi. Nếu được chỉ định, thành phần sẽ hoạt động như một [custom element.](/reference/react-dom/components#custom-html-elements) +* [`inputMode`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode): Một chuỗi. Chỉ định loại bàn phím nào sẽ hiển thị (ví dụ: văn bản, số hoặc điện thoại). +* [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop): Một chuỗi. Chỉ định thuộc tính nào phần tử đại diện cho trình thu thập dữ liệu có cấu trúc. +* [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang): Một chuỗi. Chỉ định ngôn ngữ của phần tử. +* [`onAnimationEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationend_event): Một hàm xử lý [`AnimationEvent`](#animationevent-handler). Kích hoạt khi một CSS animation hoàn thành. +* `onAnimationEndCapture`: Một phiên bản của `onAnimationEnd` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onAnimationIteration`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationiteration_event): Một hàm xử lý [`AnimationEvent`](#animationevent-handler). Kích hoạt khi một vòng lặp của CSS animation kết thúc và một vòng lặp khác bắt đầu. +* `onAnimationIterationCapture`: Một phiên bản của `onAnimationIteration` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onAnimationStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/animationstart_event): Một hàm xử lý [`AnimationEvent`](#animationevent-handler). Kích hoạt khi một CSS animation bắt đầu. +* `onAnimationStartCapture`: `onAnimationStart`, nhưng kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onAuxClick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi một nút con trỏ không phải là nút chính được nhấp. +* `onAuxClickCapture`: Một phiên bản của `onAuxClick` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* `onBeforeInput`: Một hàm xử lý [`InputEvent`](#inputevent-handler). Kích hoạt trước khi giá trị của một phần tử có thể chỉnh sửa được sửa đổi. React *chưa* sử dụng sự kiện [`beforeinput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event) gốc và thay vào đó cố gắng polyfill nó bằng các sự kiện khác. +* `onBeforeInputCapture`: Một phiên bản của `onBeforeInput` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* `onBlur`: Một hàm xử lý [`FocusEvent`](#focusevent-handler). Kích hoạt khi một phần tử mất focus. Không giống như sự kiện [`blur`](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) của trình duyệt tích hợp, trong React, sự kiện `onBlur` nổi lên. +* `onBlurCapture`: Một phiên bản của `onBlur` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onClick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi nút chính được nhấp trên thiết bị trỏ. +* `onClickCapture`: Một phiên bản của `onClick` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCompositionStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionstart_event): Một hàm xử lý [`CompositionEvent`](#compositionevent-handler). Kích hoạt khi một [input method editor](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) bắt đầu một phiên soạn thảo mới. +* `onCompositionStartCapture`: Một phiên bản của `onCompositionStart` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCompositionEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionend_event): Một hàm xử lý [`CompositionEvent`](#compositionevent-handler). Kích hoạt khi một [input method editor](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) hoàn thành hoặc hủy một phiên soạn thảo. +* `onCompositionEndCapture`: Một phiên bản của `onCompositionEnd` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCompositionUpdate`](https://developer.mozilla.org/en-US/docs/Web/API/Element/compositionupdate_event): Một hàm xử lý [`CompositionEvent`](#compositionevent-handler). Kích hoạt khi một [input method editor](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) nhận được một ký tự mới. +* `onCompositionUpdateCapture`: Một phiên bản của `onCompositionUpdate` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onContextMenu`](https://developer.mozilla.org/en-US/docs/Web/API/Element/contextmenu_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi người dùng cố gắng mở một context menu. +* `onContextMenuCapture`: Một phiên bản của `onContextMenu` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCopy`](https://developer.mozilla.org/en-US/docs/Web/API/Element/copy_event): Một hàm xử lý [`ClipboardEvent`](#clipboardevent-handler). Kích hoạt khi người dùng cố gắng sao chép một cái gì đó vào clipboard. +* `onCopyCapture`: Một phiên bản của `onCopy` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/cut_event): Một hàm xử lý [`ClipboardEvent`](#clipboardevent-handler). Kích hoạt khi người dùng cố gắng cắt một cái gì đó vào clipboard. +* `onCutCapture`: Một phiên bản của `onCut` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* `onDoubleClick`: Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi người dùng nhấp đúp. Tương ứng với sự kiện [`dblclick` của trình duyệt.](https://developer.mozilla.org/en-US/docs/Web/API/Element/dblclick_event) +* `onDoubleClickCapture`: Một phiên bản của `onDoubleClick` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDrag`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drag_event): Một hàm xử lý [`DragEvent`](#dragevent-handler). Kích hoạt khi người dùng đang kéo một cái gì đó. +* `onDragCapture`: Một phiên bản của `onDrag` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDragEnd`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragend_event): Một hàm xử lý [`DragEvent`](#dragevent-handler). Kích hoạt khi người dùng ngừng kéo một cái gì đó. +* `onDragEndCapture`: Một phiên bản của `onDragEnd` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDragEnter`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragenter_event): Một hàm xử lý [`DragEvent`](#dragevent-handler). Kích hoạt khi nội dung được kéo vào một mục tiêu thả hợp lệ. +* `onDragEnterCapture`: Một phiên bản của `onDragEnter` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDragOver`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragover_event): Một hàm xử lý [`DragEvent`](#dragevent-handler). Kích hoạt trên một mục tiêu thả hợp lệ trong khi nội dung được kéo đang được kéo qua nó. Bạn phải gọi `e.preventDefault()` ở đây để cho phép thả. +* `onDragOverCapture`: Một phiên bản của `onDragOver` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDragStart`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dragstart_event): Một hàm xử lý [`DragEvent`](#dragevent-handler). Kích hoạt khi người dùng bắt đầu kéo một phần tử. +* `onDragStartCapture`: Một phiên bản của `onDragStart` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDrop`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/drop_event): Một hàm xử lý [`DragEvent`](#dragevent-handler). Kích hoạt khi một cái gì đó được thả trên một mục tiêu thả hợp lệ. +* `onDropCapture`: Một phiên bản của `onDrop` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* `onFocus`: Một hàm xử lý [`FocusEvent`](#focusevent-handler). Kích hoạt khi một phần tử nhận được focus. Không giống như sự kiện [`focus`](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) của trình duyệt tích hợp, trong React, sự kiện `onFocus` nổi lên. +* `onFocusCapture`: Một phiên bản của `onFocus` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onGotPointerCapture`](https://developer.mozilla.org/en-US/docs/Web/API/Element/gotpointercapture_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một phần tử chụp một con trỏ theo chương trình. +* `onGotPointerCaptureCapture`: Một phiên bản của `onGotPointerCapture` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onKeyDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event): Một hàm xử lý [`KeyboardEvent`](#keyboardevent-handler). Kích hoạt khi một phím được nhấn. +* `onKeyDownCapture`: Một phiên bản của `onKeyDown` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onKeyPress`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event): Một hàm xử lý [`KeyboardEvent`](#keyboardevent-handler). Đã không còn được dùng. Sử dụng `onKeyDown` hoặc `onBeforeInput` thay thế. +* `onKeyPressCapture`: Một phiên bản của `onKeyPress` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onKeyUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/keyup_event): Một hàm xử lý [`KeyboardEvent`](#keyboardevent-handler). Kích hoạt khi một phím được nhả ra. +* `onKeyUpCapture`: Một phiên bản của `onKeyUp` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onLostPointerCapture`](https://developer.mozilla.org/en-US/docs/Web/API/Element/lostpointercapture_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một phần tử ngừng chụp một con trỏ. +* `onLostPointerCaptureCapture`: Một phiên bản của `onLostPointerCapture` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onMouseDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi con trỏ được nhấn xuống. +* `onMouseDownCapture`: Một phiên bản của `onMouseDown` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onMouseEnter`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi con trỏ di chuyển vào bên trong một phần tử. Không có giai đoạn capture. Thay vào đó, `onMouseLeave` và `onMouseEnter` lan truyền từ phần tử bị rời đi đến phần tử được nhập vào. +* [`onMouseLeave`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi con trỏ di chuyển ra bên ngoài một phần tử. Không có giai đoạn capture. Thay vào đó, `onMouseLeave` và `onMouseEnter` lan truyền từ phần tử bị rời đi đến phần tử được nhập vào. +* [`onMouseMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi con trỏ thay đổi tọa độ. +* `onMouseMoveCapture`: Một phiên bản của `onMouseMove` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onMouseOut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi con trỏ di chuyển ra bên ngoài một phần tử hoặc nếu nó di chuyển vào một phần tử con. +* `onMouseOutCapture`: Một phiên bản của `onMouseOut` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onMouseUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event): Một hàm xử lý [`MouseEvent`](#mouseevent-handler). Kích hoạt khi con trỏ được nhả ra. +* `onMouseUpCapture`: Một phiên bản của `onMouseUp` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPointerCancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointercancel_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi trình duyệt hủy một tương tác con trỏ. +* `onPointerCancelCapture`: Một phiên bản của `onPointerCancel` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPointerDown`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerdown_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một con trỏ trở nên hoạt động. +* `onPointerDownCapture`: Một phiên bản của `onPointerDown` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPointerEnter`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerenter_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một con trỏ di chuyển vào bên trong một phần tử. Không có giai đoạn capture. Thay vào đó, `onPointerLeave` và `onPointerEnter` lan truyền từ phần tử bị rời đi đến phần tử được nhập vào. +* [`onPointerLeave`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerleave_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một con trỏ di chuyển ra bên ngoài một phần tử. Không có giai đoạn capture. Thay vào đó, `onPointerLeave` và `onPointerEnter` lan truyền từ phần tử bị rời đi đến phần tử được nhập vào. +* [`onPointerMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointermove_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một con trỏ thay đổi tọa độ. +* `onPointerMoveCapture`: Một phiên bản của `onPointerMove` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPointerOut`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerout_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một con trỏ di chuyển ra bên ngoài một phần tử, nếu tương tác con trỏ bị hủy và [một vài lý do khác.](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerout_event) +* `onPointerOutCapture`: Một phiên bản của `onPointerOut` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPointerUp`](https://developer.mozilla.org/en-US/docs/Web/API/Element/pointerup_event): Một hàm xử lý [`PointerEvent`](#pointerevent-handler). Kích hoạt khi một con trỏ không còn hoạt động. +* `onPointerUpCapture`: Một phiên bản của `onPointerUp` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPaste`](https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event): Một hàm xử lý [`ClipboardEvent`](#clipboardevent-handler). Kích hoạt khi người dùng cố gắng dán một cái gì đó từ clipboard. +* `onPasteCapture`: Một phiên bản của `onPaste` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onScroll`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi một phần tử đã được cuộn. Sự kiện này không nổi lên. +* `onScrollCapture`: Một phiên bản của `onScroll` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt sau khi lựa chọn bên trong một phần tử có thể chỉnh sửa như một input thay đổi. React mở rộng sự kiện `onSelect` để hoạt động cho các phần tử `contentEditable={true}`. Ngoài ra, React mở rộng nó để kích hoạt cho lựa chọn trống và khi chỉnh sửa (có thể ảnh hưởng đến lựa chọn). +* `onSelectCapture`: Một phiên bản của `onSelect` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onTouchCancel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchcancel_event): Một hàm xử lý [`TouchEvent`](#touchevent-handler). Kích hoạt khi trình duyệt hủy một tương tác chạm. +* `onTouchCancelCapture`: Một phiên bản của `onTouchCancel` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onTouchEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchend_event): Một hàm xử lý [`TouchEvent`](#touchevent-handler). Kích hoạt khi một hoặc nhiều điểm chạm bị xóa. +* `onTouchEndCapture`: Một phiên bản của `onTouchEnd` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onTouchMove`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchmove_event): Một hàm xử lý [`TouchEvent`](#touchevent-handler). Kích hoạt khi một hoặc nhiều điểm chạm được di chuyển. +* `onTouchMoveCapture`: Một phiên bản của `onTouchMove` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onTouchStart`](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event): Một hàm xử lý [`TouchEvent`](#touchevent-handler). Kích hoạt khi một hoặc nhiều điểm chạm được đặt. +* `onTouchStartCapture`: Một phiên bản của `onTouchStart` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onTransitionEnd`](https://developer.mozilla.org/en-US/docs/Web/API/Element/transitionend_event): Một hàm xử lý [`TransitionEvent`](#transitionevent-handler). Kích hoạt khi một CSS transition hoàn thành. +* `onTransitionEndCapture`: Một phiên bản của `onTransitionEnd` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onWheel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event): Một hàm xử lý [`WheelEvent`](#wheelevent-handler). Kích hoạt khi người dùng xoay một nút bánh xe. +* `onWheelCapture`: Một phiên bản của `onWheel` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`role`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles): Một chuỗi. Chỉ định vai trò của phần tử một cách rõ ràng cho các công nghệ hỗ trợ. +* [`slot`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles): Một chuỗi. Chỉ định tên slot khi sử dụng shadow DOM. Trong React, một mẫu tương đương thường đạt được bằng cách truyền JSX làm props, ví dụ: `<Layout left={<Sidebar />} right={<Content />} />`. +* [`spellCheck`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck): Một boolean hoặc null. Nếu được đặt rõ ràng thành `true` hoặc `false`, sẽ bật hoặc tắt kiểm tra chính tả. +* [`tabIndex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex): Một số. Ghi đè hành vi nút Tab mặc định. [Tránh sử dụng các giá trị khác `-1` và `0`.](https://www.tpgi.com/using-the-tabindex-attribute/) +* [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title): Một chuỗi. Chỉ định văn bản tooltip cho phần tử. +* [`translate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/translate): Hoặc `'yes'` hoặc `'no'`. Truyền `'no'` loại trừ nội dung phần tử khỏi việc được dịch. +Bạn cũng có thể truyền các thuộc tính tùy chỉnh dưới dạng props, ví dụ: `mycustomprop="someValue"`. Điều này có thể hữu ích khi tích hợp với các thư viện của bên thứ ba. Tên thuộc tính tùy chỉnh phải là chữ thường và không được bắt đầu bằng `on`. Giá trị sẽ được chuyển đổi thành một chuỗi. Nếu bạn truyền `null` hoặc `undefined`, thuộc tính tùy chỉnh sẽ bị xóa. + +Các sự kiện này chỉ kích hoạt cho các phần tử [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form): + +* [`onReset`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi một biểu mẫu được đặt lại. +* `onResetCapture`: Một phiên bản của `onReset` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onSubmit`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi một biểu mẫu được gửi. +* `onSubmitCapture`: Một phiên bản của `onSubmit` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) + +Các sự kiện này chỉ kích hoạt cho các phần tử [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog). Không giống như các sự kiện của trình duyệt, chúng nổi lên trong React: + +* [`onCancel`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/cancel_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi người dùng cố gắng đóng hộp thoại. +* `onCancelCapture`: Một phiên bản của `onCancel` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onClose`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi một hộp thoại đã được đóng. +* `onCloseCapture`: Một phiên bản của `onClose` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) + +Các sự kiện này chỉ kích hoạt cho các phần tử [`<details>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details). Không giống như các sự kiện của trình duyệt, chúng nổi lên trong React: + +* [`onToggle`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDetailsElement/toggle_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi người dùng bật tắt các chi tiết. +* `onToggleCapture`: Một phiên bản của `onToggle` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) + +Các sự kiện này kích hoạt cho các phần tử [`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img), [`<iframe>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe), [`<object>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object), [`<embed>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed), [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) và SVG [`<image>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_Image_Tag). Không giống như các sự kiện của trình duyệt, chúng nổi lên trong React: + +* `onLoad`: Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi tài nguyên đã tải. +* `onLoadCapture`: Một phiên bản của `onLoad` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onError`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi tài nguyên không thể tải. +* `onErrorCapture`: Một phiên bản của `onError` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) + +Các sự kiện này kích hoạt cho các tài nguyên như [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio) và [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video). Không giống như các sự kiện của trình duyệt, chúng nổi lên trong React: + +* [`onAbort`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/abort_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi tài nguyên chưa được tải đầy đủ, nhưng không phải do lỗi. +* `onAbortCapture`: Một phiên bản của `onAbort` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCanPlay`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplay_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi có đủ dữ liệu để bắt đầu phát, nhưng không đủ để phát đến cuối mà không cần bộ đệm. +* `onCanPlayCapture`: Một phiên bản của `onCanPlay` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onCanPlayThrough`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canplaythrough_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi có đủ dữ liệu để có thể bắt đầu phát mà không cần bộ đệm cho đến cuối. +* `onCanPlayThroughCapture`: Một phiên bản của `onCanPlayThrough` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onDurationChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/durationchange_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi thời lượng phương tiện đã được cập nhật. +* `onDurationChangeCapture`: Một phiên bản của `onDurationChange` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onEmptied`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi phương tiện đã trở nên trống. +* `onEmptiedCapture`: Một phiên bản của `onEmptied` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onEncrypted`](https://w3c.github.io/encrypted-media/#dom-evt-encrypted): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi trình duyệt gặp phương tiện được mã hóa. +* `onEncryptedCapture`: Một phiên bản của `onEncrypted` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onEnded`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi quá trình phát dừng vì không còn gì để phát. +* `onEndedCapture`: Một phiên bản của `onEnded` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onError`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi tài nguyên không thể tải. +* `onErrorCapture`: Một phiên bản của `onError` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onLoadedData`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi khung phát lại hiện tại đã tải. +* `onLoadedDataCapture`: Một phiên bản của `onLoadedData` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onLoadedMetadata`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi siêu dữ liệu đã tải. +* `onLoadedMetadataCapture`: Một phiên bản của `onLoadedMetadata` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onLoadStart`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadstart_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi trình duyệt bắt đầu tải tài nguyên. +* `onLoadStartCapture`: Một phiên bản của `onLoadStart` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPause`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi phương tiện bị tạm dừng. +* `onPauseCapture`: Một phiên bản của `onPause` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPlay`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi phương tiện không còn bị tạm dừng. +* `onPlayCapture`: Một phiên bản của `onPlay` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onPlaying`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playing_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi phương tiện bắt đầu hoặc khởi động lại phát. +* `onPlayingCapture`: Một phiên bản của `onPlaying` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onProgress`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/progress_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt định kỳ trong khi tài nguyên đang tải. +* `onProgressCapture`: Một phiên bản của `onProgress` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onRateChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ratechange_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi tốc độ phát lại thay đổi. +* `onRateChangeCapture`: Một phiên bản của `onRateChange` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* `onResize`: Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi video thay đổi kích thước. +* `onResizeCapture`: Một phiên bản của `onResize` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onSeeked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeked_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi một thao tác tìm kiếm hoàn tất. +* `onSeekedCapture`: Một phiên bản của `onSeeked` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onSeeking`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi một thao tác tìm kiếm bắt đầu. +* `onSeekingCapture`: Một phiên bản của `onSeeking` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onStalled`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/stalled_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi trình duyệt đang chờ dữ liệu nhưng nó vẫn không tải. +* `onStalledCapture`: Một phiên bản của `onStalled` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onSuspend`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/suspend_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi tải tài nguyên bị tạm dừng. +* `onSuspendCapture`: Một phiên bản của `onSuspend` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onTimeUpdate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/timeupdate_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi thời gian phát lại hiện tại được cập nhật. +* `onTimeUpdateCapture`: Một phiên bản của `onTimeUpdate` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onVolumeChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volumechange_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi âm lượng đã thay đổi. +* `onVolumeChangeCapture`: Một phiên bản của `onVolumeChange` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onWaiting`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/waiting_event): Một hàm xử lý [`Event`](#event-handler). Kích hoạt khi quá trình phát dừng do tạm thời thiếu dữ liệu. +* `onWaitingCapture`: Một phiên bản của `onWaiting` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) + +#### Lưu ý {/*common-caveats*/} + +- Bạn không thể truyền cả `children` và `dangerouslySetInnerHTML` cùng một lúc. +- Một số sự kiện (như `onAbort` và `onLoad`) không nổi lên trong trình duyệt, nhưng nổi lên trong React. --- -### `ref` callback function {/*ref-callback*/} +### Hàm callback `ref` {/*ref-callback*/} -Instead of a ref object (like the one returned by [`useRef`](/reference/react/useRef#manipulating-the-dom-with-a-ref)), you may pass a function to the `ref` attribute. +Thay vì một đối tượng ref (như đối tượng được trả về bởi [`useRef`](/reference/react/useRef#manipulating-the-dom-with-a-ref)), bạn có thể truyền một hàm cho thuộc tính `ref`. ```js <div ref={(node) => { - console.log('Attached', node); + console.log('Đã gắn', node); return () => { - console.log('Clean up', node) + console.log('Dọn dẹp', node) } }}> ``` -[See an example of using the `ref` callback.](/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback) +[Xem một ví dụ về cách sử dụng callback `ref`.](/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback) -When the `<div>` DOM node is added to the screen, React will call your `ref` callback with the DOM `node` as the argument. When that `<div>` DOM node is removed, React will call your the cleanup function returned from the callback. +Khi nút DOM `<div>` được thêm vào màn hình, React sẽ gọi callback `ref` của bạn với `node` DOM làm đối số. Khi nút DOM `<div>` đó bị xóa, React sẽ gọi hàm dọn dẹp được trả về từ callback của bạn. -React will also call your `ref` callback whenever you pass a *different* `ref` callback. In the above example, `(node) => { ... }` is a different function on every render. When your component re-renders, the *previous* function will be called with `null` as the argument, and the *next* function will be called with the DOM node. +React cũng sẽ gọi callback `ref` của bạn bất cứ khi nào bạn truyền một callback `ref` *khác*. Trong ví dụ trên, `(node) => { ... }` là một hàm khác nhau trên mỗi lần render. Khi thành phần của bạn render lại, hàm *trước đó* sẽ được gọi với `null` làm đối số và hàm *tiếp theo* sẽ được gọi với nút DOM. -#### Parameters {/*ref-callback-parameters*/} +#### Tham số {/*ref-callback-parameters*/} -* `node`: A DOM node. React will pass you the DOM node when the ref gets attached. Unless you pass the same function reference for the `ref` callback on every render, the callback will get temporarily cleanup and re-create during every re-render of the component. +* `node`: Một nút DOM. React sẽ truyền cho bạn nút DOM khi ref được gắn. Trừ khi bạn truyền cùng một tham chiếu hàm cho callback `ref` trên mỗi lần render, callback sẽ tạm thời được dọn dẹp và tạo lại trong mỗi lần render lại của thành phần. <Note> -#### React 19 added cleanup functions for `ref` callbacks. {/*react-19-added-cleanup-functions-for-ref-callbacks*/} +#### React 19 đã thêm các hàm dọn dẹp cho các callback `ref`. {/*react-19-added-cleanup-functions-for-ref-callbacks*/} -To support backwards compatibility, if a cleanup function is not returned from the `ref` callback, `node` will be called with `null` when the `ref` is detached. This behavior will be removed in a future version. +Để hỗ trợ khả năng tương thích ngược, nếu một hàm dọn dẹp không được trả về từ callback `ref`, `node` sẽ được gọi với `null` khi `ref` bị tách ra. Hành vi này sẽ bị xóa trong một phiên bản tương lai. </Note> -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -* **optional** `cleanup function`: When the `ref` is detached, React will call the cleanup function. If a function is not returned by the `ref` callback, React will call the callback again with `null` as the argument when the `ref` gets detached. This behavior will be removed in a future version. +* **tùy chọn** `hàm dọn dẹp`: Khi `ref` bị tách ra, React sẽ gọi hàm dọn dẹp. Nếu một hàm không được trả về bởi callback `ref`, React sẽ gọi lại callback với `null` làm đối số khi `ref` bị tách ra. Hành vi này sẽ bị xóa trong một phiên bản tương lai. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, implement the cleanup function. -* When you pass a *different* `ref` callback, React will call the *previous* callback's cleanup function if provided. If no cleanup function is defined, the `ref` callback will be called with `null` as the argument. The *next* function will be called with the DOM node. +* Khi Strict Mode được bật, React sẽ **chạy thêm một chu kỳ thiết lập + dọn dẹp chỉ dành cho phát triển** trước lần thiết lập thực tế đầu tiên. Đây là một bài kiểm tra căng thẳng để đảm bảo rằng logic dọn dẹp của bạn "phản ánh" logic thiết lập của bạn và nó dừng hoặc hoàn tác bất cứ điều gì mà thiết lập đang làm. Nếu điều này gây ra sự cố, hãy triển khai hàm dọn dẹp. +* Khi bạn truyền một callback `ref` *khác*, React sẽ gọi hàm dọn dẹp của callback *trước đó* nếu được cung cấp. Nếu không có hàm dọn dẹp nào được xác định, callback `ref` sẽ được gọi với `null` làm đối số. Hàm *tiếp theo* sẽ được gọi với nút DOM. --- -### React event object {/*react-event-object*/} +### Đối tượng sự kiện React {/*react-event-object*/} -Your event handlers will receive a *React event object.* It is also sometimes known as a "synthetic event". +Trình xử lý sự kiện của bạn sẽ nhận được một *đối tượng sự kiện React.* Nó còn được gọi là "sự kiện tổng hợp". ```js <button onClick={e => { - console.log(e); // React event object + console.log(e); // Đối tượng sự kiện React }} /> ``` -It conforms to the same standard as the underlying DOM events, but fixes some browser inconsistencies. +Nó tuân theo cùng một tiêu chuẩn như các sự kiện DOM cơ bản, nhưng khắc phục một số điểm không nhất quán của trình duyệt. -Some React events do not map directly to the browser's native events. For example in `onMouseLeave`, `e.nativeEvent` will point to a `mouseout` event. The specific mapping is not part of the public API and may change in the future. If you need the underlying browser event for some reason, read it from `e.nativeEvent`. +Một số sự kiện React không ánh xạ trực tiếp đến các sự kiện gốc của trình duyệt. Ví dụ: trong `onMouseLeave`, `e.nativeEvent` sẽ trỏ đến một sự kiện `mouseout`. Ánh xạ cụ thể không phải là một phần của API công khai và có thể thay đổi trong tương lai. Nếu bạn cần sự kiện trình duyệt cơ bản vì một lý do nào đó, hãy đọc nó từ `e.nativeEvent`. -#### Properties {/*react-event-object-properties*/} +#### Các thuộc tính {/*react-event-object-properties*/} -React event objects implement some of the standard [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) properties: +Các đối tượng sự kiện React triển khai một số thuộc tính [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) tiêu chuẩn: -* [`bubbles`](https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles): A boolean. Returns whether the event bubbles through the DOM. -* [`cancelable`](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelable): A boolean. Returns whether the event can be canceled. -* [`currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget): A DOM node. Returns the node to which the current handler is attached in the React tree. -* [`defaultPrevented`](https://developer.mozilla.org/en-US/docs/Web/API/Event/defaultPrevented): A boolean. Returns whether `preventDefault` was called. -* [`eventPhase`](https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase): A number. Returns which phase the event is currently in. -* [`isTrusted`](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted): A boolean. Returns whether the event was initiated by user. -* [`target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target): A DOM node. Returns the node on which the event has occurred (which could be a distant child). -* [`timeStamp`](https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp): A number. Returns the time when the event occurred. +* [`bubbles`](https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles): Một boolean. Trả về liệu sự kiện có nổi lên qua DOM hay không. +* [`cancelable`](https://developer.mozilla.org/en-US/docs/Web/API/Event/cancelable): Một boolean. Trả về liệu sự kiện có thể bị hủy hay không. +* [`currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget): Một nút DOM. Trả về nút mà trình xử lý hiện tại được gắn vào trong cây React. +* [`defaultPrevented`](https://developer.mozilla.org/en-US/docs/Web/API/Event/defaultPrevented): Một boolean. Trả về liệu `preventDefault` đã được gọi hay chưa. +* [`eventPhase`](https://developer.mozilla.org/en-US/docs/Web/API/Event/eventPhase): Một số. Trả về giai đoạn mà sự kiện hiện đang ở. +* [`isTrusted`](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted): Một boolean. Trả về liệu sự kiện có được khởi tạo bởi người dùng hay không. +* [`target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target): Một nút DOM. Trả về nút mà sự kiện đã xảy ra trên đó (có thể là một phần tử con ở xa). +* [`timeStamp`](https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp): Một số. Trả về thời gian khi sự kiện xảy ra. -Additionally, React event objects provide these properties: +Ngoài ra, các đối tượng sự kiện React cung cấp các thuộc tính sau: -* `nativeEvent`: A DOM [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event). The original browser event object. +* `nativeEvent`: Một [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) DOM. Đối tượng sự kiện trình duyệt gốc. -#### Methods {/*react-event-object-methods*/} +#### Các phương thức {/*react-event-object-methods*/} -React event objects implement some of the standard [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) methods: +Các đối tượng sự kiện React triển khai một số phương thức [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) tiêu chuẩn: -* [`preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault): Prevents the default browser action for the event. -* [`stopPropagation()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation): Stops the event propagation through the React tree. +* [`preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault): Ngăn chặn hành động mặc định của trình duyệt cho sự kiện. +* [`stopPropagation()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation): Dừng sự lan truyền sự kiện qua cây React. -Additionally, React event objects provide these methods: +Ngoài ra, các đối tượng sự kiện React cung cấp các phương thức sau: -* `isDefaultPrevented()`: Returns a boolean value indicating whether `preventDefault` was called. -* `isPropagationStopped()`: Returns a boolean value indicating whether `stopPropagation` was called. -* `persist()`: Not used with React DOM. With React Native, call this to read event's properties after the event. -* `isPersistent()`: Not used with React DOM. With React Native, returns whether `persist` has been called. +* `isDefaultPrevented()`: Trả về một giá trị boolean cho biết liệu `preventDefault` đã được gọi hay chưa. +* `isPropagationStopped()`: Trả về một giá trị boolean cho biết liệu `stopPropagation` đã được gọi hay chưa. +* `persist()`: Không được sử dụng với React DOM. Với React Native, hãy gọi phương thức này để đọc các thuộc tính của sự kiện sau sự kiện. +* `isPersistent()`: Không được sử dụng với React DOM. Với React Native, trả về liệu `persist` đã được gọi hay chưa. -#### Caveats {/*react-event-object-caveats*/} +#### Lưu ý {/*react-event-object-caveats*/} -* The values of `currentTarget`, `eventPhase`, `target`, and `type` reflect the values your React code expects. Under the hood, React attaches event handlers at the root, but this is not reflected in React event objects. For example, `e.currentTarget` may not be the same as the underlying `e.nativeEvent.currentTarget`. For polyfilled events, `e.type` (React event type) may differ from `e.nativeEvent.type` (underlying type). +* Các giá trị của `currentTarget`, `eventPhase`, `target` và `type` phản ánh các giá trị mà mã React của bạn mong đợi. Dưới nền, React gắn các trình xử lý sự kiện ở gốc, nhưng điều này không được phản ánh trong các đối tượng sự kiện React. Ví dụ: `e.currentTarget` có thể không giống với `e.nativeEvent.currentTarget` cơ bản. Đối với các sự kiện được polyfill, `e.type` (loại sự kiện React) có thể khác với `e.nativeEvent.type` (loại cơ bản). --- -### `AnimationEvent` handler function {/*animationevent-handler*/} +### Hàm xử lý `AnimationEvent` {/*animationevent-handler*/} -An event handler type for the [CSS animation](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) events. +Một kiểu trình xử lý sự kiện cho các sự kiện [CSS animation](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations). ```js <div @@ -347,18 +346,18 @@ An event handler type for the [CSS animation](https://developer.mozilla.org/en-U /> ``` -#### Parameters {/*animationevent-handler-parameters*/} +#### Tham số {/*animationevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`AnimationEvent`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`AnimationEvent`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent) bổ sung sau: * [`animationName`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent/animationName) * [`elapsedTime`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent/elapsedTime) * [`pseudoElement`](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent/pseudoElement) --- -### `ClipboardEvent` handler function {/*clipboadevent-handler*/} +### Hàm xử lý `ClipboardEvent` {/*clipboadevent-handler*/} -An event handler type for the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) events. +Một kiểu trình xử lý sự kiện cho các sự kiện [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). ```js <input @@ -368,17 +367,17 @@ An event handler type for the [Clipboard API](https://developer.mozilla.org/en-U /> ``` -#### Parameters {/*clipboadevent-handler-parameters*/} +#### Tham số {/*clipboadevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`ClipboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`ClipboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent) bổ sung sau: * [`clipboardData`](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData) --- -### `CompositionEvent` handler function {/*compositionevent-handler*/} +### Hàm xử lý `CompositionEvent` {/*compositionevent-handler*/} -An event handler type for the [input method editor (IME)](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor) events. +Một kiểu trình xử lý sự kiện cho các sự kiện [input method editor (IME)](https://developer.mozilla.org/en-US/docs/Glossary/Input_method_editor). ```js <input @@ -388,16 +387,16 @@ An event handler type for the [input method editor (IME)](https://developer.mozi /> ``` -#### Parameters {/*compositionevent-handler-parameters*/} +#### Tham số {/*compositionevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`CompositionEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`CompositionEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent) bổ sung sau: * [`data`](https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent/data) --- -### `DragEvent` handler function {/*dragevent-handler*/} +### Hàm xử lý `DragEvent` {/*dragevent-handler*/} -An event handler type for the [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) events. +Một kiểu trình xử lý sự kiện cho các sự kiện [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API). ```js <> @@ -420,12 +419,12 @@ An event handler type for the [HTML Drag and Drop API](https://developer.mozilla </> ``` -#### Parameters {/*dragevent-handler-parameters*/} +#### Tham số {/*dragevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`DragEvent`](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`DragEvent`](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent) bổ sung sau: * [`dataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/dataTransfer) - It also includes the inherited [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) properties: + Nó cũng bao gồm các thuộc tính [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) được kế thừa: * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey) * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button) @@ -444,16 +443,16 @@ An event handler type for the [HTML Drag and Drop API](https://developer.mozilla * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY) * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `FocusEvent` handler function {/*focusevent-handler*/} +### Hàm xử lý `FocusEvent` {/*focusevent-handler*/} -An event handler type for the focus events. +Một kiểu trình xử lý sự kiện cho các sự kiện focus. ```js <input @@ -462,48 +461,48 @@ An event handler type for the focus events. /> ``` -[See an example.](#handling-focus-events) +[Xem một ví dụ.](#handling-focus-events) -#### Parameters {/*focusevent-handler-parameters*/} +#### Tham số {/*focusevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`FocusEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`FocusEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent) bổ sung sau: * [`relatedTarget`](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `Event` handler function {/*event-handler*/} +### Hàm xử lý `Event` {/*event-handler*/} -An event handler type for generic events. +Một kiểu trình xử lý sự kiện cho các sự kiện chung. -#### Parameters {/*event-handler-parameters*/} +#### Tham số {/*event-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with no additional properties. +* `e`: Một [đối tượng sự kiện React](#react-event-object) không có thuộc tính bổ sung. --- -### `InputEvent` handler function {/*inputevent-handler*/} +### Hàm xử lý `InputEvent` {/*inputevent-handler*/} -An event handler type for the `onBeforeInput` event. +Một kiểu trình xử lý sự kiện cho sự kiện `onBeforeInput`. ```js <input onBeforeInput={e => console.log('onBeforeInput')} /> ``` -#### Parameters {/*inputevent-handler-parameters*/} +#### Tham số {/*inputevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent) bổ sung sau: * [`data`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/data) --- -### `KeyboardEvent` handler function {/*keyboardevent-handler*/} +### Hàm xử lý `KeyboardEvent` {/*keyboardevent-handler*/} -An event handler type for keyboard events. +Một kiểu trình xử lý sự kiện cho các sự kiện bàn phím. ```js <input @@ -512,11 +511,11 @@ An event handler type for keyboard events. /> ``` -[See an example.](#handling-keyboard-events) +[Xem một ví dụ.](#handling-keyboard-events) -#### Parameters {/*keyboardevent-handler-parameters*/} +#### Tham số {/*keyboardevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`KeyboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`KeyboardEvent`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent) bổ sung sau: * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/altKey) * [`charCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/charCode) * [`code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) @@ -531,16 +530,16 @@ An event handler type for keyboard events. * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/shiftKey) * [`which`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `MouseEvent` handler function {/*mouseevent-handler*/} +### Hàm xử lý `MouseEvent` {/*mouseevent-handler*/} -An event handler type for mouse events. +Một kiểu trình xử lý sự kiện cho các sự kiện chuột. ```js <div @@ -553,11 +552,11 @@ An event handler type for mouse events. /> ``` -[See an example.](#handling-mouse-events) +[Xem một ví dụ.](#handling-mouse-events) -#### Parameters {/*mouseevent-handler-parameters*/} +#### Tham số {/*mouseevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) bổ sung sau: * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey) * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button) * [`buttons`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons) @@ -575,16 +574,16 @@ An event handler type for mouse events. * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY) * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `PointerEvent` handler function {/*pointerevent-handler*/} +### Hàm xử lý `PointerEvent` {/*pointerevent-handler*/} -An event handler type for [pointer events.](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) +Một kiểu trình xử lý sự kiện cho [pointer events.](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) ```js <div @@ -596,11 +595,11 @@ An event handler type for [pointer events.](https://developer.mozilla.org/en-US/ /> ``` -[See an example.](#handling-pointer-events) +[Xem một ví dụ.](#handling-pointer-events) -#### Parameters {/*pointerevent-handler-parameters*/} +#### Tham số {/*pointerevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent) bổ sung sau: * [`height`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height) * [`isPrimary`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary) * [`pointerId`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId) @@ -612,7 +611,7 @@ An event handler type for [pointer events.](https://developer.mozilla.org/en-US/ * [`twist`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/twist) * [`width`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width) - It also includes the inherited [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) properties: + Nó cũng bao gồm các thuộc tính [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) được kế thừa: * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey) * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button) @@ -631,16 +630,16 @@ An event handler type for [pointer events.](https://developer.mozilla.org/en-US/ * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY) * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `TouchEvent` handler function {/*touchevent-handler*/} +### Hàm xử lý `TouchEvent` {/*touchevent-handler*/} -An event handler type for [touch events.](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events) +Một kiểu trình xử lý sự kiện cho [touch events.](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events) ```js <div @@ -651,9 +650,9 @@ An event handler type for [touch events.](https://developer.mozilla.org/en-US/do /> ``` -#### Parameters {/*touchevent-handler-parameters*/} +#### Tham số {/*touchevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`TouchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`TouchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent) bổ sung sau: * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/altKey) * [`ctrlKey`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/ctrlKey) * [`changedTouches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/changedTouches) @@ -663,16 +662,16 @@ An event handler type for [touch events.](https://developer.mozilla.org/en-US/do * [`touches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches) * [`targetTouches`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/targetTouches) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `TransitionEvent` handler function {/*transitionevent-handler*/} +### Hàm xử lý `TransitionEvent` {/*transitionevent-handler*/} -An event handler type for the CSS transition events. +Một kiểu trình xử lý sự kiện cho các sự kiện CSS transition. ```js <div @@ -680,18 +679,18 @@ An event handler type for the CSS transition events. /> ``` -#### Parameters {/*transitionevent-handler-parameters*/} +#### Tham số {/*transitionevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`TransitionEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`TransitionEvent`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent) bổ sung sau: * [`elapsedTime`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent/elapsedTime) * [`propertyName`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent/propertyName) * [`pseudoElement`](https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent/pseudoElement) --- -### `UIEvent` handler function {/*uievent-handler*/} +### Hàm xử lý `UIEvent` {/*uievent-handler*/} -An event handler type for generic UI events. +Một kiểu trình xử lý sự kiện cho các sự kiện UI chung. ```js <div @@ -699,17 +698,17 @@ An event handler type for generic UI events. /> ``` -#### Parameters {/*uievent-handler-parameters*/} +#### Tham số {/*uievent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) bổ sung sau: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -### `WheelEvent` handler function {/*wheelevent-handler*/} +### Hàm xử lý `WheelEvent` {/*wheelevent-handler*/} -An event handler type for the `onWheel` event. +Một kiểu trình xử lý sự kiện cho sự kiện `onWheel`. ```js <div @@ -717,16 +716,16 @@ An event handler type for the `onWheel` event. /> ``` -#### Parameters {/*wheelevent-handler-parameters*/} +#### Tham số {/*wheelevent-handler-parameters*/} -* `e`: A [React event object](#react-event-object) with these extra [`WheelEvent`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent) properties: +* `e`: Một [đối tượng sự kiện React](#react-event-object) với các thuộc tính [`WheelEvent`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent) bổ sung sau: * [`deltaMode`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode) * [`deltaX`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaX) * [`deltaY`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaY) * [`deltaZ`](https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaZ) - It also includes the inherited [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) properties: + Nó cũng bao gồm các thuộc tính [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) được kế thừa: * [`altKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/altKey) * [`button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button) @@ -745,35 +744,34 @@ An event handler type for the `onWheel` event. * [`screenY`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/screenY) * [`shiftKey`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/shiftKey) - It also includes the inherited [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) properties: + Nó cũng bao gồm các thuộc tính [`UIEvent`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent) được kế thừa: * [`detail`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail) * [`view`](https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/view) --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Applying CSS styles {/*applying-css-styles*/} +### Áp dụng các kiểu CSS {/*applying-css-styles*/} -In React, you specify a CSS class with [`className`.](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) It works like the `class` attribute in HTML: +Trong React, bạn chỉ định một lớp CSS với [`className`.](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) Nó hoạt động giống như thuộc tính `class` trong HTML: ```js <img className="avatar" /> ``` - -Then you write the CSS rules for it in a separate CSS file: +Sau đó, bạn viết các quy tắc CSS cho nó trong một tệp CSS riêng biệt: ```css -/* In your CSS */ +/* Trong CSS của bạn */ .avatar { border-radius: 50%; } ``` -React does not prescribe how you add CSS files. In the simplest case, you'll add a [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) tag to your HTML. If you use a build tool or a framework, consult its documentation to learn how to add a CSS file to your project. +React không quy định cách bạn thêm các tệp CSS. Trong trường hợp đơn giản nhất, bạn sẽ thêm thẻ [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) vào HTML của bạn. Nếu bạn sử dụng một công cụ xây dựng hoặc một framework, hãy tham khảo tài liệu của nó để tìm hiểu cách thêm một tệp CSS vào dự án của bạn. -Sometimes, the style values depend on data. Use the `style` attribute to pass some styles dynamically: +Đôi khi, các giá trị kiểu phụ thuộc vào dữ liệu. Sử dụng thuộc tính `style` để truyền một số kiểu động: ```js {3-6} <img @@ -786,7 +784,7 @@ Sometimes, the style values depend on data. Use the `style` attribute to pass so ``` -In the above example, `style={{}}` is not a special syntax, but a regular `{}` object inside the `style={ }` [JSX curly braces.](/learn/javascript-in-jsx-with-curly-braces) We recommend only using the `style` attribute when your styles depend on JavaScript variables. +Trong ví dụ trên, `style={{}}` không phải là một cú pháp đặc biệt, mà là một đối tượng `{}` thông thường bên trong [dấu ngoặc nhọn JSX.](/learn/javascript-in-jsx-with-curly-braces) Chúng tôi khuyên bạn chỉ nên sử dụng thuộc tính `style` khi các kiểu của bạn phụ thuộc vào các biến JavaScript. <Sandpack> @@ -809,7 +807,7 @@ export default function Avatar({ user }) { return ( <img src={user.imageUrl} - alt={'Photo of ' + user.name} + alt={'Ảnh của ' + user.name} className="avatar" style={{ width: user.imageSize, @@ -830,13 +828,13 @@ export default function Avatar({ user }) { <DeepDive> -#### How to apply multiple CSS classes conditionally? {/*how-to-apply-multiple-css-classes-conditionally*/} +#### Làm thế nào để áp dụng nhiều lớp CSS một cách có điều kiện? {/*how-to-apply-multiple-css-classes-conditionally*/} -To apply CSS classes conditionally, you need to produce the `className` string yourself using JavaScript. +Để áp dụng các lớp CSS một cách có điều kiện, bạn cần tự tạo chuỗi `className` bằng JavaScript. -For example, `className={'row ' + (isSelected ? 'selected': '')}` will produce either `className="row"` or `className="row selected"` depending on whether `isSelected` is `true`. +Ví dụ: `className={'row ' + (isSelected ? 'selected': '')}` sẽ tạo ra `className="row"` hoặc `className="row selected"` tùy thuộc vào việc `isSelected` có phải là `true` hay không. -To make this more readable, you can use a tiny helper library like [`classnames`:](https://github.com/JedWatson/classnames) +Để làm cho điều này dễ đọc hơn, bạn có thể sử dụng một thư viện trợ giúp nhỏ như [`classnames`:](https://github.com/JedWatson/classnames) ```js import cn from 'classnames'; @@ -850,7 +848,7 @@ function Row({ isSelected }) { } ``` -It is especially convenient if you have multiple conditional classes: +Điều này đặc biệt thuận tiện nếu bạn có nhiều lớp có điều kiện: ```js import cn from 'classnames'; @@ -872,11 +870,11 @@ function Row({ isSelected, size }) { --- -### Manipulating a DOM node with a ref {/*manipulating-a-dom-node-with-a-ref*/} +### Thao tác với một nút DOM bằng ref {/*manipulating-a-dom-node-with-a-ref*/} -Sometimes, you'll need to get the browser DOM node associated with a tag in JSX. For example, if you want to focus an `<input>` when a button is clicked, you need to call [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the browser `<input>` DOM node. +Đôi khi, bạn sẽ cần lấy nút DOM của trình duyệt được liên kết với một thẻ trong JSX. Ví dụ: nếu bạn muốn tập trung vào một `<input>` khi một nút được nhấp, bạn cần gọi [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) trên nút DOM `<input>` của trình duyệt. -To obtain the browser DOM node for a tag, [declare a ref](/reference/react/useRef) and pass it as the `ref` attribute to that tag: +Để lấy nút DOM của trình duyệt cho một thẻ, [khai báo một ref](/reference/react/useRef) và truyền nó làm thuộc tính `ref` cho thẻ đó: ```js {7} import { useRef } from 'react'; @@ -889,7 +887,7 @@ export default function Form() { // ... ``` -React will put the DOM node into `inputRef.current` after it's been rendered to the screen. +React sẽ đặt nút DOM vào `inputRef.current` sau khi nó được hiển thị trên màn hình. <Sandpack> @@ -907,7 +905,7 @@ export default function Form() { <> <input ref={inputRef} /> <button onClick={handleClick}> - Focus the input + Focus vào ô input </button> </> ); @@ -916,24 +914,24 @@ export default function Form() { </Sandpack> -Read more about [manipulating DOM with refs](/learn/manipulating-the-dom-with-refs) and [check out more examples.](/reference/react/useRef#examples-dom) +Đọc thêm về [thao tác DOM với ref](/learn/manipulating-the-dom-with-refs) và [xem thêm các ví dụ.](/reference/react/useRef#examples-dom) -For more advanced use cases, the `ref` attribute also accepts a [callback function.](#ref-callback) +Đối với các trường hợp sử dụng nâng cao hơn, thuộc tính `ref` cũng chấp nhận một [hàm callback.](#ref-callback) --- -### Dangerously setting the inner HTML {/*dangerously-setting-the-inner-html*/} +### Thiết lập inner HTML một cách nguy hiểm {/*dangerously-setting-the-inner-html*/} -You can pass a raw HTML string to an element like so: +Bạn có thể truyền một chuỗi HTML thô cho một phần tử như sau: ```js const markup = { __html: '<p>some raw html</p>' }; return <div dangerouslySetInnerHTML={markup} />; ``` -**This is dangerous. As with the underlying DOM [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) property, you must exercise extreme caution! Unless the markup is coming from a completely trusted source, it is trivial to introduce an [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) vulnerability this way.** +**Điều này rất nguy hiểm. Giống như thuộc tính [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) của DOM cơ bản, bạn phải hết sức thận trọng! Trừ khi markup đến từ một nguồn hoàn toàn đáng tin cậy, nếu không việc đưa vào lỗ hổng [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) là rất dễ dàng.** -For example, if you use a Markdown library that converts Markdown to HTML, you trust that its parser doesn't contain bugs, and the user only sees their own input, you can display the resulting HTML like this: +Ví dụ: nếu bạn sử dụng một thư viện Markdown chuyển đổi Markdown thành HTML, bạn tin rằng trình phân tích cú pháp của nó không chứa lỗi và người dùng chỉ thấy đầu vào của riêng họ, bạn có thể hiển thị HTML kết quả như sau: <Sandpack> @@ -946,7 +944,7 @@ export default function MarkdownEditor() { return ( <> <label> - Enter some markdown: + Nhập một số markdown: <textarea value={postContent} onChange={e => setPostContent(e.target.value)} @@ -965,9 +963,9 @@ import { Remarkable } from 'remarkable'; const md = new Remarkable(); function renderMarkdownToHTML(markdown) { - // This is ONLY safe because the output HTML - // is shown to the same user, and because you - // trust this Markdown parser to not have bugs. + // Điều này CHỈ an toàn vì HTML đầu ra + // được hiển thị cho cùng một người dùng và vì bạn + // tin tưởng trình phân tích cú pháp Markdown này không có lỗi. const renderedHTML = md.render(markdown); return {__html: renderedHTML}; } @@ -1001,30 +999,30 @@ textarea { display: block; margin-top: 5px; margin-bottom: 10px; } </Sandpack> -The `{__html}` object should be created as close to where the HTML is generated as possible, like the above example does in the `renderMarkdownToHTML` function. This ensures that all raw HTML being used in your code is explicitly marked as such, and that only variables that you expect to contain HTML are passed to `dangerouslySetInnerHTML`. It is not recommended to create the object inline like `<div dangerouslySetInnerHTML={{__html: markup}} />`. +Đối tượng `{__html}` nên được tạo càng gần nơi HTML được tạo càng tốt, như ví dụ trên trong hàm `renderMarkdownToHTML`. Điều này đảm bảo rằng tất cả HTML thô được sử dụng trong mã của bạn được đánh dấu rõ ràng là như vậy và chỉ các biến mà bạn mong đợi chứa HTML mới được truyền cho `dangerouslySetInnerHTML`. Không nên tạo đối tượng nội tuyến như `<div dangerouslySetInnerHTML={{__html: markup}} />`. -To see why rendering arbitrary HTML is dangerous, replace the code above with this: +Để xem tại sao việc hiển thị HTML tùy ý là nguy hiểm, hãy thay thế mã trên bằng mã này: ```js {1-4,7,8} const post = { - // Imagine this content is stored in the database. - content: `<img src="" onerror='alert("you were hacked")'>` + // Hãy tưởng tượng nội dung này được lưu trữ trong cơ sở dữ liệu. + content: `<img src="" onerror='alert("bạn đã bị hack")'>` }; export default function MarkdownPreview() { - // 🔴 SECURITY HOLE: passing untrusted input to dangerouslySetInnerHTML + // 🔴 LỖ HỔNG BẢO MẬT: truyền đầu vào không đáng tin cậy cho dangerouslySetInnerHTML const markup = { __html: post.content }; return <div dangerouslySetInnerHTML={markup} />; } ``` -The code embedded in the HTML will run. A hacker could use this security hole to steal user information or to perform actions on their behalf. **Only use `dangerouslySetInnerHTML` with trusted and sanitized data.** +Mã được nhúng trong HTML sẽ chạy. Một hacker có thể sử dụng lỗ hổng bảo mật này để đánh cắp thông tin người dùng hoặc thực hiện các hành động thay mặt họ. **Chỉ sử dụng `dangerouslySetInnerHTML` với dữ liệu đáng tin cậy và đã được làm sạch.** --- -### Handling mouse events {/*handling-mouse-events*/} +### Xử lý các sự kiện chuột {/*handling-mouse-events*/} -This example shows some common [mouse events](#mouseevent-handler) and when they fire. +Ví dụ này cho thấy một số [sự kiện chuột](#mouseevent-handler) phổ biến và khi chúng kích hoạt. <Sandpack> @@ -1043,7 +1041,7 @@ export default function MouseExample() { onMouseOver={e => console.log('onMouseOver (first button)')} onMouseUp={e => console.log('onMouseUp (first button)')} > - First button + Nút thứ nhất </button> <button onClick={e => console.log('onClick (second button)')} @@ -1053,7 +1051,7 @@ export default function MouseExample() { onMouseOver={e => console.log('onMouseOver (second button)')} onMouseUp={e => console.log('onMouseUp (second button)')} > - Second button + Nút thứ hai </button> </div> ); @@ -1069,9 +1067,9 @@ input { margin-left: 10px; } --- -### Handling pointer events {/*handling-pointer-events*/} +### Xử lý các sự kiện con trỏ {/*handling-pointer-events*/} -This example shows some common [pointer events](#pointerevent-handler) and when they fire. +Ví dụ này cho thấy một số [sự kiện con trỏ](#pointerevent-handler) phổ biến và khi chúng kích hoạt. <Sandpack> @@ -1091,7 +1089,7 @@ export default function PointerExample() { onPointerUp={e => console.log('onPointerUp (first child)')} style={{ padding: 20, backgroundColor: 'lightyellow' }} > - First child + Con thứ nhất </div> <div onPointerDown={e => console.log('onPointerDown (second child)')} @@ -1101,7 +1099,7 @@ export default function PointerExample() { onPointerUp={e => console.log('onPointerUp (second child)')} style={{ padding: 20, backgroundColor: 'lightblue' }} > - Second child + Con thứ hai </div> </div> ); @@ -1117,9 +1115,9 @@ input { margin-left: 10px; } --- -### Handling focus events {/*handling-focus-events*/} +### Xử lý các sự kiện focus {/*handling-focus-events*/} -In React, [focus events](#focusevent-handler) bubble. You can use the `currentTarget` and `relatedTarget` to differentiate if the focusing or blurring events originated from outside of the parent element. The example shows how to detect focusing a child, focusing the parent element, and how to detect focus entering or leaving the whole subtree. +Trong React, [các sự kiện focus](#focusevent-handler) nổi lên. Bạn có thể sử dụng `currentTarget` và `relatedTarget` để phân biệt xem các sự kiện focus hoặc blur có bắt nguồn từ bên ngoài phần tử cha hay không. Ví dụ này cho thấy cách phát hiện focus vào một phần tử con, focus vào phần tử cha và cách phát hiện focus đi vào hoặc rời khỏi toàn bộ cây con. <Sandpack> @@ -1135,7 +1133,7 @@ export default function FocusExample() { console.log('focused child', e.target.name); } if (!e.currentTarget.contains(e.relatedTarget)) { - // Not triggered when swapping focus between children + // Không kích hoạt khi hoán đổi focus giữa các phần tử con console.log('focus entered parent'); } }} @@ -1146,17 +1144,17 @@ export default function FocusExample() { console.log('unfocused child', e.target.name); } if (!e.currentTarget.contains(e.relatedTarget)) { - // Not triggered when swapping focus between children + // Không kích hoạt khi hoán đổi focus giữa các phần tử con console.log('focus left parent'); } }} > <label> - First name: + Tên: <input name="firstName" /> </label> <label> - Last name: + Họ: <input name="lastName" /> </label> </div> @@ -1173,9 +1171,9 @@ input { margin-left: 10px; } --- -### Handling keyboard events {/*handling-keyboard-events*/} +### Xử lý các sự kiện bàn phím {/*handling-keyboard-events*/} -This example shows some common [keyboard events](#keyboardevent-handler) and when they fire. +Ví dụ này cho thấy một số [sự kiện bàn phím](#keyboardevent-handler) phổ biến và khi chúng kích hoạt. <Sandpack> @@ -1183,7 +1181,7 @@ This example shows some common [keyboard events](#keyboardevent-handler) and whe export default function KeyboardExample() { return ( <label> - First name: + Tên: <input name="firstName" onKeyDown={e => console.log('onKeyDown:', e.key, e.code)} diff --git a/src/content/reference/react-dom/components/form.md b/src/content/reference/react-dom/components/form.md index 115e6a4cd..bdc4f3d61 100644 --- a/src/content/reference/react-dom/components/form.md +++ b/src/content/reference/react-dom/components/form.md @@ -4,12 +4,12 @@ title: "<form>" <Intro> -The [built-in browser `<form>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) lets you create interactive controls for submitting information. +[Component `<form>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) cho phép bạn tạo các điều khiển tương tác để gửi thông tin. ```js <form action={search}> - <input name="query" /> - <button type="submit">Search</button> + <input name="query" /> + <button type="submit">Tìm kiếm</button> </form> ``` @@ -19,110 +19,111 @@ The [built-in browser `<form>` component](https://developer.mozilla.org/en-US/do --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<form>` {/*form*/} -To create interactive controls for submitting information, render the [built-in browser `<form>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form). +Để tạo các điều khiển tương tác để gửi thông tin, hãy render [component `<form>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form). ```js <form action={search}> - <input name="query" /> - <button type="submit">Search</button> + <input name="query" /> + <button type="submit">Tìm kiếm</button> </form> + ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<form>` supports all [common element props.](/reference/react-dom/components/common#props) +`<form>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) -[`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): a URL or function. When a URL is passed to `action` the form will behave like the HTML form component. When a function is passed to `action` the function will handle the form submission. The function passed to `action` may be async and will be called with a single argument containing the [form data](https://developer.mozilla.org/en-US/docs/Web/API/FormData) of the submitted form. The `action` prop can be overridden by a `formAction` attribute on a `<button>`, `<input type="submit">`, or `<input type="image">` component. +[`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): một URL hoặc một hàm. Khi một URL được truyền cho `action`, form sẽ hoạt động giống như component form HTML. Khi một hàm được truyền cho `action`, hàm đó sẽ xử lý việc gửi form. Hàm được truyền cho `action` có thể là async và sẽ được gọi với một đối số duy nhất chứa [dữ liệu form](https://developer.mozilla.org/en-US/docs/Web/API/FormData) của form đã gửi. Prop `action` có thể bị ghi đè bởi thuộc tính `formAction` trên component `<button>`, `<input type="submit">` hoặc `<input type="image">`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* When a function is passed to `action` or `formAction` the HTTP method will be POST regardless of value of the `method` prop. +* Khi một hàm được truyền cho `action` hoặc `formAction`, phương thức HTTP sẽ là POST bất kể giá trị của prop `method`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Handle form submission on the client {/*handle-form-submission-on-the-client*/} +### Xử lý việc gửi form trên client {/*handle-form-submission-on-the-client*/} -Pass a function to the `action` prop of form to run the function when the form is submitted. [`formData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) will be passed to the function as an argument so you can access the data submitted by the form. This differs from the conventional [HTML action](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action), which only accepts URLs. After the `action` function succeeds, all uncontrolled field elements in the form are reset. +Truyền một hàm cho prop `action` của form để chạy hàm khi form được gửi. [`formData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) sẽ được truyền cho hàm như một đối số để bạn có thể truy cập dữ liệu được gửi bởi form. Điều này khác với [action HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action) thông thường, chỉ chấp nhận URL. Sau khi hàm `action` thành công, tất cả các phần tử trường không được kiểm soát trong form sẽ được đặt lại. <Sandpack> ```js src/App.js export default function Search() { function search(formData) { - const query = formData.get("query"); - alert(`You searched for '${query}'`); + const query = formData.get("query"); + alert(`Bạn đã tìm kiếm '${query}'`); } return ( - <form action={search}> - <input name="query" /> - <button type="submit">Search</button> - </form> + <form action={search}> + <input name="query" /> + <button type="submit">Tìm kiếm</button> + </form> ); } ``` </Sandpack> -### Handle form submission with a Server Function {/*handle-form-submission-with-a-server-function*/} +### Xử lý việc gửi form bằng Server Function {/*handle-form-submission-with-a-server-function*/} -Render a `<form>` with an input and submit button. Pass a Server Function (a function marked with [`'use server'`](/reference/rsc/use-server)) to the `action` prop of form to run the function when the form is submitted. +Render một `<form>` với một input và nút submit. Truyền một Server Function (một hàm được đánh dấu bằng [`'use server'`](/reference/rsc/use-server)) cho prop `action` của form để chạy hàm khi form được gửi. -Passing a Server Function to `<form action>` allow users to submit forms without JavaScript enabled or before the code has loaded. This is beneficial to users who have a slow connection, device, or have JavaScript disabled and is similar to the way forms work when a URL is passed to the `action` prop. +Việc truyền một Server Function cho `<form action>` cho phép người dùng gửi form mà không cần bật JavaScript hoặc trước khi code được tải. Điều này có lợi cho những người dùng có kết nối chậm, thiết bị chậm hoặc đã tắt JavaScript và tương tự như cách form hoạt động khi một URL được truyền cho prop `action`. -You can use hidden form fields to provide data to the `<form>`'s action. The Server Function will be called with the hidden form field data as an instance of [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). +Bạn có thể sử dụng các trường form ẩn để cung cấp dữ liệu cho action của `<form>`. Server Function sẽ được gọi với dữ liệu trường form ẩn như một instance của [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). ```jsx import { updateCart } from './lib.js'; function AddToCart({productId}) { async function addToCart(formData) { - 'use server' - const productId = formData.get('productId') - await updateCart(productId) + 'use server' + const productId = formData.get('productId') + await updateCart(productId) } return ( - <form action={addToCart}> - <input type="hidden" name="productId" value={productId} /> - <button type="submit">Add to Cart</button> - </form> + <form action={addToCart}> + <input type="hidden" name="productId" value={productId} /> + <button type="submit">Thêm vào giỏ hàng</button> + </form> ); } ``` -In lieu of using hidden form fields to provide data to the `<form>`'s action, you can call the <CodeStep step={1}>`bind`</CodeStep> method to supply it with extra arguments. This will bind a new argument (<CodeStep step={2}>`productId`</CodeStep>) to the function in addition to the <CodeStep step={3}>`formData`</CodeStep> that is passed as an argument to the function. +Thay vì sử dụng các trường form ẩn để cung cấp dữ liệu cho action của `<form>`, bạn có thể gọi phương thức <CodeStep step={1}>`bind`</CodeStep> để cung cấp thêm các đối số cho nó. Điều này sẽ liên kết một đối số mới (<CodeStep step={2}>`productId`</CodeStep>) với hàm ngoài <CodeStep step={3}>`formData`</CodeStep> được truyền như một đối số cho hàm. ```jsx [[1, 8, "bind"], [2,8, "productId"], [2,4, "productId"], [3,4, "formData"]] import { updateCart } from './lib.js'; function AddToCart({productId}) { async function addToCart(productId, formData) { - "use server"; - await updateCart(productId) + "use server"; + await updateCart(productId) } const addProductToCart = addToCart.bind(null, productId); return ( - <form action={addProductToCart}> - <button type="submit">Add to Cart</button> - </form> + <form action={addProductToCart}> + <button type="submit">Thêm vào giỏ hàng</button> + </form> ); } ``` -When `<form>` is rendered by a [Server Component](/reference/rsc/use-client), and a [Server Function](/reference/rsc/server-functions) is passed to the `<form>`'s `action` prop, the form is [progressively enhanced](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). +Khi `<form>` được render bởi một [Server Component](/reference/rsc/use-client) và một [Server Function](/reference/rsc/server-functions) được truyền cho prop `action` của `<form>`, form sẽ được [nâng cấp dần](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). -### Display a pending state during form submission {/*display-a-pending-state-during-form-submission*/} -To display a pending state when a form is being submitted, you can call the `useFormStatus` Hook in a component rendered in a `<form>` and read the `pending` property returned. +### Hiển thị trạng thái chờ trong khi gửi form {/*display-a-pending-state-during-form-submission*/} +Để hiển thị trạng thái chờ khi form đang được gửi, bạn có thể gọi Hook `useFormStatus` trong một component được render trong `<form>` và đọc thuộc tính `pending` được trả về. -Here, we use the `pending` property to indicate the form is submitting. +Ở đây, chúng ta sử dụng thuộc tính `pending` để chỉ ra rằng form đang được gửi. <Sandpack> @@ -133,17 +134,17 @@ import { submitForm } from "./actions.js"; function Submit() { const { pending } = useFormStatus(); return ( - <button type="submit" disabled={pending}> - {pending ? "Submitting..." : "Submit"} - </button> + <button type="submit" disabled={pending}> + {pending ? "Đang gửi..." : "Gửi"} + </button> ); } function Form({ action }) { return ( - <form action={action}> - <Submit /> - </form> + <form action={action}> + <Submit /> + </form> ); } @@ -154,18 +155,18 @@ export default function App() { ```js src/actions.js hidden export async function submitForm(query) { - await new Promise((res) => setTimeout(res, 1000)); + await new Promise((res) => setTimeout(res, 1000)); } ``` </Sandpack> -To learn more about the `useFormStatus` Hook see the [reference documentation](/reference/react-dom/hooks/useFormStatus). +Để tìm hiểu thêm về Hook `useFormStatus`, hãy xem [tài liệu tham khảo](/reference/react-dom/hooks/useFormStatus). -### Optimistically updating form data {/*optimistically-updating-form-data*/} -The `useOptimistic` Hook provides a way to optimistically update the user interface before a background operation, like a network request, completes. In the context of forms, this technique helps to make apps feel more responsive. When a user submits a form, instead of waiting for the server's response to reflect the changes, the interface is immediately updated with the expected outcome. +### Cập nhật dữ liệu form một cách lạc quan {/*optimistically-updating-form-data*/} +Hook `useOptimistic` cung cấp một cách để cập nhật giao diện người dùng một cách lạc quan trước khi một hoạt động nền, như một yêu cầu mạng, hoàn thành. Trong ngữ cảnh của form, kỹ thuật này giúp làm cho ứng dụng có cảm giác phản hồi nhanh hơn. Khi người dùng gửi một form, thay vì chờ phản hồi của máy chủ để phản ánh các thay đổi, giao diện được cập nhật ngay lập tức với kết quả dự kiến. -For example, when a user types a message into the form and hits the "Send" button, the `useOptimistic` Hook allows the message to immediately appear in the list with a "Sending..." label, even before the message is actually sent to a server. This "optimistic" approach gives the impression of speed and responsiveness. The form then attempts to truly send the message in the background. Once the server confirms the message has been received, the "Sending..." label is removed. +Ví dụ: khi người dùng nhập một tin nhắn vào form và nhấn nút "Gửi", Hook `useOptimistic` cho phép tin nhắn xuất hiện ngay lập tức trong danh sách với nhãn "Đang gửi...", ngay cả trước khi tin nhắn thực sự được gửi đến máy chủ. Cách tiếp cận "lạc quan" này tạo ấn tượng về tốc độ và khả năng phản hồi. Sau đó, form cố gắng thực sự gửi tin nhắn trong nền. Khi máy chủ xác nhận rằng tin nhắn đã được nhận, nhãn "Đang gửi..." sẽ bị xóa. <Sandpack> @@ -177,44 +178,44 @@ import { deliverMessage } from "./actions.js"; function Thread({ messages, sendMessage }) { const formRef = useRef(); async function formAction(formData) { - addOptimisticMessage(formData.get("message")); - formRef.current.reset(); - await sendMessage(formData); + addOptimisticMessage(formData.get("message")); + formRef.current.reset(); + await sendMessage(formData); } const [optimisticMessages, addOptimisticMessage] = useOptimistic( - messages, - (state, newMessage) => [ - ...state, - { - text: newMessage, - sending: true - } - ] + messages, + (state, newMessage) => [ + ...state, + { + text: newMessage, + sending: true + } + ] ); return ( - <> - {optimisticMessages.map((message, index) => ( - <div key={index}> - {message.text} - {!!message.sending && <small> (Sending...)</small>} - </div> - ))} - <form action={formAction} ref={formRef}> - <input type="text" name="message" placeholder="Hello!" /> - <button type="submit">Send</button> - </form> - </> + <> + {optimisticMessages.map((message, index) => ( + <div key={index}> + {message.text} + {!!message.sending && <small> (Đang gửi...)</small>} + </div> + ))} + <form action={formAction} ref={formRef}> + <input type="text" name="message" placeholder="Xin chào!" /> + <button type="submit">Gửi</button> + </form> + </> ); } export default function App() { const [messages, setMessages] = useState([ - { text: "Hello there!", sending: false, key: 1 } + { text: "Xin chào!", sending: false, key: 1 } ]); async function sendMessage(formData) { - const sentMessage = await deliverMessage(formData.get("message")); - setMessages((messages) => [...messages, { text: sentMessage }]); + const sentMessage = await deliverMessage(formData.get("message")); + setMessages((messages) => [...messages, { text: sentMessage }]); } return <Thread messages={messages} sendMessage={sendMessage} />; } @@ -232,9 +233,9 @@ export async function deliverMessage(message) { [//]: # 'Uncomment the next line, and delete this line after the `useOptimistic` reference documentatino page is published' [//]: # 'To learn more about the `useOptimistic` Hook see the [reference documentation](/reference/react/hooks/useOptimistic).' -### Handling form submission errors {/*handling-form-submission-errors*/} +### Xử lý lỗi gửi form {/*handling-form-submission-errors*/} -In some cases the function called by a `<form>`'s `action` prop throws an error. You can handle these errors by wrapping `<form>` in an Error Boundary. If the function called by a `<form>`'s `action` prop throws an error, the fallback for the error boundary will be displayed. +Trong một số trường hợp, hàm được gọi bởi prop `action` của `<form>` sẽ ném ra một lỗi. Bạn có thể xử lý các lỗi này bằng cách bọc `<form>` trong một Error Boundary. Nếu hàm được gọi bởi prop `action` của `<form>` ném ra một lỗi, fallback cho error boundary sẽ được hiển thị. <Sandpack> @@ -243,17 +244,17 @@ import { ErrorBoundary } from "react-error-boundary"; export default function Search() { function search() { - throw new Error("search error"); + throw new Error("lỗi tìm kiếm"); } return ( - <ErrorBoundary - fallback={<p>There was an error while submitting the form</p>} - > - <form action={search}> - <input name="query" /> - <button type="submit">Search</button> - </form> - </ErrorBoundary> + <ErrorBoundary + fallback={<p>Đã xảy ra lỗi khi gửi form</p>} + > + <form action={search}> + <input name="query" /> + <button type="submit">Tìm kiếm</button> + </form> + </ErrorBoundary> ); } @@ -262,10 +263,10 @@ export default function Search() { ```json package.json hidden { "dependencies": { - "react": "19.0.0-rc-3edc000d-20240926", - "react-dom": "19.0.0-rc-3edc000d-20240926", - "react-scripts": "^5.0.0", - "react-error-boundary": "4.0.3" + "react": "19.0.0-rc-3edc000d-20240926", + "react-dom": "19.0.0-rc-3edc000d-20240926", + "react-scripts": "^5.0.0", + "react-error-boundary": "4.0.3" }, "main": "/index.js", "devDependencies": {} @@ -274,15 +275,15 @@ export default function Search() { </Sandpack> -### Display a form submission error without JavaScript {/*display-a-form-submission-error-without-javascript*/} +### Hiển thị lỗi gửi form mà không cần JavaScript {/*display-a-form-submission-error-without-javascript*/} -Displaying a form submission error message before the JavaScript bundle loads for progressive enhancement requires that: +Để hiển thị thông báo lỗi gửi form trước khi bundle JavaScript tải cho progressive enhancement, cần phải: -1. `<form>` be rendered by a [Server Component](/reference/rsc/use-client) -1. the function passed to the `<form>`'s `action` prop be a [Server Function](/reference/rsc/server-functions) -1. the `useActionState` Hook be used to display the error message +1. `<form>` được render bởi một [Server Component](/reference/rsc/use-client) +2. hàm được truyền cho prop `action` của `<form>` là một [Server Function](/reference/rsc/server-functions) +3. Hook `useActionState` được sử dụng để hiển thị thông báo lỗi -`useActionState` takes two parameters: a [Server Function](/reference/rsc/server-functions) and an initial state. `useActionState` returns two values, a state variable and an action. The action returned by `useActionState` should be passed to the `action` prop of the form. The state variable returned by `useActionState` can be used to display an error message. The value returned by the Server Function passed to `useActionState` will be used to update the state variable. +`useActionState` nhận hai tham số: một [Server Function](/reference/rsc/server-functions) và một trạng thái ban đầu. `useActionState` trả về hai giá trị, một biến trạng thái và một action. Action được trả về bởi `useActionState` nên được truyền cho prop `action` của form. Biến trạng thái được trả về bởi `useActionState` có thể được sử dụng để hiển thị thông báo lỗi. Giá trị được trả về bởi Server Function được truyền cho `useActionState` sẽ được sử dụng để cập nhật biến trạng thái. <Sandpack> @@ -292,27 +293,27 @@ import { signUpNewUser } from "./api"; export default function Page() { async function signup(prevState, formData) { - "use server"; - const email = formData.get("email"); - try { - await signUpNewUser(email); - alert(`Added "${email}"`); - } catch (err) { - return err.toString(); - } + "use server"; + const email = formData.get("email"); + try { + await signUpNewUser(email); + alert(`Đã thêm "${email}"`); + } catch (err) { + return err.toString(); + } } const [message, signupAction] = useActionState(signup, null); return ( - <> - <h1>Signup for my newsletter</h1> - <p>Signup with the same email twice to see an error</p> - <form action={signupAction} id="signup-form"> - <label htmlFor="email">Email: </label> - <input name="email" id="email" placeholder="react@example.com" /> - <button>Sign up</button> - {!!message && <p>{message}</p>} - </form> - </> + <> + <h1>Đăng ký nhận bản tin của tôi</h1> + <p>Đăng ký với cùng một email hai lần để xem lỗi</p> + <form action={signupAction} id="signup-form"> + <label htmlFor="email">Email: </label> + <input name="email" id="email" placeholder="react@example.com" /> + <button>Đăng ký</button> + {!!message && <p>{message}</p>} + </form> + </> ); } ``` @@ -322,7 +323,7 @@ let emails = []; export async function signUpNewUser(newEmail) { if (emails.includes(newEmail)) { - throw new Error("This email address has already been added"); + throw new Error("Địa chỉ email này đã được thêm"); } emails.push(newEmail); } @@ -330,36 +331,36 @@ export async function signUpNewUser(newEmail) { </Sandpack> -Learn more about updating state from a form action with the [`useActionState`](/reference/react/useActionState) docs +Tìm hiểu thêm về cách cập nhật trạng thái từ một form action với tài liệu [`useActionState`](/reference/react/useActionState) -### Handling multiple submission types {/*handling-multiple-submission-types*/} +### Xử lý nhiều loại gửi {/*handling-multiple-submission-types*/} -Forms can be designed to handle multiple submission actions based on the button pressed by the user. Each button inside a form can be associated with a distinct action or behavior by setting the `formAction` prop. +Form có thể được thiết kế để xử lý nhiều action gửi khác nhau dựa trên nút được người dùng nhấn. Mỗi nút bên trong một form có thể được liên kết với một action hoặc hành vi riêng biệt bằng cách đặt prop `formAction`. -When a user taps a specific button, the form is submitted, and a corresponding action, defined by that button's attributes and action, is executed. For instance, a form might submit an article for review by default but have a separate button with `formAction` set to save the article as a draft. +Khi người dùng nhấn vào một nút cụ thể, form sẽ được gửi và một action tương ứng, được xác định bởi các thuộc tính và action của nút đó, sẽ được thực thi. Ví dụ: một form có thể gửi một bài viết để xem xét theo mặc định nhưng có một nút riêng biệt với `formAction` được đặt để lưu bài viết dưới dạng bản nháp. <Sandpack> ```js src/App.js export default function Search() { function publish(formData) { - const content = formData.get("content"); - const button = formData.get("button"); - alert(`'${content}' was published with the '${button}' button`); + const content = formData.get("content"); + const button = formData.get("button"); + alert(`'${content}' đã được xuất bản bằng nút '${button}'`); } function save(formData) { - const content = formData.get("content"); - alert(`Your draft of '${content}' has been saved!`); + const content = formData.get("content"); + alert(`Bản nháp của bạn về '${content}' đã được lưu!`); } return ( - <form action={publish}> - <textarea name="content" rows={4} cols={40} /> - <br /> - <button type="submit" name="button" value="submit">Publish</button> - <button formAction={save}>Save draft</button> - </form> + <form action={publish}> + <textarea name="content" rows={4} cols={40} /> + <br /> + <button type="submit" name="button" value="submit">Xuất bản</button> + <button formAction={save}>Lưu bản nháp</button> + </form> ); } ``` diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index ec2e1d2ee..1238be8fe 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -1,40 +1,40 @@ --- -title: "React DOM Components" +title: "Các thành phần React DOM" --- <Intro> -React supports all of the browser built-in [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) and [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Element) components. +React hỗ trợ tất cả các thành phần [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) và [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Element) được tích hợp sẵn của trình duyệt. </Intro> --- -## Common components {/*common-components*/} +## Các thành phần phổ biến {/*common-components*/} -All of the built-in browser components support some props and events. +Tất cả các thành phần trình duyệt tích hợp sẵn đều hỗ trợ một số thuộc tính và sự kiện. -* [Common components (e.g. `<div>`)](/reference/react-dom/components/common) +* [Các thành phần phổ biến (ví dụ: `<div>`)](/reference/react-dom/components/common) -This includes React-specific props like `ref` and `dangerouslySetInnerHTML`. +Điều này bao gồm các thuộc tính dành riêng cho React như `ref` và `dangerouslySetInnerHTML`. --- -## Form components {/*form-components*/} +## Các thành phần biểu mẫu {/*form-components*/} -These built-in browser components accept user input: +Các thành phần trình duyệt tích hợp sẵn này chấp nhận đầu vào của người dùng: * [`<input>`](/reference/react-dom/components/input) * [`<select>`](/reference/react-dom/components/select) * [`<textarea>`](/reference/react-dom/components/textarea) -They are special in React because passing the `value` prop to them makes them *[controlled.](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable)* +Chúng đặc biệt trong React vì việc truyền thuộc tính `value` cho chúng sẽ khiến chúng trở thành *[được kiểm soát.](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable)* --- -## Resource and Metadata Components {/*resource-and-metadata-components*/} +## Các thành phần Tài nguyên và Siêu dữ liệu {/*resource-and-metadata-components*/} -These built-in browser components let you load external resources or annotate the document with metadata: +Các thành phần trình duyệt tích hợp sẵn này cho phép bạn tải tài nguyên bên ngoài hoặc chú thích tài liệu bằng siêu dữ liệu: * [`<link>`](/reference/react-dom/components/link) * [`<meta>`](/reference/react-dom/components/meta) @@ -42,13 +42,13 @@ These built-in browser components let you load external resources or annotate th * [`<style>`](/reference/react-dom/components/style) * [`<title>`](/reference/react-dom/components/title) -They are special in React because React can render them into the document head, suspend while resources are loading, and enact other behaviors that are described on the reference page for each specific component. +Chúng đặc biệt trong React vì React có thể hiển thị chúng vào đầu tài liệu, tạm dừng trong khi tài nguyên đang tải và ban hành các hành vi khác được mô tả trên trang tham khảo cho từng thành phần cụ thể. --- -## All HTML components {/*all-html-components*/} +## Tất cả các thành phần HTML {/*all-html-components*/} -React supports all built-in browser HTML components. This includes: +React hỗ trợ tất cả các thành phần HTML trình duyệt tích hợp sẵn. Điều này bao gồm: * [`<aside>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside) * [`<audio>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio) @@ -154,38 +154,38 @@ React supports all built-in browser HTML components. This includes: <Note> -Similar to the [DOM standard,](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) React uses a `camelCase` convention for prop names. For example, you'll write `tabIndex` instead of `tabindex`. You can convert existing HTML to JSX with an [online converter.](https://transform.tools/html-to-jsx) +Tương tự như [tiêu chuẩn DOM,](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) React sử dụng quy ước `camelCase` cho tên thuộc tính. Ví dụ: bạn sẽ viết `tabIndex` thay vì `tabindex`. Bạn có thể chuyển đổi HTML hiện có sang JSX bằng một [công cụ chuyển đổi trực tuyến.](https://transform.tools/html-to-jsx) </Note> --- -### Custom HTML elements {/*custom-html-elements*/} +### Các phần tử HTML tùy chỉnh {/*custom-html-elements*/} -If you render a tag with a dash, like `<my-element>`, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) In React, rendering custom elements works differently from rendering built-in browser tags: +Nếu bạn hiển thị một thẻ có dấu gạch ngang, chẳng hạn như `<my-element>`, React sẽ cho rằng bạn muốn hiển thị một [phần tử HTML tùy chỉnh.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) Trong React, việc hiển thị các phần tử tùy chỉnh hoạt động khác với việc hiển thị các thẻ trình duyệt tích hợp sẵn: -- All custom element props are serialized to strings and are always set using attributes. -- Custom elements accept `class` rather than `className`, and `for` rather than `htmlFor`. +- Tất cả các thuộc tính của phần tử tùy chỉnh được tuần tự hóa thành chuỗi và luôn được đặt bằng các thuộc tính. +- Các phần tử tùy chỉnh chấp nhận `class` thay vì `className` và `for` thay vì `htmlFor`. -If you render a built-in browser HTML element with an [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is) attribute, it will also be treated as a custom element. +Nếu bạn hiển thị một phần tử HTML trình duyệt tích hợp sẵn với thuộc tính [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is), nó cũng sẽ được coi là một phần tử tùy chỉnh. <Note> -[A future version of React will include more comprehensive support for custom elements.](https://github.com/facebook/react/issues/11347#issuecomment-1122275286) +[Một phiên bản React trong tương lai sẽ bao gồm hỗ trợ toàn diện hơn cho các phần tử tùy chỉnh.](https://github.com/facebook/react/issues/11347#issuecomment-1122275286) -You can try it by upgrading React packages to the most recent experimental version: +Bạn có thể thử bằng cách nâng cấp các gói React lên phiên bản thử nghiệm mới nhất: - `react@experimental` - `react-dom@experimental` -Experimental versions of React may contain bugs. Don't use them in production. +Các phiên bản thử nghiệm của React có thể chứa lỗi. Không sử dụng chúng trong sản xuất. </Note> --- -## All SVG components {/*all-svg-components*/} +## Tất cả các thành phần SVG {/*all-svg-components*/} -React supports all built-in browser SVG components. This includes: +React hỗ trợ tất cả các thành phần SVG trình duyệt tích hợp sẵn. Điều này bao gồm: * [`<a>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/a) * [`<animate>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate) @@ -256,20 +256,20 @@ React supports all built-in browser SVG components. This includes: <Note> -Similar to the [DOM standard,](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) React uses a `camelCase` convention for prop names. For example, you'll write `tabIndex` instead of `tabindex`. You can convert existing SVG to JSX with an [online converter.](https://transform.tools/) - -Namespaced attributes also have to be written without the colon: - -* `xlink:actuate` becomes `xlinkActuate`. -* `xlink:arcrole` becomes `xlinkArcrole`. -* `xlink:href` becomes `xlinkHref`. -* `xlink:role` becomes `xlinkRole`. -* `xlink:show` becomes `xlinkShow`. -* `xlink:title` becomes `xlinkTitle`. -* `xlink:type` becomes `xlinkType`. -* `xml:base` becomes `xmlBase`. -* `xml:lang` becomes `xmlLang`. -* `xml:space` becomes `xmlSpace`. -* `xmlns:xlink` becomes `xmlnsXlink`. +Tương tự như [tiêu chuẩn DOM,](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) React sử dụng quy ước `camelCase` cho tên thuộc tính. Ví dụ: bạn sẽ viết `tabIndex` thay vì `tabindex`. Bạn có thể chuyển đổi SVG hiện có sang JSX bằng một [công cụ chuyển đổi trực tuyến.](https://transform.tools/) + +Các thuộc tính có không gian tên cũng phải được viết không có dấu hai chấm: + +* `xlink:actuate` trở thành `xlinkActuate`. +* `xlink:arcrole` trở thành `xlinkArcrole`. +* `xlink:href` trở thành `xlinkHref`. +* `xlink:role` trở thành `xlinkRole`. +* `xlink:show` trở thành `xlinkShow`. +* `xlink:title` trở thành `xlinkTitle`. +* `xlink:type` trở thành `xlinkType`. +* `xml:base` trở thành `xmlBase`. +* `xml:lang` trở thành `xmlLang`. +* `xml:space` trở thành `xmlSpace`. +* `xmlns:xlink` trở thành `xmlnsXlink`. </Note> diff --git a/src/content/reference/react-dom/components/input.md b/src/content/reference/react-dom/components/input.md index b6214249d..f9cf04ab8 100644 --- a/src/content/reference/react-dom/components/input.md +++ b/src/content/reference/react-dom/components/input.md @@ -4,7 +4,7 @@ title: "<input>" <Intro> -The [built-in browser `<input>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) lets you render different kinds of form inputs. +[Component `<input>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) cho phép bạn render các loại input form khác nhau. ```js <input /> @@ -16,94 +16,94 @@ The [built-in browser `<input>` component](https://developer.mozilla.org/en-US/d --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<input>` {/*input*/} -To display an input, render the [built-in browser `<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) component. +Để hiển thị một input, render component `<input>` tích hợp sẵn của trình duyệt. ```js <input name="myInput" /> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<input>` supports all [common element props.](/reference/react-dom/components/common#props) - -- [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): A string or function. Overrides the parent `<form action>` for `type="submit"` and `type="image"`. When a URL is passed to `action` the form will behave like a standard HTML form. When a function is passed to `formAction` the function will handle the form submission. See [`<form action>`](/reference/react-dom/components/form#props). - -You can [make an input controlled](#controlling-an-input-with-a-state-variable) by passing one of these props: - -* [`checked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#checked): A boolean. For a checkbox input or a radio button, controls whether it is selected. -* [`value`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#value): A string. For a text input, controls its text. (For a radio button, specifies its form data.) - -When you pass either of them, you must also pass an `onChange` handler that updates the passed value. - -These `<input>` props are only relevant for uncontrolled inputs: - -* [`defaultChecked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultChecked): A boolean. Specifies [the initial value](#providing-an-initial-value-for-an-input) for `type="checkbox"` and `type="radio"` inputs. -* [`defaultValue`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultValue): A string. Specifies [the initial value](#providing-an-initial-value-for-an-input) for a text input. - -These `<input>` props are relevant both for uncontrolled and controlled inputs: - -* [`accept`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#accept): A string. Specifies which filetypes are accepted by a `type="file"` input. -* [`alt`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#alt): A string. Specifies the alternative image text for a `type="image"` input. -* [`capture`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#capture): A string. Specifies the media (microphone, video, or camera) captured by a `type="file"` input. -* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autocomplete): A string. Specifies one of the possible [autocomplete behaviors.](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) -* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autofocus): A boolean. If `true`, React will focus the element on mount. -* [`dirname`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#dirname): A string. Specifies the form field name for the element's directionality. -* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#disabled): A boolean. If `true`, the input will not be interactive and will appear dimmed. -* `children`: `<input>` does not accept children. -* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form): A string. Specifies the `id` of the `<form>` this input belongs to. If omitted, it's the closest parent form. -* [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): A string. Overrides the parent `<form action>` for `type="submit"` and `type="image"`. -* [`formEnctype`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formenctype): A string. Overrides the parent `<form enctype>` for `type="submit"` and `type="image"`. -* [`formMethod`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formmethod): A string. Overrides the parent `<form method>` for `type="submit"` and `type="image"`. -* [`formNoValidate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formnovalidate): A string. Overrides the parent `<form noValidate>` for `type="submit"` and `type="image"`. -* [`formTarget`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formtarget): A string. Overrides the parent `<form target>` for `type="submit"` and `type="image"`. -* [`height`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#height): A string. Specifies the image height for `type="image"`. -* [`list`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#list): A string. Specifies the `id` of the `<datalist>` with the autocomplete options. -* [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#max): A number. Specifies the maximum value of numerical and datetime inputs. -* [`maxLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#maxlength): A number. Specifies the maximum length of text and other inputs. -* [`min`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#min): A number. Specifies the minimum value of numerical and datetime inputs. -* [`minLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#minlength): A number. Specifies the minimum length of text and other inputs. -* [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#multiple): A boolean. Specifies whether multiple values are allowed for `<type="file"` and `type="email"`. -* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name): A string. Specifies the name for this input that's [submitted with the form.](#reading-the-input-values-when-submitting-a-form) -* `onChange`: An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Required for [controlled inputs.](#controlling-an-input-with-a-state-variable) Fires immediately when the input's value is changed by the user (for example, it fires on every keystroke). Behaves like the browser [`input` event.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) -* `onChangeCapture`: A version of `onChange` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires immediately when the value is changed by the user. For historical reasons, in React it is idiomatic to use `onChange` instead which works similarly. -* `onInputCapture`: A version of `onInput` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires if an input fails validation on form submit. Unlike the built-in `invalid` event, the React `onInvalid` event bubbles. -* `onInvalidCapture`: A version of `onInvalid` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires after the selection inside the `<input>` changes. React extends the `onSelect` event to also fire for empty selection and on edits (which may affect the selection). -* `onSelectCapture`: A version of `onSelect` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`pattern`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#pattern): A string. Specifies the pattern that the `value` must match. -* [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder): A string. Displayed in a dimmed color when the input value is empty. -* [`readOnly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly): A boolean. If `true`, the input is not editable by the user. -* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#required): A boolean. If `true`, the value must be provided for the form to submit. -* [`size`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#size): A number. Similar to setting width, but the unit depends on the control. -* [`src`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#src): A string. Specifies the image source for a `type="image"` input. -* [`step`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#step): A positive number or an `'any'` string. Specifies the distance between valid values. -* [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#type): A string. One of the [input types.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) -* [`width`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#width): A string. Specifies the image width for a `type="image"` input. - -#### Caveats {/*caveats*/} - -- Checkboxes need `checked` (or `defaultChecked`), not `value` (or `defaultValue`). -- If a text input receives a string `value` prop, it will be [treated as controlled.](#controlling-an-input-with-a-state-variable) -- If a checkbox or a radio button receives a boolean `checked` prop, it will be [treated as controlled.](#controlling-an-input-with-a-state-variable) -- An input can't be both controlled and uncontrolled at the same time. -- An input cannot switch between being controlled or uncontrolled over its lifetime. -- Every controlled input needs an `onChange` event handler that synchronously updates its backing value. +`<input>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) + +- [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): Một chuỗi hoặc một hàm. Ghi đè `<form action>` của phần tử cha cho `type="submit"` và `type="image"`. Khi một URL được truyền cho `action`, form sẽ hoạt động như một form HTML tiêu chuẩn. Khi một hàm được truyền cho `formAction`, hàm đó sẽ xử lý việc gửi form. Xem [`<form action>`](/reference/react-dom/components/form#props). + +Bạn có thể [làm cho một input được kiểm soát](#controlling-an-input-with-a-state-variable) bằng cách truyền một trong các props sau: + +* [`checked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#checked): Một boolean. Đối với một input checkbox hoặc một radio button, kiểm soát xem nó có được chọn hay không. +* [`value`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#value): Một chuỗi. Đối với một input văn bản, kiểm soát văn bản của nó. (Đối với một radio button, chỉ định dữ liệu form của nó.) + +Khi bạn truyền một trong hai prop này, bạn cũng phải truyền một trình xử lý `onChange` để cập nhật giá trị đã truyền. + +Các props `<input>` này chỉ liên quan đến các input không được kiểm soát: + +* [`defaultChecked`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultChecked): Một boolean. Chỉ định [giá trị ban đầu](#providing-an-initial-value-for-an-input) cho các input `type="checkbox"` và `type="radio"`. +* [`defaultValue`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultValue): Một chuỗi. Chỉ định [giá trị ban đầu](#providing-an-initial-value-for-an-input) cho một input văn bản. + +Các props `<input>` này có liên quan đến cả input được kiểm soát và không được kiểm soát: + +* [`accept`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#accept): Một chuỗi. Chỉ định loại file nào được chấp nhận bởi một input `type="file"`. +* [`alt`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#alt): Một chuỗi. Chỉ định văn bản thay thế hình ảnh cho một input `type="image"`. +* [`capture`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#capture): Một chuỗi. Chỉ định phương tiện (microphone, video hoặc camera) được chụp bởi một input `type="file"`. +* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autocomplete): Một chuỗi. Chỉ định một trong các [hành vi autocomplete có thể.](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) +* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#autofocus): Một boolean. Nếu `true`, React sẽ focus phần tử khi mount. +* [`dirname`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#dirname): Một chuỗi. Chỉ định tên trường form cho hướng của phần tử. +* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#disabled): Một boolean. Nếu `true`, input sẽ không tương tác được và sẽ xuất hiện màu xám. +* `children`: `<input>` không chấp nhận children. +* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form): Một chuỗi. Chỉ định `id` của `<form>` mà input này thuộc về. Nếu bị bỏ qua, nó là form cha gần nhất. +* [`formAction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formaction): Một chuỗi. Ghi đè `<form action>` của phần tử cha cho `type="submit"` và `type="image"`. +* [`formEnctype`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formenctype): Một chuỗi. Ghi đè `<form enctype>` của phần tử cha cho `type="submit"` và `type="image"`. +* [`formMethod`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formmethod): Một chuỗi. Ghi đè `<form method>` của phần tử cha cho `type="submit"` và `type="image"`. +* [`formNoValidate`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formnovalidate): Một chuỗi. Ghi đè `<form noValidate>` của phần tử cha cho `type="submit"` và `type="image"`. +* [`formTarget`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#formtarget): Một chuỗi. Ghi đè `<form target>` của phần tử cha cho `type="submit"` và `type="image"`. +* [`height`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#height): Một chuỗi. Chỉ định chiều cao hình ảnh cho `type="image"`. +* [`list`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#list): Một chuỗi. Chỉ định `id` của `<datalist>` với các tùy chọn autocomplete. +* [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#max): Một số. Chỉ định giá trị tối đa của các input số và ngày giờ. +* [`maxLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#maxlength): Một số. Chỉ định độ dài tối đa của văn bản và các input khác. +* [`min`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#min): Một số. Chỉ định giá trị tối thiểu của các input số và ngày giờ. +* [`minLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#minlength): Một số. Chỉ định độ dài tối thiểu của văn bản và các input khác. +* [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#multiple): Một boolean. Chỉ định xem nhiều giá trị có được phép cho `<type="file"` và `type="email"` hay không. +* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name): Một chuỗi. Chỉ định tên cho input này được [gửi cùng với form.](#reading-the-input-values-when-submitting-a-form) +* `onChange`: Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Bắt buộc đối với [các input được kiểm soát.](#controlling-an-input-with-a-state-variable) Kích hoạt ngay lập tức khi giá trị của input bị thay đổi bởi người dùng (ví dụ: nó kích hoạt trên mỗi lần gõ phím). Hoạt động giống như sự kiện [`input` của trình duyệt.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) +* `onChangeCapture`: Một phiên bản của `onChange` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt ngay lập tức khi giá trị bị thay đổi bởi người dùng. Vì lý do lịch sử, trong React, thành ngữ là sử dụng `onChange` thay thế, hoạt động tương tự. +* `onInputCapture`: Một phiên bản của `onInput` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt nếu một input không vượt qua xác thực khi gửi form. Không giống như sự kiện `invalid` tích hợp, sự kiện `onInvalid` của React nổi bọt. +* `onInvalidCapture`: Một phiên bản của `onInvalid` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt sau khi lựa chọn bên trong `<input>` thay đổi. React mở rộng sự kiện `onSelect` để cũng kích hoạt cho lựa chọn trống và khi chỉnh sửa (có thể ảnh hưởng đến lựa chọn). +* `onSelectCapture`: Một phiên bản của `onSelect` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`pattern`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#pattern): Một chuỗi. Chỉ định pattern mà `value` phải khớp. +* [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#placeholder): Một chuỗi. Được hiển thị bằng màu mờ khi giá trị input trống. +* [`readOnly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly): Một boolean. Nếu `true`, input không thể chỉnh sửa được bởi người dùng. +* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#required): Một boolean. Nếu `true`, giá trị phải được cung cấp để form được gửi. +* [`size`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#size): Một số. Tương tự như cài đặt width, nhưng đơn vị phụ thuộc vào control. +* [`src`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#src): Một chuỗi. Chỉ định nguồn hình ảnh cho một input `type="image"`. +* [`step`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#step): Một số dương hoặc một chuỗi `'any'`. Chỉ định khoảng cách giữa các giá trị hợp lệ. +* [`type`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#type): Một chuỗi. Một trong các [loại input.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) +* [`width`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#width): Một chuỗi. Chỉ định chiều rộng hình ảnh cho một input `type="image"`. + +#### Lưu ý {/*caveats*/} + +- Checkbox cần `checked` (hoặc `defaultChecked`), không phải `value` (hoặc `defaultValue`). +- Nếu một input văn bản nhận được một prop `value` kiểu chuỗi, nó sẽ được [xử lý như được kiểm soát.](#controlling-an-input-with-a-state-variable) +- Nếu một checkbox hoặc một radio button nhận được một prop `checked` kiểu boolean, nó sẽ được [xử lý như được kiểm soát.](#controlling-an-input-with-a-state-variable) +- Một input không thể vừa được kiểm soát vừa không được kiểm soát cùng một lúc. +- Một input không thể chuyển đổi giữa việc được kiểm soát hoặc không được kiểm soát trong suốt vòng đời của nó. +- Mọi input được kiểm soát cần một trình xử lý sự kiện `onChange` để đồng bộ cập nhật giá trị backing của nó. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Displaying inputs of different types {/*displaying-inputs-of-different-types*/} +### Hiển thị các input thuộc các loại khác nhau {/*displaying-inputs-of-different-types*/} -To display an input, render an `<input>` component. By default, it will be a text input. You can pass `type="checkbox"` for a checkbox, `type="radio"` for a radio button, [or one of the other input types.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) +Để hiển thị một input, render một component `<input>`. Theo mặc định, nó sẽ là một input văn bản. Bạn có thể truyền `type="checkbox"` cho một checkbox, `type="radio"` cho một radio button, [hoặc một trong các loại input khác.](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types) <Sandpack> @@ -148,11 +148,11 @@ input { margin: 5px; } --- -### Providing a label for an input {/*providing-a-label-for-an-input*/} +### Cung cấp một label cho một input {/*providing-a-label-for-an-input*/} -Typically, you will place every `<input>` inside a [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) tag. This tells the browser that this label is associated with that input. When the user clicks the label, the browser will automatically focus the input. It's also essential for accessibility: a screen reader will announce the label caption when the user focuses the associated input. +Thông thường, bạn sẽ đặt mọi `<input>` bên trong một thẻ [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label). Điều này cho trình duyệt biết rằng label này được liên kết với input đó. Khi người dùng nhấp vào label, trình duyệt sẽ tự động focus vào input. Nó cũng rất cần thiết cho khả năng truy cập: một trình đọc màn hình sẽ thông báo caption của label khi người dùng focus vào input được liên kết. -If you can't nest `<input>` into a `<label>`, associate them by passing the same ID to `<input id>` and [`<label htmlFor>`.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) To avoid conflicts between multiple instances of one component, generate such an ID with [`useId`.](/reference/react/useId) +Nếu bạn không thể lồng `<input>` vào một `<label>`, hãy liên kết chúng bằng cách truyền cùng một ID cho `<input id>` và [`<label htmlFor>`.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) Để tránh xung đột giữa nhiều instance của một component, hãy tạo một ID như vậy bằng [`useId`.](/reference/react/useId) <Sandpack> @@ -183,9 +183,9 @@ input { margin: 5px; } --- -### Providing an initial value for an input {/*providing-an-initial-value-for-an-input*/} +### Cung cấp một giá trị ban đầu cho một input {/*providing-an-initial-value-for-an-input*/} -You can optionally specify the initial value for any input. Pass it as the `defaultValue` string for text inputs. Checkboxes and radio buttons should specify the initial value with the `defaultChecked` boolean instead. +Bạn có thể tùy chọn chỉ định giá trị ban đầu cho bất kỳ input nào. Truyền nó dưới dạng chuỗi `defaultValue` cho các input văn bản. Checkbox và radio button nên chỉ định giá trị ban đầu bằng boolean `defaultChecked` thay thế. <Sandpack> @@ -235,25 +235,25 @@ input { margin: 5px; } --- -### Reading the input values when submitting a form {/*reading-the-input-values-when-submitting-a-form*/} +### Đọc các giá trị input khi gửi form {/*reading-the-input-values-when-submitting-a-form*/} -Add a [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) around your inputs with a [`<button type="submit">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) inside. It will call your `<form onSubmit>` event handler. By default, the browser will send the form data to the current URL and refresh the page. You can override that behavior by calling `e.preventDefault()`. Read the form data with [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). +Thêm một [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) xung quanh các input của bạn với một [`<button type="submit">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) bên trong. Nó sẽ gọi trình xử lý sự kiện `<form onSubmit>` của bạn. Theo mặc định, trình duyệt sẽ gửi dữ liệu form đến URL hiện tại và làm mới trang. Bạn có thể ghi đè hành vi đó bằng cách gọi `e.preventDefault()`. Đọc dữ liệu form bằng [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). <Sandpack> ```js export default function MyForm() { function handleSubmit(e) { - // Prevent the browser from reloading the page + // Ngăn trình duyệt tải lại trang e.preventDefault(); - // Read the form data + // Đọc dữ liệu form const form = e.target; const formData = new FormData(form); - // You can pass formData as a fetch body directly: + // Bạn có thể truyền formData làm body fetch trực tiếp: fetch('/some-api', { method: form.method, body: formData }); - // Or you can work with it as a plain object: + // Hoặc bạn có thể làm việc với nó như một đối tượng thuần túy: const formJson = Object.fromEntries(formData.entries()); console.log(formJson); } @@ -291,38 +291,38 @@ input { margin: 5px; } <Note> -Give a `name` to every `<input>`, for example `<input name="firstName" defaultValue="Taylor" />`. The `name` you specified will be used as a key in the form data, for example `{ firstName: "Taylor" }`. +Đặt một `name` cho mỗi `<input>`, ví dụ: `<input name="firstName" defaultValue="Taylor" />`. `name` bạn đã chỉ định sẽ được sử dụng làm key trong dữ liệu form, ví dụ: `{ firstName: "Taylor" }`. </Note> <Pitfall> -By default, a `<button>` inside a `<form>` without a `type` attribute will submit it. This can be surprising! If you have your own custom `Button` React component, consider using [`<button type="button">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) instead of `<button>` (with no type). Then, to be explicit, use `<button type="submit">` for buttons that *are* supposed to submit the form. +Theo mặc định, một `<button>` bên trong một `<form>` mà không có thuộc tính `type` sẽ gửi nó. Điều này có thể gây ngạc nhiên! Nếu bạn có component React `Button` tùy chỉnh của riêng mình, hãy cân nhắc sử dụng [`<button type="button">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) thay vì `<button>` (không có type). Sau đó, để rõ ràng, hãy sử dụng `<button type="submit">` cho các button *có* nhiệm vụ gửi form. </Pitfall> --- -### Controlling an input with a state variable {/*controlling-an-input-with-a-state-variable*/} +### Kiểm soát một input bằng một biến trạng thái {/*controlling-an-input-with-a-state-variable*/} -An input like `<input />` is *uncontrolled.* Even if you [pass an initial value](#providing-an-initial-value-for-an-input) like `<input defaultValue="Initial text" />`, your JSX only specifies the initial value. It does not control what the value should be right now. +Một input như `<input />` là *không được kiểm soát.* Ngay cả khi bạn [truyền một giá trị ban đầu](#providing-an-initial-value-for-an-input) như `<input defaultValue="Initial text" />`, JSX của bạn chỉ chỉ định giá trị ban đầu. Nó không kiểm soát giá trị nên là gì ngay bây giờ. -**To render a _controlled_ input, pass the `value` prop to it (or `checked` for checkboxes and radios).** React will force the input to always have the `value` you passed. Usually, you would do this by declaring a [state variable:](/reference/react/useState) +**Để render một input _được kiểm soát_, hãy truyền prop `value` cho nó (hoặc `checked` cho checkbox và radio).** React sẽ buộc input luôn có `value` mà bạn đã truyền. Thông thường, bạn sẽ làm điều này bằng cách khai báo một [biến trạng thái:](/reference/react/useState) ```js {2,6,7} function Form() { - const [firstName, setFirstName] = useState(''); // Declare a state variable... + const [firstName, setFirstName] = useState(''); // Khai báo một biến trạng thái... // ... return ( <input - value={firstName} // ...force the input's value to match the state variable... - onChange={e => setFirstName(e.target.value)} // ... and update the state variable on any edits! + value={firstName} // ...buộc giá trị của input khớp với biến trạng thái... + onChange={e => setFirstName(e.target.value)} // ...và cập nhật biến trạng thái trên mọi chỉnh sửa! /> ); } ``` -A controlled input makes sense if you needed state anyway--for example, to re-render your UI on every edit: +Một input được kiểm soát có ý nghĩa nếu bạn cần trạng thái anyway--ví dụ: để re-render UI của bạn trên mọi chỉnh sửa: ```js {2,9} function Form() { @@ -337,7 +337,7 @@ function Form() { ... ``` -It's also useful if you want to offer multiple ways to adjust the input state (for example, by clicking a button): +Nó cũng hữu ích nếu bạn muốn cung cấp nhiều cách để điều chỉnh trạng thái input (ví dụ: bằng cách nhấp vào một button): ```js {3-4,10-11,14} function Form() { @@ -358,7 +358,7 @@ function Form() { </button> ``` -The `value` you pass to controlled components should not be `undefined` or `null`. If you need the initial value to be empty (such as with the `firstName` field below), initialize your state variable to an empty string (`''`). +`value` bạn truyền cho các component được kiểm soát không được là `undefined` hoặc `null`. Nếu bạn cần giá trị ban đầu là trống (chẳng hạn như với trường `firstName` bên dưới), hãy khởi tạo biến trạng thái của bạn thành một chuỗi trống (`''`). <Sandpack> @@ -410,17 +410,17 @@ p { font-weight: bold; } <Pitfall> -**If you pass `value` without `onChange`, it will be impossible to type into the input.** When you control an input by passing some `value` to it, you *force* it to always have the value you passed. So if you pass a state variable as a `value` but forget to update that state variable synchronously during the `onChange` event handler, React will revert the input after every keystroke back to the `value` that you specified. +**Nếu bạn truyền `value` mà không có `onChange`, sẽ không thể nhập vào input.** Khi bạn kiểm soát một input bằng cách truyền một số `value` cho nó, bạn *buộc* nó luôn có giá trị bạn đã truyền. Vì vậy, nếu bạn truyền một biến trạng thái làm `value` nhưng quên cập nhật biến trạng thái đó một cách đồng bộ trong trình xử lý sự kiện `onChange`, React sẽ hoàn nguyên input sau mỗi lần gõ phím trở lại `value` mà bạn đã chỉ định. </Pitfall> --- -### Optimizing re-rendering on every keystroke {/*optimizing-re-rendering-on-every-keystroke*/} +### Tối ưu hóa re-rendering trên mỗi lần gõ phím {/*optimizing-re-rendering-on-every-keystroke*/} -When you use a controlled input, you set the state on every keystroke. If the component containing your state re-renders a large tree, this can get slow. There's a few ways you can optimize re-rendering performance. +Khi bạn sử dụng một input được kiểm soát, bạn đặt trạng thái trên mỗi lần gõ phím. Nếu component chứa trạng thái của bạn re-render một cây lớn, điều này có thể trở nên chậm. Có một vài cách bạn có thể tối ưu hóa hiệu suất re-rendering. -For example, suppose you start with a form that re-renders all page content on every keystroke: +Ví dụ: giả sử bạn bắt đầu với một form re-render tất cả nội dung trang trên mỗi lần gõ phím: ```js {5-8} function App() { @@ -436,7 +436,7 @@ function App() { } ``` -Since `<PageContent />` doesn't rely on the input state, you can move the input state into its own component: +Vì `<PageContent />` không dựa vào trạng thái input, bạn có thể di chuyển trạng thái input vào component riêng của nó: ```js {4,10-17} function App() { @@ -458,140 +458,140 @@ function SignupForm() { } ``` -This significantly improves performance because now only `SignupForm` re-renders on every keystroke. +Điều này cải thiện đáng kể hiệu suất vì bây giờ chỉ `SignupForm` re-render trên mỗi lần gõ phím. -If there is no way to avoid re-rendering (for example, if `PageContent` depends on the search input's value), [`useDeferredValue`](/reference/react/useDeferredValue#deferring-re-rendering-for-a-part-of-the-ui) lets you keep the controlled input responsive even in the middle of a large re-render. +Nếu không có cách nào để tránh re-rendering (ví dụ: nếu `PageContent` phụ thuộc vào giá trị của input tìm kiếm), [`useDeferredValue`](/reference/react/useDeferredValue#deferring-re-rendering-for-a-part-of-the-ui) cho phép bạn giữ cho input được kiểm soát phản hồi ngay cả ở giữa một re-render lớn. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My text input doesn't update when I type into it {/*my-text-input-doesnt-update-when-i-type-into-it*/} +### Input văn bản của tôi không cập nhật khi tôi nhập vào nó {/*my-text-input-doesnt-update-when-i-type-into-it*/} -If you render an input with `value` but no `onChange`, you will see an error in the console: +Nếu bạn render một input với `value` nhưng không có `onChange`, bạn sẽ thấy một lỗi trong console: ```js -// 🔴 Bug: controlled text input with no onChange handler +// 🔴 Lỗi: input văn bản được kiểm soát không có trình xử lý onChange <input value={something} /> ``` <ConsoleBlock level="error"> -You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`. +Bạn đã cung cấp một prop `value` cho một trường form mà không có trình xử lý `onChange`. Điều này sẽ render một trường chỉ đọc. Nếu trường nên có thể thay đổi, hãy sử dụng `defaultValue`. Nếu không, hãy đặt `onChange` hoặc `readOnly`. </ConsoleBlock> -As the error message suggests, if you only wanted to [specify the *initial* value,](#providing-an-initial-value-for-an-input) pass `defaultValue` instead: +Như thông báo lỗi gợi ý, nếu bạn chỉ muốn [chỉ định giá trị *ban đầu*,](#providing-an-initial-value-for-an-input) hãy truyền `defaultValue` thay thế: ```js -// ✅ Good: uncontrolled input with an initial value +// ✅ Tốt: input không được kiểm soát với một giá trị ban đầu <input defaultValue={something} /> ``` -If you want [to control this input with a state variable,](#controlling-an-input-with-a-state-variable) specify an `onChange` handler: +Nếu bạn muốn [kiểm soát input này bằng một biến trạng thái,](#controlling-an-input-with-a-state-variable) hãy chỉ định một trình xử lý `onChange`: ```js -// ✅ Good: controlled input with onChange +// ✅ Tốt: input được kiểm soát với onChange <input value={something} onChange={e => setSomething(e.target.value)} /> ``` -If the value is intentionally read-only, add a `readOnly` prop to suppress the error: +Nếu giá trị cố ý chỉ đọc, hãy thêm một prop `readOnly` để ngăn chặn lỗi: ```js -// ✅ Good: readonly controlled input without on change +// ✅ Tốt: input được kiểm soát chỉ đọc không có on change <input value={something} readOnly={true} /> ``` --- -### My checkbox doesn't update when I click on it {/*my-checkbox-doesnt-update-when-i-click-on-it*/} +### Checkbox của tôi không cập nhật khi tôi nhấp vào nó {/*my-checkbox-doesnt-update-when-i-click-on-it*/} -If you render a checkbox with `checked` but no `onChange`, you will see an error in the console: +Nếu bạn render một checkbox với `checked` nhưng không có `onChange`, bạn sẽ thấy một lỗi trong console: ```js -// 🔴 Bug: controlled checkbox with no onChange handler +// 🔴 Lỗi: checkbox được kiểm soát không có trình xử lý onChange <input type="checkbox" checked={something} /> ``` <ConsoleBlock level="error"> -You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`. +Bạn đã cung cấp một prop `checked` cho một trường form mà không có trình xử lý `onChange`. Điều này sẽ render một trường chỉ đọc. Nếu trường nên có thể thay đổi, hãy sử dụng `defaultChecked`. Nếu không, hãy đặt `onChange` hoặc `readOnly`. </ConsoleBlock> -As the error message suggests, if you only wanted to [specify the *initial* value,](#providing-an-initial-value-for-an-input) pass `defaultChecked` instead: +Như thông báo lỗi gợi ý, nếu bạn chỉ muốn [chỉ định giá trị *ban đầu*,](#providing-an-initial-value-for-an-input) hãy truyền `defaultChecked` thay thế: ```js -// ✅ Good: uncontrolled checkbox with an initial value +// ✅ Tốt: checkbox không được kiểm soát với một giá trị ban đầu <input type="checkbox" defaultChecked={something} /> ``` -If you want [to control this checkbox with a state variable,](#controlling-an-input-with-a-state-variable) specify an `onChange` handler: +Nếu bạn muốn [kiểm soát checkbox này bằng một biến trạng thái,](#controlling-an-input-with-a-state-variable) hãy chỉ định một trình xử lý `onChange`: ```js -// ✅ Good: controlled checkbox with onChange +// ✅ Tốt: checkbox được kiểm soát với onChange <input type="checkbox" checked={something} onChange={e => setSomething(e.target.checked)} /> ``` <Pitfall> -You need to read `e.target.checked` rather than `e.target.value` for checkboxes. +Bạn cần đọc `e.target.checked` thay vì `e.target.value` cho checkbox. </Pitfall> -If the checkbox is intentionally read-only, add a `readOnly` prop to suppress the error: +Nếu checkbox cố ý chỉ đọc, hãy thêm một prop `readOnly` để ngăn chặn lỗi: ```js -// ✅ Good: readonly controlled input without on change +// ✅ Tốt: input được kiểm soát chỉ đọc không có on change <input type="checkbox" checked={something} readOnly={true} /> ``` --- -### My input caret jumps to the beginning on every keystroke {/*my-input-caret-jumps-to-the-beginning-on-every-keystroke*/} +### Caret input của tôi nhảy về đầu trên mỗi lần gõ phím {/*my-input-caret-jumps-to-the-beginning-on-every-keystroke*/} -If you [control an input,](#controlling-an-input-with-a-state-variable) you must update its state variable to the input's value from the DOM during `onChange`. +Nếu bạn [kiểm soát một input,](#controlling-an-input-with-a-state-variable) bạn phải cập nhật biến trạng thái của nó thành giá trị của input từ DOM trong `onChange`. -You can't update it to something other than `e.target.value` (or `e.target.checked` for checkboxes): +Bạn không thể cập nhật nó thành một thứ khác ngoài `e.target.value` (hoặc `e.target.checked` cho checkbox): ```js function handleChange(e) { - // 🔴 Bug: updating an input to something other than e.target.value + // 🔴 Lỗi: cập nhật một input thành một thứ khác ngoài e.target.value setFirstName(e.target.value.toUpperCase()); } ``` -You also can't update it asynchronously: +Bạn cũng không thể cập nhật nó một cách không đồng bộ: ```js function handleChange(e) { - // 🔴 Bug: updating an input asynchronously + // 🔴 Lỗi: cập nhật một input một cách không đồng bộ setTimeout(() => { setFirstName(e.target.value); }, 100); } ``` -To fix your code, update it synchronously to `e.target.value`: +Để sửa mã của bạn, hãy cập nhật nó một cách đồng bộ thành `e.target.value`: ```js function handleChange(e) { - // ✅ Updating a controlled input to e.target.value synchronously + // ✅ Cập nhật một input được kiểm soát thành e.target.value một cách đồng bộ setFirstName(e.target.value); } ``` -If this doesn't fix the problem, it's possible that the input gets removed and re-added from the DOM on every keystroke. This can happen if you're accidentally [resetting state](/learn/preserving-and-resetting-state) on every re-render, for example if the input or one of its parents always receives a different `key` attribute, or if you nest component function definitions (which is not supported and causes the "inner" component to always be considered a different tree). +Nếu điều này không khắc phục được sự cố, có thể input bị xóa và thêm lại từ DOM trên mỗi lần gõ phím. Điều này có thể xảy ra nếu bạn vô tình [đặt lại trạng thái](/learn/preserving-and-resetting-state) trên mỗi lần re-render, ví dụ: nếu input hoặc một trong các phần tử cha của nó luôn nhận một thuộc tính `key` khác nhau, hoặc nếu bạn lồng các định nghĩa hàm component (điều này không được hỗ trợ và khiến component "bên trong" luôn được coi là một cây khác). --- -### I'm getting an error: "A component is changing an uncontrolled input to be controlled" {/*im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled*/} +### Tôi đang gặp lỗi: "Một component đang thay đổi một input không được kiểm soát thành được kiểm soát" {/*im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled*/} -If you provide a `value` to the component, it must remain a string throughout its lifetime. +Nếu bạn cung cấp một `value` cho component, nó phải duy trì là một chuỗi trong suốt vòng đời của nó. -You cannot pass `value={undefined}` first and later pass `value="some string"` because React won't know whether you want the component to be uncontrolled or controlled. A controlled component should always receive a string `value`, not `null` or `undefined`. +Bạn không thể truyền `value={undefined}` trước và sau đó truyền `value="some string"` vì React sẽ không biết bạn muốn component không được kiểm soát hay được kiểm soát. Một component được kiểm soát phải luôn nhận một `value` kiểu chuỗi, không phải `null` hoặc `undefined`. -If your `value` is coming from an API or a state variable, it might be initialized to `null` or `undefined`. In that case, either set it to an empty string (`''`) initially, or pass `value={someValue ?? ''}` to ensure `value` is a string. +Nếu `value` của bạn đến từ một API hoặc một biến trạng thái, nó có thể được khởi tạo thành `null` hoặc `undefined`. Trong trường hợp đó, hãy đặt nó thành một chuỗi trống (`''`) ban đầu, hoặc truyền `value={someValue ?? ''}` để đảm bảo `value` là một chuỗi. -Similarly, if you pass `checked` to a checkbox, ensure it's always a boolean. +Tương tự, nếu bạn truyền `checked` cho một checkbox, hãy đảm bảo nó luôn là một boolean. diff --git a/src/content/reference/react-dom/components/link.md b/src/content/reference/react-dom/components/link.md index 41236eb3d..d80a3b0c9 100644 --- a/src/content/reference/react-dom/components/link.md +++ b/src/content/reference/react-dom/components/link.md @@ -4,7 +4,7 @@ link: "<link>" <Intro> -The [built-in browser `<link>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) lets you use external resources such as stylesheets or annotate the document with link metadata. +[Thành phần `<link>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) cho phép bạn sử dụng các tài nguyên bên ngoài như biểu định kiểu hoặc chú thích tài liệu bằng siêu dữ liệu liên kết. ```js <link rel="icon" href="favicon.ico" /> @@ -16,94 +16,94 @@ The [built-in browser `<link>` component](https://developer.mozilla.org/en-US/do --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<link>` {/*link*/} -To link to external resources such as stylesheets, fonts, and icons, or to annotate the document with link metadata, render the [built-in browser `<link>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link). You can render `<link>` from any component and React will [in most cases](#special-rendering-behavior) place the corresponding DOM element in the document head. +Để liên kết đến các tài nguyên bên ngoài như biểu định kiểu, phông chữ và biểu tượng, hoặc để chú thích tài liệu bằng siêu dữ liệu liên kết, hãy hiển thị [thành phần `<link>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link). Bạn có thể hiển thị `<link>` từ bất kỳ thành phần nào và React sẽ [trong hầu hết các trường hợp](#special-rendering-behavior) đặt phần tử DOM tương ứng trong phần đầu của tài liệu. ```js <link rel="icon" href="favicon.ico" /> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<link>` supports all [common element props.](/reference/react-dom/components/common#props) +`<link>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) -* `rel`: a string, required. Specifies the [relationship to the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel). React [treats links with `rel="stylesheet"` differently](#special-rendering-behavior) from other links. +* `rel`: một chuỗi, bắt buộc. Chỉ định [mối quan hệ với tài nguyên](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel). React [xử lý các liên kết có `rel="stylesheet"` khác biệt](#special-rendering-behavior) so với các liên kết khác. -These props apply when `rel="stylesheet"`: +Các props này áp dụng khi `rel="stylesheet"`: -* `precedence`: a string. Tells React where to rank the `<link>` DOM node relative to others in the document `<head>`, which determines which stylesheet can override the other. React will infer that precedence values it discovers first are "lower" and precedence values it discovers later are "higher". Many style systems can work fine using a single precedence value because style rules are atomic. Stylesheets with the same precedence go together whether they are `<link>` or inline `<style>` tags or loaded using [`preinit`](/reference/react-dom/preinit) functions. -* `media`: a string. Restricts the stylesheet to a certain [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries). -* `title`: a string. Specifies the name of an [alternative stylesheet](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets). +* `precedence`: một chuỗi. Cho React biết vị trí xếp hạng nút DOM `<link>` so với các nút khác trong `<head>` của tài liệu, điều này xác định biểu định kiểu nào có thể ghi đè biểu định kiểu khác. React sẽ suy ra rằng các giá trị precedence mà nó khám phá đầu tiên là "thấp hơn" và các giá trị precedence mà nó khám phá sau là "cao hơn". Nhiều hệ thống kiểu có thể hoạt động tốt bằng cách sử dụng một giá trị precedence duy nhất vì các quy tắc kiểu là nguyên tử. Các biểu định kiểu có cùng precedence đi cùng nhau cho dù chúng là thẻ `<link>` hay thẻ `<style>` nội tuyến hoặc được tải bằng các hàm [`preinit`](/reference/react-dom/preinit). +* `media`: một chuỗi. Hạn chế biểu định kiểu cho một [truy vấn phương tiện](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries) nhất định. +* `title`: một chuỗi. Chỉ định tên của một [biểu định kiểu thay thế](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets). -These props apply when `rel="stylesheet"` but disable React's [special treatment of stylesheets](#special-rendering-behavior): +Các props này áp dụng khi `rel="stylesheet"` nhưng tắt [cách xử lý đặc biệt của React đối với biểu định kiểu](#special-rendering-behavior): -* `disabled`: a boolean. Disables the stylesheet. -* `onError`: a function. Called when the stylesheet fails to load. -* `onLoad`: a function. Called when the stylesheet finishes being loaded. +* `disabled`: một boolean. Vô hiệu hóa biểu định kiểu. +* `onError`: một hàm. Được gọi khi biểu định kiểu không tải được. +* `onLoad`: một hàm. Được gọi khi biểu định kiểu tải xong. -These props apply when `rel="preload"` or `rel="modulepreload"`: +Các props này áp dụng khi `rel="preload"` hoặc `rel="modulepreload"`: -* `as`: a string. The type of resource. Its possible values are `audio`, `document`, `embed`, `fetch`, `font`, `image`, `object`, `script`, `style`, `track`, `video`, `worker`. -* `imageSrcSet`: a string. Applicable only when `as="image"`. Specifies the [source set of the image](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). -* `imageSizes`: a string. Applicable only when `as="image"`. Specifies the [sizes of the image](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). +* `as`: một chuỗi. Loại tài nguyên. Các giá trị có thể của nó là `audio`, `document`, `embed`, `fetch`, `font`, `image`, `object`, `script`, `style`, `track`, `video`, `worker`. +* `imageSrcSet`: một chuỗi. Chỉ áp dụng khi `as="image"`. Chỉ định [tập hợp nguồn của hình ảnh](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). +* `imageSizes`: một chuỗi. Chỉ áp dụng khi `as="image"`. Chỉ định [kích thước của hình ảnh](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). -These props apply when `rel="icon"` or `rel="apple-touch-icon"`: +Các props này áp dụng khi `rel="icon"` hoặc `rel="apple-touch-icon"`: -* `sizes`: a string. The [sizes of the icon](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). +* `sizes`: một chuỗi. [Kích thước của biểu tượng](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). -These props apply in all cases: +Các props này áp dụng trong mọi trường hợp: -* `href`: a string. The URL of the linked resource. -* `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. It is required when `as` is set to `"fetch"`. -* `referrerPolicy`: a string. The [Referrer header](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy) to send when fetching. Its possible values are `no-referrer-when-downgrade` (the default), `no-referrer`, `origin`, `origin-when-cross-origin`, and `unsafe-url`. -* `fetchPriority`: a string. Suggests a relative priority for fetching the resource. The possible values are `auto` (the default), `high`, and `low`. -* `hrefLang`: a string. The language of the linked resource. -* `integrity`: a string. A cryptographic hash of the resource, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). -* `type`: a string. The MIME type of the linked resource. +* `href`: một chuỗi. URL của tài nguyên được liên kết. +* `crossOrigin`: một chuỗi. [Chính sách CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) để sử dụng. Các giá trị có thể của nó là `anonymous` và `use-credentials`. Nó là bắt buộc khi `as` được đặt thành `"fetch"`. +* `referrerPolicy`: một chuỗi. [Tiêu đề Referrer](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy) để gửi khi tìm nạp. Các giá trị có thể của nó là `no-referrer-when-downgrade` (mặc định), `no-referrer`, `origin`, `origin-when-cross-origin` và `unsafe-url`. +* `fetchPriority`: một chuỗi. Đề xuất mức độ ưu tiên tương đối để tìm nạp tài nguyên. Các giá trị có thể là `auto` (mặc định), `high` và `low`. +* `hrefLang`: một chuỗi. Ngôn ngữ của tài nguyên được liên kết. +* `integrity`: một chuỗi. Một hàm băm mật mã của tài nguyên, để [xác minh tính xác thực của nó](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). +* `type`: một chuỗi. Loại MIME của tài nguyên được liên kết. -Props that are **not recommended** for use with React: +Các props **không được khuyến nghị** sử dụng với React: -* `blocking`: a string. If set to `"render"`, instructs the browser not to render the page until the stylesheet is loaded. React provides more fine-grained control using Suspense. +* `blocking`: một chuỗi. Nếu được đặt thành `"render"`, hướng dẫn trình duyệt không hiển thị trang cho đến khi biểu định kiểu được tải. React cung cấp khả năng kiểm soát chi tiết hơn bằng cách sử dụng Suspense. -#### Special rendering behavior {/*special-rendering-behavior*/} +#### Hành vi hiển thị đặc biệt {/*special-rendering-behavior*/} -React will always place the DOM element corresponding to the `<link>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<link>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render `<link>` components itself. +React sẽ luôn đặt phần tử DOM tương ứng với thành phần `<link>` trong `<head>` của tài liệu, bất kể nó được hiển thị ở đâu trong cây React. `<head>` là vị trí hợp lệ duy nhất cho `<link>` tồn tại trong DOM, nhưng thật tiện lợi và giữ cho mọi thứ có thể kết hợp nếu một thành phần đại diện cho một trang cụ thể có thể tự hiển thị các thành phần `<link>`. -There are a few exceptions to this: +Có một vài ngoại lệ đối với điều này: -* If the `<link>` has a `rel="stylesheet"` prop, then it has to also have a `precedence` prop to get this special behavior. This is because the order of stylesheets within the document is significant, so React needs to know how to order this stylesheet relative to others, which you specify using the `precedence` prop. If the `precedence` prop is omitted, there is no special behavior. -* If the `<link>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t apply to the document but instead represents metadata about a specific part of the page. -* If the `<link>` has an `onLoad` or `onError` prop, because in that case you are managing the loading of the linked resource manually within your React component. +* Nếu `<link>` có prop `rel="stylesheet"`, thì nó cũng phải có prop `precedence` để có được hành vi đặc biệt này. Điều này là do thứ tự của các biểu định kiểu trong tài liệu là quan trọng, vì vậy React cần biết cách sắp xếp biểu định kiểu này so với các biểu định kiểu khác, mà bạn chỉ định bằng cách sử dụng prop `precedence`. Nếu prop `precedence` bị bỏ qua, sẽ không có hành vi đặc biệt nào. +* Nếu `<link>` có prop [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop), sẽ không có hành vi đặc biệt nào, vì trong trường hợp này, nó không áp dụng cho tài liệu mà thay vào đó đại diện cho siêu dữ liệu về một phần cụ thể của trang. +* Nếu `<link>` có prop `onLoad` hoặc `onError`, vì trong trường hợp đó, bạn đang quản lý việc tải tài nguyên được liên kết theo cách thủ công trong thành phần React của mình. -#### Special behavior for stylesheets {/*special-behavior-for-stylesheets*/} +#### Hành vi đặc biệt đối với biểu định kiểu {/*special-behavior-for-stylesheets*/} -In addition, if the `<link>` is to a stylesheet (namely, it has `rel="stylesheet"` in its props), React treats it specially in the following ways: +Ngoài ra, nếu `<link>` là một biểu định kiểu (cụ thể là nó có `rel="stylesheet"` trong các props của nó), React sẽ xử lý nó đặc biệt theo những cách sau: -* The component that renders `<link>` will [suspend](/reference/react/Suspense) while the stylesheet is loading. -* If multiple components render links to the same stylesheet, React will de-duplicate them and only put a single link into the DOM. Two links are considered the same if they have the same `href` prop. +* Thành phần hiển thị `<link>` sẽ [tạm ngưng](/reference/react/Suspense) trong khi biểu định kiểu đang tải. +* Nếu nhiều thành phần hiển thị các liên kết đến cùng một biểu định kiểu, React sẽ loại bỏ các liên kết trùng lặp và chỉ đặt một liên kết duy nhất vào DOM. Hai liên kết được coi là giống nhau nếu chúng có cùng prop `href`. -There are two exception to this special behavior: +Có hai ngoại lệ đối với hành vi đặc biệt này: -* If the link doesn't have a `precedence` prop, there is no special behavior, because the order of stylesheets within the document is significant, so React needs to know how to order this stylesheet relative to others, which you specify using the `precedence` prop. -* If you supply any of the `onLoad`, `onError`, or `disabled` props, there is no special behavior, because these props indicate that you are managing the loading of the stylesheet manually within your component. +* Nếu liên kết không có prop `precedence`, sẽ không có hành vi đặc biệt nào, vì thứ tự của các biểu định kiểu trong tài liệu là quan trọng, vì vậy React cần biết cách sắp xếp biểu định kiểu này so với các biểu định kiểu khác, mà bạn chỉ định bằng cách sử dụng prop `precedence`. +* Nếu bạn cung cấp bất kỳ prop `onLoad`, `onError` hoặc `disabled` nào, sẽ không có hành vi đặc biệt nào, vì các prop này chỉ ra rằng bạn đang quản lý việc tải biểu định kiểu theo cách thủ công trong thành phần của mình. -This special treatment comes with two caveats: +Cách xử lý đặc biệt này đi kèm với hai lưu ý: -* React will ignore changes to props after the link has been rendered. (React will issue a warning in development if this happens.) -* React may leave the link in the DOM even after the component that rendered it has been unmounted. +* React sẽ bỏ qua các thay đổi đối với các props sau khi liên kết đã được hiển thị. (React sẽ đưa ra cảnh báo trong quá trình phát triển nếu điều này xảy ra.) +* React có thể để lại liên kết trong DOM ngay cả sau khi thành phần hiển thị nó đã bị hủy gắn kết. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Linking to related resources {/*linking-to-related-resources*/} +### Liên kết đến các tài nguyên liên quan {/*linking-to-related-resources*/} -You can annotate the document with links to related resources such as an icon, canonical URL, or pingback. React will place this metadata within the document `<head>` regardless of where in the React tree it is rendered. +Bạn có thể chú thích tài liệu bằng các liên kết đến các tài nguyên liên quan như biểu tượng, URL chuẩn hoặc pingback. React sẽ đặt siêu dữ liệu này trong `<head>` của tài liệu bất kể nó được hiển thị ở đâu trong cây React. <SandpackWithHTMLOutput> @@ -115,7 +115,7 @@ export default function BlogPage() { <ShowRenderedHTML> <link rel="icon" href="favicon.ico" /> <link rel="pingback" href="http://www.example.com/xmlrpc.php" /> - <h1>My Blog</h1> + <h1>Blog của tôi</h1> <p>...</p> </ShowRenderedHTML> ); @@ -124,12 +124,12 @@ export default function BlogPage() { </SandpackWithHTMLOutput> -### Linking to a stylesheet {/*linking-to-a-stylesheet*/} +### Liên kết đến một biểu định kiểu {/*linking-to-a-stylesheet*/} -If a component depends on a certain stylesheet in order to be displayed correctly, you can render a link to that stylesheet within the component. Your component will [suspend](/reference/react/Suspense) while the stylesheet is loading. You must supply the `precedence` prop, which tells React where to place this stylesheet relative to others — stylesheets with higher precedence can override those with lower precedence. +Nếu một thành phần phụ thuộc vào một biểu định kiểu nhất định để được hiển thị chính xác, bạn có thể hiển thị một liên kết đến biểu định kiểu đó trong thành phần. Thành phần của bạn sẽ [tạm ngưng](/reference/react/Suspense) trong khi biểu định kiểu đang tải. Bạn phải cung cấp prop `precedence`, cho React biết vị trí đặt biểu định kiểu này so với các biểu định kiểu khác — các biểu định kiểu có precedence cao hơn có thể ghi đè các biểu định kiểu có precedence thấp hơn. <Note> -When you want to use a stylesheet, it can be beneficial to call the [preinit](/reference/react-dom/preinit) function. Calling this function may allow the browser to start fetching the stylesheet earlier than if you just render a `<link>` component, for example by sending an [HTTP Early Hints response](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103). +Khi bạn muốn sử dụng một biểu định kiểu, có thể có lợi khi gọi hàm [preinit](/reference/react-dom/preinit). Gọi hàm này có thể cho phép trình duyệt bắt đầu tìm nạp biểu định kiểu sớm hơn so với khi bạn chỉ hiển thị một thành phần `<link>`, ví dụ: bằng cách gửi [phản hồi HTTP Early Hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103). </Note> <SandpackWithHTMLOutput> @@ -149,9 +149,9 @@ export default function SiteMapPage() { </SandpackWithHTMLOutput> -### Controlling stylesheet precedence {/*controlling-stylesheet-precedence*/} +### Kiểm soát precedence của biểu định kiểu {/*controlling-stylesheet-precedence*/} -Stylesheets can conflict with each other, and when they do, the browser goes with the one that comes later in the document. React lets you control the order of stylesheets with the `precedence` prop. In this example, three components render stylesheets, and the ones with the same precedence are grouped together in the `<head>`. +Các biểu định kiểu có thể xung đột với nhau và khi chúng xung đột, trình duyệt sẽ sử dụng biểu định kiểu xuất hiện sau trong tài liệu. React cho phép bạn kiểm soát thứ tự của các biểu định kiểu bằng prop `precedence`. Trong ví dụ này, ba thành phần hiển thị các biểu định kiểu và các thành phần có cùng precedence được nhóm lại với nhau trong `<head>`. <SandpackWithHTMLOutput> @@ -185,11 +185,11 @@ function ThirdComponent() { </SandpackWithHTMLOutput> -Note the `precedence` values themselves are arbitrary and their naming is up to you. React will infer that precedence values it discovers first are "lower" and precedence values it discovers later are "higher". +Lưu ý rằng các giá trị `precedence` là tùy ý và việc đặt tên chúng là tùy thuộc vào bạn. React sẽ suy ra rằng các giá trị precedence mà nó khám phá đầu tiên là "thấp hơn" và các giá trị precedence mà nó khám phá sau là "cao hơn". -### Deduplicated stylesheet rendering {/*deduplicated-stylesheet-rendering*/} +### Loại bỏ hiển thị biểu định kiểu trùng lặp {/*deduplicated-stylesheet-rendering*/} -If you render the same stylesheet from multiple components, React will place only a single `<link>` in the document head. +Nếu bạn hiển thị cùng một biểu định kiểu từ nhiều thành phần, React sẽ chỉ đặt một `<link>` duy nhất trong phần đầu của tài liệu. <SandpackWithHTMLOutput> @@ -213,13 +213,13 @@ function Component() { </SandpackWithHTMLOutput> -### Annotating specific items within the document with links {/*annotating-specific-items-within-the-document-with-links*/} +### Chú thích các mục cụ thể trong tài liệu bằng các liên kết {/*annotating-specific-items-within-the-document-with-links*/} -You can use the `<link>` component with the `itemProp` prop to annotate specific items within the document with links to related resources. In this case, React will *not* place these annotations within the document `<head>` but will place them like any other React component. +Bạn có thể sử dụng thành phần `<link>` với prop `itemProp` để chú thích các mục cụ thể trong tài liệu bằng các liên kết đến các tài nguyên liên quan. Trong trường hợp này, React sẽ *không* đặt các chú thích này trong `<head>` của tài liệu mà sẽ đặt chúng như bất kỳ thành phần React nào khác. ```js <section itemScope> - <h3>Annotating specific items</h3> + <h3>Chú thích các mục cụ thể</h3> <link itemProp="author" href="http://example.com/" /> <p>...</p> </section> diff --git a/src/content/reference/react-dom/components/meta.md b/src/content/reference/react-dom/components/meta.md index 2173ce002..e4143d2ee 100644 --- a/src/content/reference/react-dom/components/meta.md +++ b/src/content/reference/react-dom/components/meta.md @@ -4,7 +4,7 @@ meta: "<meta>" <Intro> -The [built-in browser `<meta>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) lets you add metadata to the document. +[Thành phần `<meta>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) cho phép bạn thêm metadata vào tài liệu. ```js <meta name="keywords" content="React, JavaScript, semantic markup, html" /> @@ -16,51 +16,51 @@ The [built-in browser `<meta>` component](https://developer.mozilla.org/en-US/do --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<meta>` {/*meta*/} -To add document metadata, render the [built-in browser `<meta>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta). You can render `<meta>` from any component and React will always place the corresponding DOM element in the document head. +Để thêm metadata cho tài liệu, hãy render [thành phần `<meta>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta). Bạn có thể render `<meta>` từ bất kỳ thành phần nào và React sẽ luôn đặt phần tử DOM tương ứng vào phần đầu của tài liệu. ```js <meta name="keywords" content="React, JavaScript, semantic markup, html" /> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<meta>` supports all [common element props.](/reference/react-dom/components/common#props) +`<meta>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) -It should have *exactly one* of the following props: `name`, `httpEquiv`, `charset`, `itemProp`. The `<meta>` component does something different depending on which of these props is specified. +Nó phải có *chính xác một* trong các props sau: `name`, `httpEquiv`, `charset`, `itemProp`. Thành phần `<meta>` thực hiện một việc khác nhau tùy thuộc vào prop nào trong số này được chỉ định. -* `name`: a string. Specifies the [kind of metadata](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name) to be attached to the document. -* `charset`: a string. Specifies the character set used by the document. The only valid value is `"utf-8"`. -* `httpEquiv`: a string. Specifies a directive for processing the document. -* `itemProp`: a string. Specifies metadata about a particular item within the document rather than the document as a whole. -* `content`: a string. Specifies the metadata to be attached when used with the `name` or `itemProp` props or the behavior of the directive when used with the `httpEquiv` prop. +* `name`: một chuỗi. Chỉ định [loại metadata](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name) sẽ được đính kèm vào tài liệu. +* `charset`: một chuỗi. Chỉ định bộ ký tự được sử dụng bởi tài liệu. Giá trị hợp lệ duy nhất là `"utf-8"`. +* `httpEquiv`: một chuỗi. Chỉ định một chỉ thị để xử lý tài liệu. +* `itemProp`: một chuỗi. Chỉ định metadata về một mục cụ thể trong tài liệu thay vì toàn bộ tài liệu. +* `content`: một chuỗi. Chỉ định metadata sẽ được đính kèm khi được sử dụng với các props `name` hoặc `itemProp` hoặc hành vi của chỉ thị khi được sử dụng với prop `httpEquiv`. -#### Special rendering behavior {/*special-rendering-behavior*/} +#### Hành vi render đặc biệt {/*special-rendering-behavior*/} -React will always place the DOM element corresponding to the `<meta>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<meta>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render `<meta>` components itself. +React sẽ luôn đặt phần tử DOM tương ứng với thành phần `<meta>` bên trong `<head>` của tài liệu, bất kể nó được render ở đâu trong cây React. `<head>` là vị trí hợp lệ duy nhất cho `<meta>` tồn tại trong DOM, nhưng thật tiện lợi và giữ cho mọi thứ có thể kết hợp được nếu một thành phần đại diện cho một trang cụ thể có thể tự render các thành phần `<meta>`. -There is one exception to this: if `<meta>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t represent metadata about the document but rather metadata about a specific part of the page. +Có một ngoại lệ đối với điều này: nếu `<meta>` có một prop [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop), thì không có hành vi đặc biệt nào, vì trong trường hợp này, nó không đại diện cho metadata về tài liệu mà là metadata về một phần cụ thể của trang. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Annotating the document with metadata {/*annotating-the-document-with-metadata*/} +### Chú thích tài liệu bằng metadata {/*annotating-the-document-with-metadata*/} -You can annotate the document with metadata such as keywords, a summary, or the author’s name. React will place this metadata within the document `<head>` regardless of where in the React tree it is rendered. +Bạn có thể chú thích tài liệu bằng metadata như từ khóa, tóm tắt hoặc tên tác giả. React sẽ đặt metadata này trong `<head>` của tài liệu bất kể nó được render ở đâu trong cây React. ```html <meta name="author" content="John Smith" /> <meta name="keywords" content="React, JavaScript, semantic markup, html" /> -<meta name="description" content="API reference for the <meta> component in React DOM" /> +<meta name="description" content="Tham khảo API cho thành phần <meta> trong React DOM" /> ``` -You can render the `<meta>` component from any component. React will put a `<meta>` DOM node in the document `<head>`. +Bạn có thể render thành phần `<meta>` từ bất kỳ thành phần nào. React sẽ đặt một node DOM `<meta>` trong `<head>` của tài liệu. <SandpackWithHTMLOutput> @@ -71,8 +71,8 @@ export default function SiteMapPage() { return ( <ShowRenderedHTML> <meta name="keywords" content="React" /> - <meta name="description" content="A site map for the React website" /> - <h1>Site Map</h1> + <meta name="description" content="Sơ đồ trang web cho trang web React" /> + <h1>Sơ đồ trang web</h1> <p>...</p> </ShowRenderedHTML> ); @@ -81,14 +81,14 @@ export default function SiteMapPage() { </SandpackWithHTMLOutput> -### Annotating specific items within the document with metadata {/*annotating-specific-items-within-the-document-with-metadata*/} +### Chú thích các mục cụ thể trong tài liệu bằng metadata {/*annotating-specific-items-within-the-document-with-metadata*/} -You can use the `<meta>` component with the `itemProp` prop to annotate specific items within the document with metadata. In this case, React will *not* place these annotations within the document `<head>` but will place them like any other React component. +Bạn có thể sử dụng thành phần `<meta>` với prop `itemProp` để chú thích các mục cụ thể trong tài liệu bằng metadata. Trong trường hợp này, React sẽ *không* đặt các chú thích này trong `<head>` của tài liệu mà sẽ đặt chúng như bất kỳ thành phần React nào khác. ```js <section itemScope> - <h3>Annotating specific items</h3> - <meta itemProp="description" content="API reference for using <meta> with itemProp" /> + <h3>Chú thích các mục cụ thể</h3> + <meta itemProp="description" content="Tham khảo API để sử dụng <meta> với itemProp" /> <p>...</p> </section> ``` diff --git a/src/content/reference/react-dom/components/option.md b/src/content/reference/react-dom/components/option.md index 84725854c..3b842cc2a 100644 --- a/src/content/reference/react-dom/components/option.md +++ b/src/content/reference/react-dom/components/option.md @@ -4,7 +4,7 @@ title: "<option>" <Intro> -The [built-in browser `<option>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) lets you render an option inside a [`<select>`](/reference/react-dom/components/select) box. +[`<option>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) tích hợp sẵn của trình duyệt cho phép bạn hiển thị một tùy chọn bên trong hộp [`<select>`](/reference/react-dom/components/select). ```js <select> @@ -19,11 +19,11 @@ The [built-in browser `<option>` component](https://developer.mozilla.org/en-US/ --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<option>` {/*option*/} -The [built-in browser `<option>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) lets you render an option inside a [`<select>`](/reference/react-dom/components/select) box. +[`<option>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option) tích hợp sẵn của trình duyệt cho phép bạn hiển thị một tùy chọn bên trong hộp [`<select>`](/reference/react-dom/components/select). ```js <select> @@ -32,31 +32,31 @@ The [built-in browser `<option>` component](https://developer.mozilla.org/en-US/ </select> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<option>` supports all [common element props.](/reference/react-dom/components/common#props) +`<option>` hỗ trợ tất cả [các thuộc tính phần tử thông thường.](/reference/react-dom/components/common#props) -Additionally, `<option>` supports these props: +Ngoài ra, `<option>` hỗ trợ các thuộc tính sau: -* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#disabled): A boolean. If `true`, the option will not be selectable and will appear dimmed. -* [`label`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#label): A string. Specifies the meaning of the option. If not specified, the text inside the option is used. -* [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#value): The value to be used [when submitting the parent `<select>` in a form](/reference/react-dom/components/select#reading-the-select-box-value-when-submitting-a-form) if this option is selected. +* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#disabled): Một giá trị boolean. Nếu `true`, tùy chọn sẽ không thể chọn và sẽ xuất hiện màu xám. +* [`label`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#label): Một chuỗi. Chỉ định ý nghĩa của tùy chọn. Nếu không được chỉ định, văn bản bên trong tùy chọn sẽ được sử dụng. +* [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option#value): Giá trị được sử dụng [khi gửi `<select>` mẹ trong một biểu mẫu](/reference/react-dom/components/select#reading-the-select-box-value-when-submitting-a-form) nếu tùy chọn này được chọn. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* React does not support the `selected` attribute on `<option>`. Instead, pass this option's `value` to the parent [`<select defaultValue>`](/reference/react-dom/components/select#providing-an-initially-selected-option) for an uncontrolled select box, or [`<select value>`](/reference/react-dom/components/select#controlling-a-select-box-with-a-state-variable) for a controlled select. +* React không hỗ trợ thuộc tính `selected` trên `<option>`. Thay vào đó, hãy truyền `value` của tùy chọn này cho [`<select defaultValue>`](/reference/react-dom/components/select#providing-an-initially-selected-option) mẹ cho một hộp chọn không được kiểm soát hoặc [`<select value>`](/reference/react-dom/components/select#controlling-a-select-box-with-a-state-variable) cho một lựa chọn được kiểm soát. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Displaying a select box with options {/*displaying-a-select-box-with-options*/} +### Hiển thị hộp chọn với các tùy chọn {/*displaying-a-select-box-with-options*/} -Render a `<select>` with a list of `<option>` components inside to display a select box. Give each `<option>` a `value` representing the data to be submitted with the form. +Kết xuất một `<select>` với một danh sách các thành phần `<option>` bên trong để hiển thị một hộp chọn. Cung cấp cho mỗi `<option>` một `value` đại diện cho dữ liệu sẽ được gửi với biểu mẫu. -[Read more about displaying a `<select>` with a list of `<option>` components.](/reference/react-dom/components/select) +[Đọc thêm về hiển thị một `<select>` với một danh sách các thành phần `<option>`.](/reference/react-dom/components/select) <Sandpack> @@ -64,11 +64,11 @@ Render a `<select>` with a list of `<option>` components inside to display a sel export default function FruitPicker() { return ( <label> - Pick a fruit: + Chọn một loại trái cây: <select name="selectedFruit"> - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> ); @@ -79,5 +79,4 @@ export default function FruitPicker() { select { margin: 5px; } ``` -</Sandpack> - +</Sandpack> diff --git a/src/content/reference/react-dom/components/progress.md b/src/content/reference/react-dom/components/progress.md index 9a8d60ab0..a7649699b 100644 --- a/src/content/reference/react-dom/components/progress.md +++ b/src/content/reference/react-dom/components/progress.md @@ -4,7 +4,7 @@ title: "<progress>" <Intro> -The [built-in browser `<progress>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) lets you render a progress indicator. +[Built-in browser `<progress>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) cho phép bạn hiển thị một chỉ báo tiến trình. ```js <progress value={0.5} /> @@ -16,36 +16,36 @@ The [built-in browser `<progress>` component](https://developer.mozilla.org/en-U --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<progress>` {/*progress*/} -To display a progress indicator, render the [built-in browser `<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) component. +Để hiển thị một chỉ báo tiến trình, render [built-in browser `<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) component. ```js <progress value={0.5} /> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<progress>` supports all [common element props.](/reference/react-dom/components/common#props) +`<progress>` hỗ trợ tất cả [common element props.](/reference/react-dom/components/common#props) -Additionally, `<progress>` supports these props: +Ngoài ra, `<progress>` hỗ trợ các props sau: -* [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#max): A number. Specifies the maximum `value`. Defaults to `1`. -* [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#value): A number between `0` and `max`, or `null` for indeterminate progress. Specifies how much was done. +* [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#max): Một số. Chỉ định giá trị `value` tối đa. Mặc định là `1`. +* [`value`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress#value): Một số giữa `0` và `max`, hoặc `null` cho tiến trình không xác định. Chỉ định mức độ hoàn thành. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Controlling a progress indicator {/*controlling-a-progress-indicator*/} +### Điều khiển một chỉ báo tiến trình {/*controlling-a-progress-indicator*/} -To display a progress indicator, render a `<progress>` component. You can pass a number `value` between `0` and the `max` value you specify. If you don't pass a `max` value, it will assumed to be `1` by default. +Để hiển thị một chỉ báo tiến trình, render một `<progress>` component. Bạn có thể truyền một số `value` giữa `0` và giá trị `max` mà bạn chỉ định. Nếu bạn không truyền giá trị `max`, nó sẽ được mặc định là `1`. -If the operation is not ongoing, pass `value={null}` to put the progress indicator into an indeterminate state. +Nếu thao tác không diễn ra liên tục, hãy truyền `value={null}` để đưa chỉ báo tiến trình vào trạng thái không xác định. <Sandpack> diff --git a/src/content/reference/react-dom/components/script.md b/src/content/reference/react-dom/components/script.md index 6bf3d83c7..468badf64 100644 --- a/src/content/reference/react-dom/components/script.md +++ b/src/content/reference/react-dom/components/script.md @@ -4,7 +4,7 @@ script: "<script>" <Intro> -The [built-in browser `<script>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) lets you add a script to your document. +[Thành phần `<script>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) cho phép bạn thêm một script vào tài liệu của mình. ```js <script> alert("hi!") </script> @@ -16,71 +16,71 @@ The [built-in browser `<script>` component](https://developer.mozilla.org/en-US/ --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<script>` {/*script*/} -To add inline or external scripts to your document, render the [built-in browser `<script>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script). You can render `<script>` from any component and React will [in certain cases](#special-rendering-behavior) place the corresponding DOM element in the document head and de-duplicate identical scripts. +Để thêm các script nội tuyến hoặc bên ngoài vào tài liệu của bạn, hãy hiển thị [thành phần `<script>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script). Bạn có thể hiển thị `<script>` từ bất kỳ thành phần nào và React sẽ [trong một số trường hợp](#special-rendering-behavior) đặt phần tử DOM tương ứng vào đầu tài liệu và loại bỏ các script giống hệt nhau. ```js <script> alert("hi!") </script> <script src="script.js" /> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<script>` supports all [common element props.](/reference/react-dom/components/common#props) +`<script>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) -It should have *either* `children` or a `src` prop. +Nó phải có *hoặc* `children` hoặc một prop `src`. -* `children`: a string. The source code of an inline script. -* `src`: a string. The URL of an external script. +* `children`: một chuỗi. Mã nguồn của một script nội tuyến. +* `src`: một chuỗi. URL của một script bên ngoài. -Other supported props: +Các props được hỗ trợ khác: -* `async`: a boolean. Allows the browser to defer execution of the script until the rest of the document has been processed — the preferred behavior for performance. -* `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. -* `fetchPriority`: a string. Lets the browser rank scripts in priority when fetching multiple scripts at the same time. Can be `"high"`, `"low"`, or `"auto"` (the default). -* `integrity`: a string. A cryptographic hash of the script, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). -* `noModule`: a boolean. Disables the script in browsers that support ES modules — allowing for a fallback script for browsers that do not. -* `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. -* `referrer`: a string. Says [what Referer header to send](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#referrerpolicy) when fetching the script and any resources that the script fetches in turn. -* `type`: a string. Says whether the script is a [classic script, ES module, or import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type). +* `async`: một boolean. Cho phép trình duyệt trì hoãn việc thực thi script cho đến khi phần còn lại của tài liệu đã được xử lý — hành vi ưu tiên cho hiệu suất. +* `crossOrigin`: một chuỗi. [Chính sách CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) để sử dụng. Các giá trị có thể của nó là `anonymous` và `use-credentials`. +* `fetchPriority`: một chuỗi. Cho phép trình duyệt xếp hạng các script theo thứ tự ưu tiên khi tìm nạp nhiều script cùng một lúc. Có thể là `high`, `low` hoặc `auto` (mặc định). +* `integrity`: một chuỗi. Một hàm băm mật mã của script, để [xác minh tính xác thực của nó](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). +* `noModule`: một boolean. Vô hiệu hóa script trong các trình duyệt hỗ trợ ES modules — cho phép một script dự phòng cho các trình duyệt không hỗ trợ. +* `nonce`: một chuỗi. Một [nonce mật mã để cho phép tài nguyên](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) khi sử dụng Chính sách bảo mật nội dung nghiêm ngặt. +* `referrer`: một chuỗi. Cho biết [tiêu đề Referer nào sẽ gửi](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#referrerpolicy) khi tìm nạp script và bất kỳ tài nguyên nào mà script tìm nạp đến lượt. +* `type`: một chuỗi. Cho biết liệu script là [script cổ điển, ES module hoặc import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type). -Props that disable React's [special treatment of scripts](#special-rendering-behavior): +Các props vô hiệu hóa [cách xử lý đặc biệt của React đối với các script](#special-rendering-behavior): -* `onError`: a function. Called when the script fails to load. -* `onLoad`: a function. Called when the script finishes being loaded. +* `onError`: một hàm. Được gọi khi script không tải được. +* `onLoad`: một hàm. Được gọi khi script tải xong. -Props that are **not recommended** for use with React: +Các props **không được khuyến nghị** sử dụng với React: -* `blocking`: a string. If set to `"render"`, instructs the browser not to render the page until the scriptsheet is loaded. React provides more fine-grained control using Suspense. -* `defer`: a string. Prevents the browser from executing the script until the document is done loading. Not compatible with streaming server-rendered components. Use the `async` prop instead. +* `blocking`: một chuỗi. Nếu được đặt thành `"render"`, hướng dẫn trình duyệt không hiển thị trang cho đến khi scriptsheet được tải. React cung cấp khả năng kiểm soát chi tiết hơn bằng Suspense. +* `defer`: một chuỗi. Ngăn trình duyệt thực thi script cho đến khi tài liệu được tải xong. Không tương thích với các thành phần kết xuất phía máy chủ trực tuyến. Sử dụng prop `async` thay thế. -#### Special rendering behavior {/*special-rendering-behavior*/} +#### Hành vi kết xuất đặc biệt {/*special-rendering-behavior*/} -React can move `<script>` components to the document's `<head>` and de-duplicate identical scripts. +React có thể di chuyển các thành phần `<script>` đến `<head>` của tài liệu và loại bỏ các script giống hệt nhau. -To opt into this behavior, provide the `src` and `async={true}` props. React will de-duplicate scripts if they have the same `src`. The `async` prop must be true to allow scripts to be safely moved. +Để chọn tham gia hành vi này, hãy cung cấp các prop `src` và `async={true}`. React sẽ loại bỏ các script trùng lặp nếu chúng có cùng `src`. Prop `async` phải là true để cho phép các script được di chuyển một cách an toàn. -This special treatment comes with two caveats: +Cách xử lý đặc biệt này đi kèm với hai lưu ý: -* React will ignore changes to props after the script has been rendered. (React will issue a warning in development if this happens.) -* React may leave the script in the DOM even after the component that rendered it has been unmounted. (This has no effect as scripts just execute once when they are inserted into the DOM.) +* React sẽ bỏ qua các thay đổi đối với các prop sau khi script đã được hiển thị. (React sẽ đưa ra cảnh báo trong quá trình phát triển nếu điều này xảy ra.) +* React có thể để lại script trong DOM ngay cả sau khi thành phần hiển thị nó đã bị hủy gắn kết. (Điều này không có hiệu lực vì các script chỉ thực thi một lần khi chúng được chèn vào DOM.) --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering an external script {/*rendering-an-external-script*/} +### Kết xuất một script bên ngoài {/*rendering-an-external-script*/} -If a component depends on certain scripts in order to be displayed correctly, you can render a `<script>` within the component. -However, the component might be committed before the script has finished loading. -You can start depending on the script content once the `load` event is fired e.g. by using the `onLoad` prop. +Nếu một thành phần phụ thuộc vào các script nhất định để được hiển thị chính xác, bạn có thể hiển thị một `<script>` bên trong thành phần đó. +Tuy nhiên, thành phần có thể được commit trước khi script tải xong. +Bạn có thể bắt đầu phụ thuộc vào nội dung script sau khi sự kiện `load` được kích hoạt, ví dụ: bằng cách sử dụng prop `onLoad`. -React will de-duplicate scripts that have the same `src`, inserting only one of them into the DOM even if multiple components render it. +React sẽ loại bỏ các script trùng lặp có cùng `src`, chỉ chèn một trong số chúng vào DOM ngay cả khi nhiều thành phần hiển thị nó. <SandpackWithHTMLOutput> @@ -108,12 +108,12 @@ export default function Page() { </SandpackWithHTMLOutput> <Note> -When you want to use a script, it can be beneficial to call the [preinit](/reference/react-dom/preinit) function. Calling this function may allow the browser to start fetching the script earlier than if you just render a `<script>` component, for example by sending an [HTTP Early Hints response](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103). +Khi bạn muốn sử dụng một script, có thể có lợi khi gọi hàm [preinit](/reference/react-dom/preinit). Gọi hàm này có thể cho phép trình duyệt bắt đầu tìm nạp script sớm hơn so với khi bạn chỉ hiển thị một thành phần `<script>`, ví dụ: bằng cách gửi [phản hồi HTTP Early Hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103). </Note> -### Rendering an inline script {/*rendering-an-inline-script*/} +### Kết xuất một script nội tuyến {/*rendering-an-inline-script*/} -To include an inline script, render the `<script>` component with the script source code as its children. Inline scripts are not de-duplicated or moved to the document `<head>`. +Để bao gồm một script nội tuyến, hãy hiển thị thành phần `<script>` với mã nguồn script là children của nó. Các script nội tuyến không được loại bỏ trùng lặp hoặc di chuyển đến `<head>` của tài liệu. <SandpackWithHTMLOutput> diff --git a/src/content/reference/react-dom/components/select.md b/src/content/reference/react-dom/components/select.md index 0dbdc1537..3098d59f5 100644 --- a/src/content/reference/react-dom/components/select.md +++ b/src/content/reference/react-dom/components/select.md @@ -4,7 +4,7 @@ title: "<select>" <Intro> -The [built-in browser `<select>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) lets you render a select box with options. +[Thành phần `<select>` dựng sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) cho phép bạn hiển thị một hộp chọn với các tùy chọn. ```js <select> @@ -19,11 +19,11 @@ The [built-in browser `<select>` component](https://developer.mozilla.org/en-US/ --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<select>` {/*select*/} -To display a select box, render the [built-in browser `<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) component. +Để hiển thị hộp chọn, hãy hiển thị thành phần [`<select>` dựng sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select). ```js <select> @@ -32,55 +32,55 @@ To display a select box, render the [built-in browser `<select>`](https://develo </select> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<select>` supports all [common element props.](/reference/react-dom/components/common#props) +`<select>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) -You can [make a select box controlled](#controlling-a-select-box-with-a-state-variable) by passing a `value` prop: +Bạn có thể [tạo một hộp chọn được kiểm soát](#controlling-a-select-box-with-a-state-variable) bằng cách truyền một prop `value`: -* `value`: A string (or an array of strings for [`multiple={true}`](#enabling-multiple-selection)). Controls which option is selected. Every value string match the `value` of some `<option>` nested inside the `<select>`. +* `value`: Một chuỗi (hoặc một mảng các chuỗi cho [`multiple={true}`](#enabling-multiple-selection)). Kiểm soát tùy chọn nào được chọn. Mỗi chuỗi giá trị phải khớp với `value` của một số `<option>` được lồng bên trong `<select>`. -When you pass `value`, you must also pass an `onChange` handler that updates the passed value. +Khi bạn truyền `value`, bạn cũng phải truyền một trình xử lý `onChange` để cập nhật giá trị đã truyền. -If your `<select>` is uncontrolled, you may pass the `defaultValue` prop instead: +Nếu `<select>` của bạn không được kiểm soát, bạn có thể truyền prop `defaultValue` thay thế: -* `defaultValue`: A string (or an array of strings for [`multiple={true}`](#enabling-multiple-selection)). Specifies [the initially selected option.](#providing-an-initially-selected-option) +* `defaultValue`: Một chuỗi (hoặc một mảng các chuỗi cho [`multiple={true}`](#enabling-multiple-selection)). Chỉ định [tùy chọn được chọn ban đầu.](#providing-an-initially-selected-option) -These `<select>` props are relevant both for uncontrolled and controlled select boxes: +Các prop `<select>` này có liên quan cho cả hộp chọn được kiểm soát và không được kiểm soát: -* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#autocomplete): A string. Specifies one of the possible [autocomplete behaviors.](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) -* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#autofocus): A boolean. If `true`, React will focus the element on mount. -* `children`: `<select>` accepts [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option), [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup), and [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) components as children. You can also pass your own components as long as they eventually render one of the allowed components. If you pass your own components that eventually render `<option>` tags, each `<option>` you render must have a `value`. -* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#disabled): A boolean. If `true`, the select box will not be interactive and will appear dimmed. -* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#form): A string. Specifies the `id` of the `<form>` this select box belongs to. If omitted, it's the closest parent form. -* [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#multiple): A boolean. If `true`, the browser allows [multiple selection.](#enabling-multiple-selection) -* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#name): A string. Specifies the name for this select box that's [submitted with the form.](#reading-the-select-box-value-when-submitting-a-form) -* `onChange`: An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Required for [controlled select boxes.](#controlling-a-select-box-with-a-state-variable) Fires immediately when the user picks a different option. Behaves like the browser [`input` event.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) -* `onChangeCapture`: A version of `onChange` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires immediately when the value is changed by the user. For historical reasons, in React it is idiomatic to use `onChange` instead which works similarly. -* `onInputCapture`: A version of `onInput` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires if an input fails validation on form submit. Unlike the built-in `invalid` event, the React `onInvalid` event bubbles. -* `onInvalidCapture`: A version of `onInvalid` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#required): A boolean. If `true`, the value must be provided for the form to submit. -* [`size`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#size): A number. For `multiple={true}` selects, specifies the preferred number of initially visible items. +* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#autocomplete): Một chuỗi. Chỉ định một trong các [hành vi tự động hoàn thành có thể.](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete#values) +* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#autofocus): Một boolean. Nếu `true`, React sẽ tập trung vào phần tử khi mount. +* `children`: `<select>` chấp nhận các thành phần [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option), [`<optgroup>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup) và [`<datalist>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) làm children. Bạn cũng có thể truyền các thành phần của riêng bạn miễn là cuối cùng chúng hiển thị một trong các thành phần được phép. Nếu bạn truyền các thành phần của riêng bạn mà cuối cùng hiển thị các thẻ `<option>`, mỗi `<option>` bạn hiển thị phải có một `value`. +* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#disabled): Một boolean. Nếu `true`, hộp chọn sẽ không tương tác được và sẽ xuất hiện màu xám. +* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#form): Một chuỗi. Chỉ định `id` của `<form>` mà hộp chọn này thuộc về. Nếu bỏ qua, nó là form cha gần nhất. +* [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#multiple): Một boolean. Nếu `true`, trình duyệt cho phép [chọn nhiều tùy chọn.](#enabling-multiple-selection) +* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#name): Một chuỗi. Chỉ định tên cho hộp chọn này được [gửi cùng với form.](#reading-the-select-box-value-when-submitting-a-form) +* `onChange`: Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Bắt buộc đối với [các hộp chọn được kiểm soát.](#controlling-a-select-box-with-a-state-variable) Kích hoạt ngay lập tức khi người dùng chọn một tùy chọn khác. Hoạt động giống như sự kiện [`input` của trình duyệt.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) +* `onChangeCapture`: Một phiên bản của `onChange` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt ngay lập tức khi giá trị được thay đổi bởi người dùng. Vì lý do lịch sử, trong React, thành ngữ là sử dụng `onChange` thay thế, hoạt động tương tự. +* `onInputCapture`: Một phiên bản của `onInput` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt nếu một input không vượt qua xác thực khi gửi form. Không giống như sự kiện `invalid` dựng sẵn, sự kiện `onInvalid` của React nổi bọt. +* `onInvalidCapture`: Một phiên bản của `onInvalid` kích hoạt trong [giai đoạn capture.](/learn/responding-to-events#capture-phase-events) +* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#required): Một boolean. Nếu `true`, giá trị phải được cung cấp để form được gửi. +* [`size`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#size): Một số. Đối với các select có `multiple={true}`, chỉ định số lượng mục hiển thị ban đầu ưu tiên. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -- Unlike in HTML, passing a `selected` attribute to `<option>` is not supported. Instead, use [`<select defaultValue>`](#providing-an-initially-selected-option) for uncontrolled select boxes and [`<select value>`](#controlling-a-select-box-with-a-state-variable) for controlled select boxes. -- If a select box receives a `value` prop, it will be [treated as controlled.](#controlling-a-select-box-with-a-state-variable) -- A select box can't be both controlled and uncontrolled at the same time. -- A select box cannot switch between being controlled or uncontrolled over its lifetime. -- Every controlled select box needs an `onChange` event handler that synchronously updates its backing value. +- Không giống như trong HTML, việc truyền một thuộc tính `selected` cho `<option>` không được hỗ trợ. Thay vào đó, hãy sử dụng [`<select defaultValue>`](#providing-an-initially-selected-option) cho các hộp chọn không được kiểm soát và [`<select value>`](#controlling-a-select-box-with-a-state-variable) cho các hộp chọn được kiểm soát. +- Nếu một hộp chọn nhận được một prop `value`, nó sẽ được [xử lý như được kiểm soát.](#controlling-a-select-box-with-a-state-variable) +- Một hộp chọn không thể vừa được kiểm soát vừa không được kiểm soát cùng một lúc. +- Một hộp chọn không thể chuyển đổi giữa việc được kiểm soát hoặc không được kiểm soát trong suốt thời gian tồn tại của nó. +- Mọi hộp chọn được kiểm soát cần một trình xử lý sự kiện `onChange` để đồng bộ cập nhật giá trị backing của nó. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Displaying a select box with options {/*displaying-a-select-box-with-options*/} +### Hiển thị hộp chọn với các tùy chọn {/*displaying-a-select-box-with-options*/} -Render a `<select>` with a list of `<option>` components inside to display a select box. Give each `<option>` a `value` representing the data to be submitted with the form. +Hiển thị một `<select>` với một danh sách các thành phần `<option>` bên trong để hiển thị một hộp chọn. Cung cấp cho mỗi `<option>` một `value` đại diện cho dữ liệu sẽ được gửi cùng với form. <Sandpack> @@ -88,11 +88,11 @@ Render a `<select>` with a list of `<option>` components inside to display a sel export default function FruitPicker() { return ( <label> - Pick a fruit: + Chọn một loại trái cây: <select name="selectedFruit"> - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> ); @@ -103,15 +103,15 @@ export default function FruitPicker() { select { margin: 5px; } ``` -</Sandpack> +</Sandpack> --- -### Providing a label for a select box {/*providing-a-label-for-a-select-box*/} +### Cung cấp nhãn cho hộp chọn {/*providing-a-label-for-a-select-box*/} -Typically, you will place every `<select>` inside a [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) tag. This tells the browser that this label is associated with that select box. When the user clicks the label, the browser will automatically focus the select box. It's also essential for accessibility: a screen reader will announce the label caption when the user focuses the select box. +Thông thường, bạn sẽ đặt mọi `<select>` bên trong một thẻ [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label). Điều này cho trình duyệt biết rằng nhãn này được liên kết với hộp chọn đó. Khi người dùng nhấp vào nhãn, trình duyệt sẽ tự động tập trung vào hộp chọn. Nó cũng rất cần thiết cho khả năng truy cập: trình đọc màn hình sẽ thông báo chú thích nhãn khi người dùng tập trung vào hộp chọn. -If you can't nest `<select>` into a `<label>`, associate them by passing the same ID to `<select id>` and [`<label htmlFor>`.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) To avoid conflicts between multiple instances of one component, generate such an ID with [`useId`.](/reference/react/useId) +Nếu bạn không thể lồng `<select>` vào một `<label>`, hãy liên kết chúng bằng cách truyền cùng một ID cho `<select id>` và [`<label htmlFor>`.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) Để tránh xung đột giữa nhiều phiên bản của một thành phần, hãy tạo một ID như vậy bằng [`useId`.](/reference/react/useId) <Sandpack> @@ -123,21 +123,21 @@ export default function Form() { return ( <> <label> - Pick a fruit: + Chọn một loại trái cây: <select name="selectedFruit"> - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> <hr /> <label htmlFor={vegetableSelectId}> - Pick a vegetable: + Chọn một loại rau: </label> <select id={vegetableSelectId} name="selectedVegetable"> - <option value="cucumber">Cucumber</option> - <option value="corn">Corn</option> - <option value="tomato">Tomato</option> + <option value="cucumber">Dưa chuột</option> + <option value="corn">Ngô</option> + <option value="tomato">Cà chua</option> </select> </> ); @@ -150,12 +150,11 @@ select { margin: 5px; } </Sandpack> - --- -### Providing an initially selected option {/*providing-an-initially-selected-option*/} +### Cung cấp tùy chọn được chọn ban đầu {/*providing-an-initially-selected-option*/} -By default, the browser will select the first `<option>` in the list. To select a different option by default, pass that `<option>`'s `value` as the `defaultValue` to the `<select>` element. +Theo mặc định, trình duyệt sẽ chọn `<option>` đầu tiên trong danh sách. Để chọn một tùy chọn khác theo mặc định, hãy truyền `value` của `<option>` đó làm `defaultValue` cho phần tử `<select>`. <Sandpack> @@ -163,11 +162,11 @@ By default, the browser will select the first `<option>` in the list. To select export default function FruitPicker() { return ( <label> - Pick a fruit: + Chọn một loại trái cây: <select name="selectedFruit" defaultValue="orange"> - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> ); @@ -178,19 +177,19 @@ export default function FruitPicker() { select { margin: 5px; } ``` -</Sandpack> +</Sandpack> <Pitfall> -Unlike in HTML, passing a `selected` attribute to an individual `<option>` is not supported. +Không giống như trong HTML, việc truyền một thuộc tính `selected` cho một `<option>` riêng lẻ không được hỗ trợ. </Pitfall> --- -### Enabling multiple selection {/*enabling-multiple-selection*/} +### Cho phép chọn nhiều tùy chọn {/*enabling-multiple-selection*/} -Pass `multiple={true}` to the `<select>` to let the user select multiple options. In that case, if you also specify `defaultValue` to choose the initially selected options, it must be an array. +Truyền `multiple={true}` cho `<select>` để cho phép người dùng chọn nhiều tùy chọn. Trong trường hợp đó, nếu bạn cũng chỉ định `defaultValue` để chọn các tùy chọn được chọn ban đầu, nó phải là một mảng. <Sandpack> @@ -198,15 +197,15 @@ Pass `multiple={true}` to the `<select>` to let the user select multiple options export default function FruitPicker() { return ( <label> - Pick some fruits: + Chọn một vài loại trái cây: <select name="selectedFruit" defaultValue={['orange', 'banana']} multiple={true} > - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> ); @@ -221,55 +220,55 @@ select { display: block; margin-top: 10px; width: 200px; } --- -### Reading the select box value when submitting a form {/*reading-the-select-box-value-when-submitting-a-form*/} +### Đọc giá trị hộp chọn khi gửi form {/*reading-the-select-box-value-when-submitting-a-form*/} -Add a [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) around your select box with a [`<button type="submit">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) inside. It will call your `<form onSubmit>` event handler. By default, the browser will send the form data to the current URL and refresh the page. You can override that behavior by calling `e.preventDefault()`. Read the form data with [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). +Thêm một [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) xung quanh hộp chọn của bạn với một [`<button type="submit">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) bên trong. Nó sẽ gọi trình xử lý sự kiện `<form onSubmit>` của bạn. Theo mặc định, trình duyệt sẽ gửi dữ liệu form đến URL hiện tại và làm mới trang. Bạn có thể ghi đè hành vi đó bằng cách gọi `e.preventDefault()`. Đọc dữ liệu form bằng [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). <Sandpack> ```js export default function EditPost() { function handleSubmit(e) { - // Prevent the browser from reloading the page + // Ngăn trình duyệt tải lại trang e.preventDefault(); - // Read the form data + // Đọc dữ liệu form const form = e.target; const formData = new FormData(form); - // You can pass formData as a fetch body directly: + // Bạn có thể truyền formData làm một fetch body trực tiếp: fetch('/some-api', { method: form.method, body: formData }); - // You can generate a URL out of it, as the browser does by default: + // Bạn có thể tạo một URL từ nó, như trình duyệt làm theo mặc định: console.log(new URLSearchParams(formData).toString()); - // You can work with it as a plain object. + // Bạn có thể làm việc với nó như một đối tượng thuần túy. const formJson = Object.fromEntries(formData.entries()); - console.log(formJson); // (!) This doesn't include multiple select values - // Or you can get an array of name-value pairs. + console.log(formJson); // (!) Điều này không bao gồm các giá trị select nhiều + // Hoặc bạn có thể nhận được một mảng các cặp tên-giá trị. console.log([...formData.entries()]); } return ( <form method="post" onSubmit={handleSubmit}> <label> - Pick your favorite fruit: + Chọn loại trái cây yêu thích của bạn: <select name="selectedFruit" defaultValue="orange"> - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> <label> - Pick all your favorite vegetables: + Chọn tất cả các loại rau yêu thích của bạn: <select name="selectedVegetables" multiple={true} defaultValue={['corn', 'tomato']} > - <option value="cucumber">Cucumber</option> - <option value="corn">Corn</option> - <option value="tomato">Tomato</option> + <option value="cucumber">Dưa chuột</option> + <option value="corn">Ngô</option> + <option value="tomato">Cà chua</option> </select> </label> <hr /> - <button type="reset">Reset</button> - <button type="submit">Submit</button> + <button type="reset">Đặt lại</button> + <button type="submit">Gửi</button> </form> ); } @@ -284,44 +283,44 @@ label { margin-bottom: 20px; } <Note> -Give a `name` to your `<select>`, for example `<select name="selectedFruit" />`. The `name` you specified will be used as a key in the form data, for example `{ selectedFruit: "orange" }`. +Cung cấp một `name` cho `<select>` của bạn, ví dụ: `<select name="selectedFruit" />`. `name` bạn đã chỉ định sẽ được sử dụng làm khóa trong dữ liệu form, ví dụ: `{ selectedFruit: "orange" }`. -If you use `<select multiple={true}>`, the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) you'll read from the form will include each selected value as a separate name-value pair. Look closely at the console logs in the example above. +Nếu bạn sử dụng `<select multiple={true}>`, [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) bạn sẽ đọc từ form sẽ bao gồm mỗi giá trị được chọn như một cặp tên-giá trị riêng biệt. Xem kỹ các bản ghi console trong ví dụ trên. </Note> <Pitfall> -By default, *any* `<button>` inside a `<form>` will submit it. This can be surprising! If you have your own custom `Button` React component, consider returning [`<button type="button">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) instead of `<button>`. Then, to be explicit, use `<button type="submit">` for buttons that *are* supposed to submit the form. +Theo mặc định, *bất kỳ* `<button>` nào bên trong một `<form>` sẽ gửi nó. Điều này có thể gây ngạc nhiên! Nếu bạn có thành phần React `Button` tùy chỉnh của riêng mình, hãy cân nhắc trả về [`<button type="button">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) thay vì `<button>`. Sau đó, để rõ ràng, hãy sử dụng `<button type="submit">` cho các nút *được* cho là để gửi form. </Pitfall> --- -### Controlling a select box with a state variable {/*controlling-a-select-box-with-a-state-variable*/} +### Kiểm soát hộp chọn bằng một biến trạng thái {/*controlling-a-select-box-with-a-state-variable*/} -A select box like `<select />` is *uncontrolled.* Even if you [pass an initially selected value](#providing-an-initially-selected-option) like `<select defaultValue="orange" />`, your JSX only specifies the initial value, not the value right now. +Một hộp chọn như `<select />` là *không được kiểm soát*. Ngay cả khi bạn [truyền một giá trị được chọn ban đầu](#providing-an-initially-selected-option) như `<select defaultValue="orange" />`, JSX của bạn chỉ định giá trị ban đầu, không phải giá trị ngay bây giờ. -**To render a _controlled_ select box, pass the `value` prop to it.** React will force the select box to always have the `value` you passed. Typically, you will control a select box by declaring a [state variable:](/reference/react/useState) +**Để hiển thị một hộp chọn _được kiểm soát_, hãy truyền prop `value` cho nó.** React sẽ buộc hộp chọn luôn có `value` bạn đã truyền. Thông thường, bạn sẽ kiểm soát một hộp chọn bằng cách khai báo một [biến trạng thái:](/reference/react/useState) ```js {2,6,7} function FruitPicker() { - const [selectedFruit, setSelectedFruit] = useState('orange'); // Declare a state variable... + const [selectedFruit, setSelectedFruit] = useState('orange'); // Khai báo một biến trạng thái... // ... return ( <select - value={selectedFruit} // ...force the select's value to match the state variable... - onChange={e => setSelectedFruit(e.target.value)} // ... and update the state variable on any change! + value={selectedFruit} // ...buộc giá trị của select khớp với biến trạng thái... + onChange={e => setSelectedFruit(e.target.value)} // ...và cập nhật biến trạng thái khi có bất kỳ thay đổi nào! > - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> ); } ``` -This is useful if you want to re-render some part of the UI in response to every selection. +Điều này hữu ích nếu bạn muốn hiển thị lại một số phần của UI để đáp ứng với mọi lựa chọn. <Sandpack> @@ -334,19 +333,19 @@ export default function FruitPicker() { return ( <> <label> - Pick a fruit: + Chọn một loại trái cây: <select value={selectedFruit} onChange={e => setSelectedFruit(e.target.value)} > - <option value="apple">Apple</option> - <option value="banana">Banana</option> - <option value="orange">Orange</option> + <option value="apple">Táo</option> + <option value="banana">Chuối</option> + <option value="orange">Cam</option> </select> </label> <hr /> <label> - Pick all your favorite vegetables: + Chọn tất cả các loại rau yêu thích của bạn: <select multiple={true} value={selectedVegs} @@ -356,14 +355,14 @@ export default function FruitPicker() { setSelectedVegs(values); }} > - <option value="cucumber">Cucumber</option> - <option value="corn">Corn</option> - <option value="tomato">Tomato</option> + <option value="cucumber">Dưa chuột</option> + <option value="corn">Ngô</option> + <option value="tomato">Cà chua</option> </select> </label> <hr /> - <p>Your favorite fruit: {selectedFruit}</p> - <p>Your favorite vegetables: {selectedVegs.join(', ')}</p> + <p>Loại trái cây yêu thích của bạn: {selectedFruit}</p> + <p>Các loại rau yêu thích của bạn: {selectedVegs.join(', ')}</p> </> ); } @@ -377,8 +376,8 @@ select { margin-bottom: 10px; display: block; } <Pitfall> -**If you pass `value` without `onChange`, it will be impossible to select an option.** When you control a select box by passing some `value` to it, you *force* it to always have the value you passed. So if you pass a state variable as a `value` but forget to update that state variable synchronously during the `onChange` event handler, React will revert the select box after every keystroke back to the `value` that you specified. +**Nếu bạn truyền `value` mà không có `onChange`, bạn sẽ không thể chọn một tùy chọn.** Khi bạn kiểm soát một hộp chọn bằng cách truyền một số `value` cho nó, bạn *buộc* nó luôn có giá trị bạn đã truyền. Vì vậy, nếu bạn truyền một biến trạng thái làm `value` nhưng quên cập nhật biến trạng thái đó một cách đồng bộ trong trình xử lý sự kiện `onChange`, React sẽ hoàn nguyên hộp chọn sau mỗi lần nhấn phím trở lại `value` mà bạn đã chỉ định. -Unlike in HTML, passing a `selected` attribute to an individual `<option>` is not supported. +Không giống như trong HTML, việc truyền một thuộc tính `selected` cho một `<option>` riêng lẻ không được hỗ trợ. </Pitfall> diff --git a/src/content/reference/react-dom/components/style.md b/src/content/reference/react-dom/components/style.md index 39df92d9f..790b09137 100644 --- a/src/content/reference/react-dom/components/style.md +++ b/src/content/reference/react-dom/components/style.md @@ -4,7 +4,7 @@ style: "<style>" <Intro> -The [built-in browser `<style>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style) lets you add inline CSS stylesheets to your document. +[Thành phần `<style>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style) cho phép bạn thêm các biểu định kiểu CSS nội tuyến vào tài liệu của mình. ```js <style>{` p { color: red; } `}</style> @@ -16,58 +16,58 @@ The [built-in browser `<style>` component](https://developer.mozilla.org/en-US/d --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<style>` {/*style*/} -To add inline styles to your document, render the [built-in browser `<style>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style). You can render `<style>` from any component and React will [in certain cases](#special-rendering-behavior) place the corresponding DOM element in the document head and de-duplicate identical styles. +Để thêm các kiểu nội tuyến vào tài liệu của bạn, hãy hiển thị [thành phần `<style>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style). Bạn có thể hiển thị `<style>` từ bất kỳ thành phần nào và React sẽ [trong một số trường hợp](#special-rendering-behavior) đặt phần tử DOM tương ứng vào đầu tài liệu và loại bỏ các kiểu giống hệt nhau. ```js <style>{` p { color: red; } `}</style> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<style>` supports all [common element props.](/reference/react-dom/components/common#props) +`<style>` hỗ trợ tất cả [các props phần tử thông thường.](/reference/react-dom/components/common#props) -* `children`: a string, required. The contents of the stylesheet. -* `precedence`: a string. Tells React where to rank the `<style>` DOM node relative to others in the document `<head>`, which determines which stylesheet can override the other. React will infer that precedence values it discovers first are "lower" and precedence values it discovers later are "higher". Many style systems can work fine using a single precedence value because style rules are atomic. Stylesheets with the same precedence go together whether they are `<link>` or inline `<style>` tags or loaded using [`preinit`](/reference/react-dom/preinit) functions. -* `href`: a string. Allows React to [de-duplicate styles](#special-rendering-behavior) that have the same `href`. -* `media`: a string. Restricts the stylesheet to a certain [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries). -* `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. -* `title`: a string. Specifies the name of an [alternative stylesheet](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets). +* `children`: một chuỗi, bắt buộc. Nội dung của biểu định kiểu. +* `precedence`: một chuỗi. Cho React biết vị trí xếp hạng nút DOM `<style>` so với các nút khác trong `<head>` của tài liệu, điều này xác định biểu định kiểu nào có thể ghi đè lên biểu định kiểu khác. React sẽ suy ra rằng các giá trị độ ưu tiên mà nó khám phá đầu tiên là "thấp hơn" và các giá trị độ ưu tiên mà nó khám phá sau là "cao hơn". Nhiều hệ thống kiểu có thể hoạt động tốt bằng cách sử dụng một giá trị độ ưu tiên duy nhất vì các quy tắc kiểu là nguyên tử. Các biểu định kiểu có cùng độ ưu tiên đi cùng nhau cho dù chúng là thẻ `<link>` hay thẻ `<style>` nội tuyến hoặc được tải bằng các hàm [`preinit`](/reference/react-dom/preinit). +* `href`: một chuỗi. Cho phép React [loại bỏ các kiểu trùng lặp](#special-rendering-behavior) có cùng `href`. +* `media`: một chuỗi. Hạn chế biểu định kiểu cho một [truy vấn phương tiện](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries) nhất định. +* `nonce`: một chuỗi. Một [nonce mật mã để cho phép tài nguyên](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) khi sử dụng Chính sách bảo mật nội dung nghiêm ngặt. +* `title`: một chuỗi. Chỉ định tên của một [biểu định kiểu thay thế](https://developer.mozilla.org/en-US/docs/Web/CSS/Alternative_style_sheets). -Props that are **not recommended** for use with React: +Các props **không được khuyến nghị** sử dụng với React: -* `blocking`: a string. If set to `"render"`, instructs the browser not to render the page until the stylesheet is loaded. React provides more fine-grained control using Suspense. +* `blocking`: một chuỗi. Nếu được đặt thành `"render"`, hãy hướng dẫn trình duyệt không hiển thị trang cho đến khi biểu định kiểu được tải. React cung cấp khả năng kiểm soát chi tiết hơn bằng cách sử dụng Suspense. -#### Special rendering behavior {/*special-rendering-behavior*/} +#### Hành vi hiển thị đặc biệt {/*special-rendering-behavior*/} -React can move `<style>` components to the document's `<head>`, de-duplicate identical stylesheets, and [suspend](/reference/react/Suspense) while the stylesheet is loading. +React có thể di chuyển các thành phần `<style>` đến `<head>` của tài liệu, loại bỏ các biểu định kiểu giống hệt nhau và [tạm ngưng](/reference/react/Suspense) trong khi biểu định kiểu đang tải. -To opt into this behavior, provide the `href` and `precedence` props. React will de-duplicate styles if they have the same `href`. The precedence prop tells React where to rank the `<style>` DOM node relative to others in the document `<head>`, which determines which stylesheet can override the other. +Để chọn tham gia hành vi này, hãy cung cấp các props `href` và `precedence`. React sẽ loại bỏ các kiểu trùng lặp nếu chúng có cùng `href`. Prop precedence cho React biết vị trí xếp hạng nút DOM `<style>` so với các nút khác trong `<head>` của tài liệu, điều này xác định biểu định kiểu nào có thể ghi đè lên biểu định kiểu khác. -This special treatment comes with two caveats: +Cách xử lý đặc biệt này đi kèm với hai lưu ý: -* React will ignore changes to props after the style has been rendered. (React will issue a warning in development if this happens.) -* React will drop all extraneous props when using the `precedence` prop (beyond `href` and `precedence`). -* React may leave the style in the DOM even after the component that rendered it has been unmounted. +* React sẽ bỏ qua các thay đổi đối với các props sau khi kiểu đã được hiển thị. (React sẽ đưa ra cảnh báo trong quá trình phát triển nếu điều này xảy ra.) +* React sẽ loại bỏ tất cả các props thừa khi sử dụng prop `precedence` (ngoài `href` và `precedence`). +* React có thể để lại kiểu trong DOM ngay cả sau khi thành phần hiển thị nó đã bị hủy gắn kết. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering an inline CSS stylesheet {/*rendering-an-inline-css-stylesheet*/} +### Hiển thị biểu định kiểu CSS nội tuyến {/*rendering-an-inline-css-stylesheet*/} -If a component depends on certain CSS styles in order to be displayed correctly, you can render an inline stylesheet within the component. +Nếu một thành phần phụ thuộc vào các kiểu CSS nhất định để hiển thị chính xác, bạn có thể hiển thị một biểu định kiểu nội tuyến trong thành phần đó. -The `href` prop should uniquely identify the stylesheet, because React will de-duplicate stylesheets that have the same `href`. -If you supply a `precedence` prop, React will reorder inline stylesheets based on the order these values appear in the component tree. +Prop `href` phải xác định duy nhất biểu định kiểu, vì React sẽ loại bỏ các biểu định kiểu trùng lặp có cùng `href`. +Nếu bạn cung cấp một prop `precedence`, React sẽ sắp xếp lại các biểu định kiểu nội tuyến dựa trên thứ tự các giá trị này xuất hiện trong cây thành phần. -Inline stylesheets will not trigger Suspense boundaries while they're loading. -Even if they load async resources like fonts or images. +Các biểu định kiểu nội tuyến sẽ không kích hoạt các ranh giới Suspense trong khi chúng đang tải. +Ngay cả khi chúng tải các tài nguyên không đồng bộ như phông chữ hoặc hình ảnh. <SandpackWithHTMLOutput> diff --git a/src/content/reference/react-dom/components/textarea.md b/src/content/reference/react-dom/components/textarea.md index 9bd29fa38..992a7a7fa 100644 --- a/src/content/reference/react-dom/components/textarea.md +++ b/src/content/reference/react-dom/components/textarea.md @@ -4,7 +4,7 @@ title: "<textarea>" <Intro> -The [built-in browser `<textarea>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) lets you render a multiline text input. +[Thành phần `<textarea>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) cho phép bạn hiển thị một trường nhập văn bản nhiều dòng. ```js <textarea /> @@ -16,72 +16,72 @@ The [built-in browser `<textarea>` component](https://developer.mozilla.org/en-U --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<textarea>` {/*textarea*/} -To display a text area, render the [built-in browser `<textarea>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) component. +Để hiển thị một vùng văn bản, hãy hiển thị thành phần [`<textarea>` tích hợp sẵn của trình duyệt](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea). ```js <textarea name="postContent" /> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<textarea>` supports all [common element props.](/reference/react-dom/components/common#props) +`<textarea>` hỗ trợ tất cả [các thuộc tính phần tử chung.](/reference/react-dom/components/common#props) -You can [make a text area controlled](#controlling-a-text-area-with-a-state-variable) by passing a `value` prop: +Bạn có thể [làm cho một vùng văn bản được kiểm soát](#controlling-a-text-area-with-a-state-variable) bằng cách truyền một thuộc tính `value`: -* `value`: A string. Controls the text inside the text area. +* `value`: Một chuỗi. Kiểm soát văn bản bên trong vùng văn bản. -When you pass `value`, you must also pass an `onChange` handler that updates the passed value. +Khi bạn truyền `value`, bạn cũng phải truyền một trình xử lý `onChange` để cập nhật giá trị đã truyền. -If your `<textarea>` is uncontrolled, you may pass the `defaultValue` prop instead: +Nếu `<textarea>` của bạn không được kiểm soát, bạn có thể truyền thuộc tính `defaultValue` thay thế: -* `defaultValue`: A string. Specifies [the initial value](#providing-an-initial-value-for-a-text-area) for a text area. +* `defaultValue`: Một chuỗi. Chỉ định [giá trị ban đầu](#providing-an-initial-value-for-a-text-area) cho một vùng văn bản. -These `<textarea>` props are relevant both for uncontrolled and controlled text areas: +Các thuộc tính `<textarea>` này có liên quan cho cả vùng văn bản được kiểm soát và không được kiểm soát: -* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#autocomplete): Either `'on'` or `'off'`. Specifies the autocomplete behavior. -* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#autofocus): A boolean. If `true`, React will focus the element on mount. -* `children`: `<textarea>` does not accept children. To set the initial value, use `defaultValue`. -* [`cols`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#cols): A number. Specifies the default width in average character widths. Defaults to `20`. -* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#disabled): A boolean. If `true`, the input will not be interactive and will appear dimmed. -* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#form): A string. Specifies the `id` of the `<form>` this input belongs to. If omitted, it's the closest parent form. -* [`maxLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#maxlength): A number. Specifies the maximum length of text. -* [`minLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#minlength): A number. Specifies the minimum length of text. -* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name): A string. Specifies the name for this input that's [submitted with the form.](#reading-the-textarea-value-when-submitting-a-form) -* `onChange`: An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Required for [controlled text areas.](#controlling-a-text-area-with-a-state-variable) Fires immediately when the input's value is changed by the user (for example, it fires on every keystroke). Behaves like the browser [`input` event.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) -* `onChangeCapture`: A version of `onChange` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires immediately when the value is changed by the user. For historical reasons, in React it is idiomatic to use `onChange` instead which works similarly. -* `onInputCapture`: A version of `onInput` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires if an input fails validation on form submit. Unlike the built-in `invalid` event, the React `onInvalid` event bubbles. -* `onInvalidCapture`: A version of `onInvalid` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement/select_event): An [`Event` handler](/reference/react-dom/components/common#event-handler) function. Fires after the selection inside the `<textarea>` changes. React extends the `onSelect` event to also fire for empty selection and on edits (which may affect the selection). -* `onSelectCapture`: A version of `onSelect` that fires in the [capture phase.](/learn/responding-to-events#capture-phase-events) -* [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#placeholder): A string. Displayed in a dimmed color when the text area value is empty. -* [`readOnly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#readonly): A boolean. If `true`, the text area is not editable by the user. -* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#required): A boolean. If `true`, the value must be provided for the form to submit. -* [`rows`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#rows): A number. Specifies the default height in average character heights. Defaults to `2`. -* [`wrap`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#wrap): Either `'hard'`, `'soft'`, or `'off'`. Specifies how the text should be wrapped when submitting a form. +* [`autoComplete`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#autocomplete): Hoặc `'on'` hoặc `'off'`. Chỉ định hành vi tự động hoàn thành. +* [`autoFocus`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#autofocus): Một boolean. Nếu `true`, React sẽ tập trung vào phần tử khi mount. +* `children`: `<textarea>` không chấp nhận children. Để đặt giá trị ban đầu, hãy sử dụng `defaultValue`. +* [`cols`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#cols): Một số. Chỉ định chiều rộng mặc định theo chiều rộng ký tự trung bình. Mặc định là `20`. +* [`disabled`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#disabled): Một boolean. Nếu `true`, đầu vào sẽ không tương tác và sẽ xuất hiện mờ đi. +* [`form`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#form): Một chuỗi. Chỉ định `id` của `<form>` mà đầu vào này thuộc về. Nếu bỏ qua, nó là biểu mẫu cha gần nhất. +* [`maxLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#maxlength): Một số. Chỉ định độ dài tối đa của văn bản. +* [`minLength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#minlength): Một số. Chỉ định độ dài tối thiểu của văn bản. +* [`name`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name): Một chuỗi. Chỉ định tên cho đầu vào này được [gửi cùng với biểu mẫu.](#reading-the-textarea-value-when-submitting-a-form) +* `onChange`: Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Bắt buộc đối với [các vùng văn bản được kiểm soát.](#controlling-a-text-area-with-a-state-variable) Kích hoạt ngay lập tức khi giá trị của đầu vào được thay đổi bởi người dùng (ví dụ: nó kích hoạt trên mỗi lần nhấn phím). Hoạt động giống như trình duyệt [`input` event.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event) +* `onChangeCapture`: Một phiên bản của `onChange` kích hoạt trong [capture phase.](/learn/responding-to-events#capture-phase-events) +* [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt ngay lập tức khi giá trị được thay đổi bởi người dùng. Vì lý do lịch sử, trong React, thành ngữ là sử dụng `onChange` thay thế, hoạt động tương tự. +* `onInputCapture`: Một phiên bản của `onInput` kích hoạt trong [capture phase.](/learn/responding-to-events#capture-phase-events) +* [`onInvalid`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt nếu một đầu vào không vượt qua xác thực khi gửi biểu mẫu. Không giống như sự kiện `invalid` tích hợp, sự kiện `onInvalid` của React nổi lên. +* `onInvalidCapture`: Một phiên bản của `onInvalid` kích hoạt trong [capture phase.](/learn/responding-to-events#capture-phase-events) +* [`onSelect`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement/select_event): Một hàm [`Event` handler](/reference/react-dom/components/common#event-handler). Kích hoạt sau khi lựa chọn bên trong `<textarea>` thay đổi. React mở rộng sự kiện `onSelect` để cũng kích hoạt cho lựa chọn trống và khi chỉnh sửa (có thể ảnh hưởng đến lựa chọn). +* `onSelectCapture`: Một phiên bản của `onSelect` kích hoạt trong [capture phase.](/learn/responding-to-events#capture-phase-events) +* [`placeholder`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#placeholder): Một chuỗi. Được hiển thị bằng màu mờ khi giá trị của vùng văn bản trống. +* [`readOnly`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#readonly): Một boolean. Nếu `true`, vùng văn bản không thể chỉnh sửa được bởi người dùng. +* [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#required): Một boolean. Nếu `true`, giá trị phải được cung cấp để biểu mẫu được gửi. +* [`rows`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#rows): Một số. Chỉ định chiều cao mặc định theo chiều cao ký tự trung bình. Mặc định là `2`. +* [`wrap`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#wrap): Hoặc `'hard'`, `'soft'`, hoặc `'off'`. Chỉ định cách văn bản sẽ được xuống dòng khi gửi biểu mẫu. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -- Passing children like `<textarea>something</textarea>` is not allowed. [Use `defaultValue` for initial content.](#providing-an-initial-value-for-a-text-area) -- If a text area receives a string `value` prop, it will be [treated as controlled.](#controlling-a-text-area-with-a-state-variable) -- A text area can't be both controlled and uncontrolled at the same time. -- A text area cannot switch between being controlled or uncontrolled over its lifetime. -- Every controlled text area needs an `onChange` event handler that synchronously updates its backing value. +* Không được phép truyền children như `<textarea>something</textarea>`. [Sử dụng `defaultValue` cho nội dung ban đầu.](#providing-an-initial-value-for-a-text-area) +* Nếu một vùng văn bản nhận được một thuộc tính `value` chuỗi, nó sẽ được [xử lý như được kiểm soát.](#controlling-a-text-area-with-a-state-variable) +* Một vùng văn bản không thể vừa được kiểm soát vừa không được kiểm soát cùng một lúc. +* Một vùng văn bản không thể chuyển đổi giữa việc được kiểm soát hoặc không được kiểm soát trong suốt vòng đời của nó. +* Mỗi vùng văn bản được kiểm soát cần một trình xử lý sự kiện `onChange` để đồng bộ cập nhật giá trị sao lưu của nó. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Displaying a text area {/*displaying-a-text-area*/} +### Hiển thị một vùng văn bản {/*displaying-a-text-area*/} -Render `<textarea>` to display a text area. You can specify its default size with the [`rows`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#rows) and [`cols`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#cols) attributes, but by default the user will be able to resize it. To disable resizing, you can specify `resize: none` in the CSS. +Hiển thị `<textarea>` để hiển thị một vùng văn bản. Bạn có thể chỉ định kích thước mặc định của nó với các thuộc tính [`rows`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#rows) và [`cols`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#cols), nhưng theo mặc định, người dùng sẽ có thể thay đổi kích thước nó. Để tắt thay đổi kích thước, bạn có thể chỉ định `resize: none` trong CSS. <Sandpack> @@ -107,11 +107,11 @@ label, textarea { display: block; } --- -### Providing a label for a text area {/*providing-a-label-for-a-text-area*/} +### Cung cấp một nhãn cho một vùng văn bản {/*providing-a-label-for-a-text-area*/} -Typically, you will place every `<textarea>` inside a [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) tag. This tells the browser that this label is associated with that text area. When the user clicks the label, the browser will focus the text area. It's also essential for accessibility: a screen reader will announce the label caption when the user focuses the text area. +Thông thường, bạn sẽ đặt mọi `<textarea>` bên trong một thẻ [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label). Điều này cho trình duyệt biết rằng nhãn này được liên kết với vùng văn bản đó. Khi người dùng nhấp vào nhãn, trình duyệt sẽ tập trung vào vùng văn bản. Nó cũng rất cần thiết cho khả năng truy cập: một trình đọc màn hình sẽ thông báo chú thích nhãn khi người dùng tập trung vào vùng văn bản. -If you can't nest `<textarea>` into a `<label>`, associate them by passing the same ID to `<textarea id>` and [`<label htmlFor>`.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) To avoid conflicts between instances of one component, generate such an ID with [`useId`.](/reference/react/useId) +Nếu bạn không thể lồng `<textarea>` vào một `<label>`, hãy liên kết chúng bằng cách truyền cùng một ID cho `<textarea id>` và [`<label htmlFor>`.](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor) Để tránh xung đột giữa các phiên bản của một thành phần, hãy tạo một ID như vậy với [`useId`.](/reference/react/useId) <Sandpack> @@ -144,9 +144,9 @@ input { margin: 5px; } --- -### Providing an initial value for a text area {/*providing-an-initial-value-for-a-text-area*/} +### Cung cấp một giá trị ban đầu cho một vùng văn bản {/*providing-an-initial-value-for-a-text-area*/} -You can optionally specify the initial value for the text area. Pass it as the `defaultValue` string. +Bạn có thể tùy chọn chỉ định giá trị ban đầu cho vùng văn bản. Truyền nó dưới dạng chuỗi `defaultValue`. <Sandpack> @@ -177,15 +177,16 @@ label, textarea { display: block; } <Pitfall> -Unlike in HTML, passing initial text like `<textarea>Some content</textarea>` is not supported. +Không giống như trong HTML, việc truyền văn bản ban đầu như `<textarea>Some content</textarea>` không được hỗ trợ. </Pitfall> --- -### Reading the text area value when submitting a form {/*reading-the-text-area-value-when-submitting-a-form*/} +### Đọc giá trị vùng văn bản khi gửi biểu mẫu {/*reading-the-textarea-value-when-submitting-a-form*/} + +Thêm một [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) xung quanh vùng văn bản của bạn với một [`<button type="submit">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) bên trong. Nó sẽ gọi trình xử lý sự kiện `<form onSubmit>` của bạn. Theo mặc định, trình duyệt sẽ gửi dữ liệu biểu mẫu đến URL hiện tại và làm mới trang. Bạn có thể ghi đè hành vi đó bằng cách gọi `e.preventDefault()`. Đọc dữ liệu biểu mẫu với [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). -Add a [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) around your textarea with a [`<button type="submit">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) inside. It will call your `<form onSubmit>` event handler. By default, the browser will send the form data to the current URL and refresh the page. You can override that behavior by calling `e.preventDefault()`. Read the form data with [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). <Sandpack> ```js @@ -237,38 +238,38 @@ input { margin: 5px; } <Note> -Give a `name` to your `<textarea>`, for example `<textarea name="postContent" />`. The `name` you specified will be used as a key in the form data, for example `{ postContent: "Your post" }`. +Cung cấp một `name` cho `<textarea>` của bạn, ví dụ: `<textarea name="postContent" />`. `name` bạn đã chỉ định sẽ được sử dụng làm khóa trong dữ liệu biểu mẫu, ví dụ: `{ postContent: "Your post" }`. </Note> <Pitfall> -By default, *any* `<button>` inside a `<form>` will submit it. This can be surprising! If you have your own custom `Button` React component, consider returning [`<button type="button">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) instead of `<button>`. Then, to be explicit, use `<button type="submit">` for buttons that *are* supposed to submit the form. +Theo mặc định, *bất kỳ* `<button>` nào bên trong một `<form>` sẽ gửi nó. Điều này có thể gây ngạc nhiên! Nếu bạn có thành phần React `Button` tùy chỉnh của riêng mình, hãy cân nhắc trả về [`<button type="button">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/button) thay vì `<button>`. Sau đó, để rõ ràng, hãy sử dụng `<button type="submit">` cho các nút *được* cho là để gửi biểu mẫu. </Pitfall> --- -### Controlling a text area with a state variable {/*controlling-a-text-area-with-a-state-variable*/} +### Kiểm soát một vùng văn bản với một biến trạng thái {/*controlling-a-text-area-with-a-state-variable*/} -A text area like `<textarea />` is *uncontrolled.* Even if you [pass an initial value](#providing-an-initial-value-for-a-text-area) like `<textarea defaultValue="Initial text" />`, your JSX only specifies the initial value, not the value right now. +Một vùng văn bản như `<textarea />` là *không được kiểm soát*. Ngay cả khi bạn [truyền một giá trị ban đầu](#providing-an-initial-value-for-a-text-area) như `<textarea defaultValue="Initial text" />`, JSX của bạn chỉ định giá trị ban đầu, không phải giá trị ngay bây giờ. -**To render a _controlled_ text area, pass the `value` prop to it.** React will force the text area to always have the `value` you passed. Typically, you will control a text area by declaring a [state variable:](/reference/react/useState) +**Để hiển thị một vùng văn bản _được kiểm soát_, hãy truyền thuộc tính `value` cho nó.** React sẽ buộc vùng văn bản luôn có `value` mà bạn đã truyền. Thông thường, bạn sẽ kiểm soát một vùng văn bản bằng cách khai báo một [biến trạng thái:](/reference/react/useState) ```js {2,6,7} function NewPost() { - const [postContent, setPostContent] = useState(''); // Declare a state variable... + const [postContent, setPostContent] = useState(''); // Khai báo một biến trạng thái... // ... return ( <textarea - value={postContent} // ...force the input's value to match the state variable... - onChange={e => setPostContent(e.target.value)} // ... and update the state variable on any edits! + value={postContent} // ...buộc giá trị của đầu vào khớp với biến trạng thái... + onChange={e => setPostContent(e.target.value)} // ...và cập nhật biến trạng thái trên bất kỳ chỉnh sửa nào! /> ); } ``` -This is useful if you want to re-render some part of the UI in response to every keystroke. +Điều này hữu ích nếu bạn muốn hiển thị lại một phần của giao diện người dùng để đáp ứng với mỗi lần nhấn phím. <Sandpack> @@ -330,94 +331,93 @@ textarea { display: block; margin-top: 5px; margin-bottom: 10px; } <Pitfall> -**If you pass `value` without `onChange`, it will be impossible to type into the text area.** When you control a text area by passing some `value` to it, you *force* it to always have the value you passed. So if you pass a state variable as a `value` but forget to update that state variable synchronously during the `onChange` event handler, React will revert the text area after every keystroke back to the `value` that you specified. +**Nếu bạn truyền `value` mà không có `onChange`, sẽ không thể nhập vào vùng văn bản.** Khi bạn kiểm soát một vùng văn bản bằng cách truyền một số `value` cho nó, bạn *buộc* nó luôn có giá trị mà bạn đã truyền. Vì vậy, nếu bạn truyền một biến trạng thái làm `value` nhưng quên cập nhật biến trạng thái đó một cách đồng bộ trong trình xử lý sự kiện `onChange`, React sẽ hoàn nguyên vùng văn bản sau mỗi lần nhấn phím trở lại `value` mà bạn đã chỉ định. </Pitfall> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My text area doesn't update when I type into it {/*my-text-area-doesnt-update-when-i-type-into-it*/} +### Vùng văn bản của tôi không cập nhật khi tôi nhập vào nó {/*my-text-area-doesnt-update-when-i-type-into-it*/} -If you render a text area with `value` but no `onChange`, you will see an error in the console: +Nếu bạn hiển thị một vùng văn bản với `value` nhưng không có `onChange`, bạn sẽ thấy một lỗi trong bảng điều khiển: ```js -// 🔴 Bug: controlled text area with no onChange handler +// 🔴 Lỗi: vùng văn bản được kiểm soát không có trình xử lý onChange <textarea value={something} /> ``` <ConsoleBlock level="error"> -You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`. +Bạn đã cung cấp một thuộc tính `value` cho một trường biểu mẫu mà không có trình xử lý `onChange`. Điều này sẽ hiển thị một trường chỉ đọc. Nếu trường có thể thay đổi, hãy sử dụng `defaultValue`. Nếu không, hãy đặt `onChange` hoặc `readOnly`. </ConsoleBlock> -As the error message suggests, if you only wanted to [specify the *initial* value,](#providing-an-initial-value-for-a-text-area) pass `defaultValue` instead: +Như thông báo lỗi gợi ý, nếu bạn chỉ muốn [chỉ định giá trị *ban đầu*,](#providing-an-initial-value-for-a-text-area) hãy truyền `defaultValue` thay thế: ```js -// ✅ Good: uncontrolled text area with an initial value +// ✅ Tốt: vùng văn bản không được kiểm soát với một giá trị ban đầu <textarea defaultValue={something} /> ``` -If you want [to control this text area with a state variable,](#controlling-a-text-area-with-a-state-variable) specify an `onChange` handler: +Nếu bạn muốn [kiểm soát vùng văn bản này với một biến trạng thái,](#controlling-a-text-area-with-a-state-variable) hãy chỉ định một trình xử lý `onChange`: ```js -// ✅ Good: controlled text area with onChange +// ✅ Tốt: vùng văn bản được kiểm soát với onChange <textarea value={something} onChange={e => setSomething(e.target.value)} /> ``` -If the value is intentionally read-only, add a `readOnly` prop to suppress the error: +Nếu giá trị cố ý chỉ đọc, hãy thêm một thuộc tính `readOnly` để ngăn chặn lỗi: ```js -// ✅ Good: readonly controlled text area without on change +// ✅ Tốt: vùng văn bản được kiểm soát chỉ đọc không có on change <textarea value={something} readOnly={true} /> ``` --- -### My text area caret jumps to the beginning on every keystroke {/*my-text-area-caret-jumps-to-the-beginning-on-every-keystroke*/} +### Dấu mũ vùng văn bản của tôi nhảy về đầu mỗi khi nhấn phím {/*my-text-area-caret-jumps-to-the-beginning-on-every-keystroke*/} -If you [control a text area,](#controlling-a-text-area-with-a-state-variable) you must update its state variable to the text area's value from the DOM during `onChange`. +Nếu bạn [kiểm soát một vùng văn bản,](#controlling-a-text-area-with-a-state-variable) bạn phải cập nhật biến trạng thái của nó thành giá trị của vùng văn bản từ DOM trong `onChange`. -You can't update it to something other than `e.target.value`: +Bạn không thể cập nhật nó thành một cái gì đó khác với `e.target.value`: ```js function handleChange(e) { - // 🔴 Bug: updating an input to something other than e.target.value + // 🔴 Lỗi: cập nhật một đầu vào thành một cái gì đó khác với e.target.value setFirstName(e.target.value.toUpperCase()); } ``` -You also can't update it asynchronously: +Bạn cũng không thể cập nhật nó một cách không đồng bộ: ```js function handleChange(e) { - // 🔴 Bug: updating an input asynchronously + // 🔴 Lỗi: cập nhật một đầu vào một cách không đồng bộ setTimeout(() => { setFirstName(e.target.value); }, 100); } ``` -To fix your code, update it synchronously to `e.target.value`: +Để sửa mã của bạn, hãy cập nhật nó một cách đồng bộ thành `e.target.value`: ```js function handleChange(e) { - // ✅ Updating a controlled input to e.target.value synchronously + // ✅ Cập nhật một đầu vào được kiểm soát thành e.target.value một cách đồng bộ setFirstName(e.target.value); } ``` -If this doesn't fix the problem, it's possible that the text area gets removed and re-added from the DOM on every keystroke. This can happen if you're accidentally [resetting state](/learn/preserving-and-resetting-state) on every re-render. For example, this can happen if the text area or one of its parents always receives a different `key` attribute, or if you nest component definitions (which is not allowed in React and causes the "inner" component to remount on every render). +Nếu điều này không khắc phục được sự cố, có thể là vùng văn bản bị xóa và thêm lại từ DOM trên mỗi lần nhấn phím. Điều này có thể xảy ra nếu bạn vô tình [đặt lại trạng thái](/learn/preserving-and-resetting-state) trên mỗi lần hiển thị lại. Ví dụ: điều này có thể xảy ra nếu vùng văn bản hoặc một trong các phần tử cha của nó luôn nhận được một thuộc tính `key` khác nhau, hoặc nếu bạn lồng các định nghĩa thành phần (điều này không được phép trong React và khiến thành phần "bên trong" được gắn lại trên mỗi lần hiển thị). --- -### I'm getting an error: "A component is changing an uncontrolled input to be controlled" {/*im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled*/} - +### Tôi đang gặp lỗi: "Một thành phần đang thay đổi một đầu vào không được kiểm soát thành được kiểm soát" {/*im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled*/} -If you provide a `value` to the component, it must remain a string throughout its lifetime. +Nếu bạn cung cấp một `value` cho thành phần, nó phải vẫn là một chuỗi trong suốt vòng đời của nó. -You cannot pass `value={undefined}` first and later pass `value="some string"` because React won't know whether you want the component to be uncontrolled or controlled. A controlled component should always receive a string `value`, not `null` or `undefined`. +Bạn không thể truyền `value={undefined}` trước và sau đó truyền `value="some string"` vì React sẽ không biết bạn muốn thành phần không được kiểm soát hay được kiểm soát. Một thành phần được kiểm soát phải luôn nhận được một chuỗi `value`, không phải `null` hoặc `undefined`. -If your `value` is coming from an API or a state variable, it might be initialized to `null` or `undefined`. In that case, either set it to an empty string (`''`) initially, or pass `value={someValue ?? ''}` to ensure `value` is a string. +Nếu `value` của bạn đến từ một API hoặc một biến trạng thái, nó có thể được khởi tạo thành `null` hoặc `undefined`. Trong trường hợp đó, hãy đặt nó thành một chuỗi trống (`''`) ban đầu hoặc truyền `value={someValue ?? ''}` để đảm bảo `value` là một chuỗi. diff --git a/src/content/reference/react-dom/components/title.md b/src/content/reference/react-dom/components/title.md index 005939046..5e0bac283 100644 --- a/src/content/reference/react-dom/components/title.md +++ b/src/content/reference/react-dom/components/title.md @@ -4,7 +4,7 @@ title: "<title>" <Intro> -The [built-in browser `<title>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title) lets you specify the title of the document. +[Built-in browser `<title>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title) cho phép bạn chỉ định tiêu đề của tài liệu. ```js <title>My Blog</title> @@ -16,45 +16,45 @@ The [built-in browser `<title>` component](https://developer.mozilla.org/en-US/d --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<title>` {/*title*/} -To specify the title of the document, render the [built-in browser `<title>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title). You can render `<title>` from any component and React will always place the corresponding DOM element in the document head. +Để chỉ định tiêu đề của tài liệu, hãy render [built-in browser `<title>` component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title). Bạn có thể render `<title>` từ bất kỳ component nào và React sẽ luôn đặt DOM element tương ứng vào document head. ```js <title>My Blog</title> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) #### Props {/*props*/} -`<title>` supports all [common element props.](/reference/react-dom/components/common#props) +`<title>` hỗ trợ tất cả [common element props.](/reference/react-dom/components/common#props) -* `children`: `<title>` accepts only text as a child. This text will become the title of the document. You can also pass your own components as long as they only render text. +* `children`: `<title>` chỉ chấp nhận text làm child. Text này sẽ trở thành tiêu đề của tài liệu. Bạn cũng có thể truyền các component của riêng bạn miễn là chúng chỉ render text. #### Special rendering behavior {/*special-rendering-behavior*/} -React will always place the DOM element corresponding to the `<title>` component within the document’s `<head>`, regardless of where in the React tree it is rendered. The `<head>` is the only valid place for `<title>` to exist within the DOM, yet it’s convenient and keeps things composable if a component representing a specific page can render its `<title>` itself. +React sẽ luôn đặt DOM element tương ứng với component `<title>` vào bên trong `<head>` của tài liệu, bất kể nó được render ở đâu trong React tree. `<head>` là vị trí hợp lệ duy nhất cho `<title>` tồn tại trong DOM, tuy nhiên, sẽ rất tiện lợi và giữ cho mọi thứ có thể kết hợp được nếu một component đại diện cho một trang cụ thể có thể tự render `<title>` của nó. -There are two exception to this: -* If `<title>` is within an `<svg>` component, then there is no special behavior, because in this context it doesn’t represent the document’s title but rather is an [accessibility annotation for that SVG graphic](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title). -* If the `<title>` has an [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, there is no special behavior, because in this case it doesn’t represent the document’s title but rather metadata about a specific part of the page. +Có hai ngoại lệ cho điều này: +* Nếu `<title>` nằm trong một component `<svg>`, thì sẽ không có hành vi đặc biệt nào, vì trong ngữ cảnh này, nó không đại diện cho tiêu đề của tài liệu mà là một [accessibility annotation cho SVG graphic đó](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title). +* Nếu `<title>` có một [`itemProp`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop) prop, thì sẽ không có hành vi đặc biệt nào, vì trong trường hợp này, nó không đại diện cho tiêu đề của tài liệu mà là metadata về một phần cụ thể của trang. <Pitfall> -Only render a single `<title>` at a time. If more than one component renders a `<title>` tag at the same time, React will place all of those titles in the document head. When this happens, the behavior of browsers and search engines is undefined. +Chỉ render một `<title>` tại một thời điểm. Nếu nhiều hơn một component render một thẻ `<title>` cùng một lúc, React sẽ đặt tất cả các tiêu đề đó vào document head. Khi điều này xảy ra, hành vi của trình duyệt và công cụ tìm kiếm là không xác định. </Pitfall> --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Set the document title {/*set-the-document-title*/} +### Đặt tiêu đề tài liệu {/*set-the-document-title*/} -Render the `<title>` component from any component with text as its children. React will put a `<title>` DOM node in the document `<head>`. +Render component `<title>` từ bất kỳ component nào với text làm children của nó. React sẽ đặt một `<title>` DOM node trong document `<head>`. <SandpackWithHTMLOutput> @@ -74,15 +74,15 @@ export default function ContactUsPage() { </SandpackWithHTMLOutput> -### Use variables in the title {/*use-variables-in-the-title*/} +### Sử dụng biến trong tiêu đề {/*use-variables-in-the-title*/} -The children of the `<title>` component must be a single string of text. (Or a single number or a single object with a `toString` method.) It might not be obvious, but using JSX curly braces like this: +Children của component `<title>` phải là một chuỗi text duy nhất. (Hoặc một số duy nhất hoặc một đối tượng duy nhất có phương thức `toString`.) Có vẻ không rõ ràng, nhưng sử dụng dấu ngoặc nhọn JSX như thế này: ```js -<title>Results page {pageNumber}</title> // 🔴 Problem: This is not a single string +<title>Results page {pageNumber}</title> // 🔴 Vấn đề: Đây không phải là một chuỗi duy nhất ``` -... actually causes the `<title>` component to get a two-element array as its children (the string `"Results page"` and the value of `pageNumber`). This will cause an error. Instead, use string interpolation to pass `<title>` a single string: +... thực sự khiến component `<title>` nhận được một mảng hai phần tử làm children của nó (chuỗi `"Results page"` và giá trị của `pageNumber`). Điều này sẽ gây ra lỗi. Thay vào đó, hãy sử dụng string interpolation để truyền cho `<title>` một chuỗi duy nhất: ```js <title>{`Results page ${pageNumber}`}</title> diff --git a/src/content/reference/react-dom/createPortal.md b/src/content/reference/react-dom/createPortal.md index c04510f80..576a082c8 100644 --- a/src/content/reference/react-dom/createPortal.md +++ b/src/content/reference/react-dom/createPortal.md @@ -4,8 +4,7 @@ title: createPortal <Intro> -`createPortal` lets you render some children into a different part of the DOM. - +`createPortal` cho phép bạn kết xuất một số children vào một phần khác của DOM. ```js <div> @@ -20,11 +19,11 @@ title: createPortal --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `createPortal(children, domNode, key?)` {/*createportal*/} -To create a portal, call `createPortal`, passing some JSX, and the DOM node where it should be rendered: +Để tạo một portal, hãy gọi `createPortal`, truyền một số JSX và nút DOM nơi nó sẽ được kết xuất: ```js import { createPortal } from 'react-dom'; @@ -32,53 +31,53 @@ import { createPortal } from 'react-dom'; // ... <div> - <p>This child is placed in the parent div.</p> + <p>Child này được đặt trong div cha.</p> {createPortal( - <p>This child is placed in the document body.</p>, + <p>Child này được đặt trong phần body của tài liệu.</p>, document.body )} </div> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -A portal only changes the physical placement of the DOM node. In every other way, the JSX you render into a portal acts as a child node of the React component that renders it. For example, the child can access the context provided by the parent tree, and events bubble up from children to parents according to the React tree. +Một portal chỉ thay đổi vị trí vật lý của nút DOM. Về mọi mặt khác, JSX bạn kết xuất vào một portal hoạt động như một nút con của thành phần React kết xuất nó. Ví dụ: child có thể truy cập ngữ cảnh được cung cấp bởi cây cha và các sự kiện nổi lên từ children lên cha theo cây React. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `children`: Anything that can be rendered with React, such as a piece of JSX (e.g. `<div />` or `<SomeComponent />`), a [Fragment](/reference/react/Fragment) (`<>...</>`), a string or a number, or an array of these. +* `children`: Bất cứ thứ gì có thể được kết xuất bằng React, chẳng hạn như một đoạn JSX (ví dụ: `<div />` hoặc `<SomeComponent />`), một [Fragment](/reference/react/Fragment) (`<>...</>`), một chuỗi hoặc một số, hoặc một mảng các phần tử này. -* `domNode`: Some DOM node, such as those returned by `document.getElementById()`. The node must already exist. Passing a different DOM node during an update will cause the portal content to be recreated. +* `domNode`: Một số nút DOM, chẳng hạn như những nút được trả về bởi `document.getElementById()`. Nút phải đã tồn tại. Truyền một nút DOM khác trong quá trình cập nhật sẽ khiến nội dung cổng được tạo lại. -* **optional** `key`: A unique string or number to be used as the portal's [key.](/learn/rendering-lists/#keeping-list-items-in-order-with-key) +* **tùy chọn** `key`: Một chuỗi hoặc số duy nhất được sử dụng làm [key của portal.](/learn/rendering-lists/#keeping-list-items-in-order-with-key) -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`createPortal` returns a React node that can be included into JSX or returned from a React component. If React encounters it in the render output, it will place the provided `children` inside the provided `domNode`. +`createPortal` trả về một nút React có thể được đưa vào JSX hoặc được trả về từ một thành phần React. Nếu React gặp nó trong đầu ra kết xuất, nó sẽ đặt `children` đã cung cấp bên trong `domNode` đã cung cấp. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Events from portals propagate according to the React tree rather than the DOM tree. For example, if you click inside a portal, and the portal is wrapped in `<div onClick>`, that `onClick` handler will fire. If this causes issues, either stop the event propagation from inside the portal, or move the portal itself up in the React tree. +* Các sự kiện từ cổng lan truyền theo cây React chứ không phải cây DOM. Ví dụ: nếu bạn nhấp vào bên trong một cổng và cổng được bao bọc trong `<div onClick>`, trình xử lý `onClick` đó sẽ kích hoạt. Nếu điều này gây ra sự cố, hãy dừng lan truyền sự kiện từ bên trong cổng hoặc di chuyển chính cổng đó lên trên cây React. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering to a different part of the DOM {/*rendering-to-a-different-part-of-the-dom*/} +### Kết xuất đến một phần khác của DOM {/*rendering-to-a-different-part-of-the-dom*/} -*Portals* let your components render some of their children into a different place in the DOM. This lets a part of your component "escape" from whatever containers it may be in. For example, a component can display a modal dialog or a tooltip that appears above and outside of the rest of the page. +*Portals* cho phép các thành phần của bạn kết xuất một số children của chúng vào một vị trí khác trong DOM. Điều này cho phép một phần của thành phần của bạn "thoát" khỏi bất kỳ vùng chứa nào mà nó có thể ở trong đó. Ví dụ: một thành phần có thể hiển thị một hộp thoại phương thức hoặc một chú giải công cụ xuất hiện phía trên và bên ngoài phần còn lại của trang. -To create a portal, render the result of `createPortal` with <CodeStep step={1}>some JSX</CodeStep> and the <CodeStep step={2}>DOM node where it should go</CodeStep>: +Để tạo một portal, hãy kết xuất kết quả của `createPortal` với <CodeStep step={1}>một số JSX</CodeStep> và <CodeStep step={2}>nút DOM nơi nó sẽ đi</CodeStep>: -```js [[1, 8, "<p>This child is placed in the document body.</p>"], [2, 9, "document.body"]] +```js [[1, 8, "<p>Child này được đặt trong phần body của tài liệu.</p>"], [2, 9, "document.body"]] import { createPortal } from 'react-dom'; function MyComponent() { return ( <div style={{ border: '2px solid black' }}> - <p>This child is placed in the parent div.</p> + <p>Child này được đặt trong div cha.</p> {createPortal( - <p>This child is placed in the document body.</p>, + <p>Child này được đặt trong phần body của tài liệu.</p>, document.body )} </div> @@ -86,9 +85,9 @@ function MyComponent() { } ``` -React will put the DOM nodes for <CodeStep step={1}>the JSX you passed</CodeStep> inside of the <CodeStep step={2}>DOM node you provided</CodeStep>. +React sẽ đặt các nút DOM cho <CodeStep step={1}>JSX bạn đã truyền</CodeStep> bên trong <CodeStep step={2}>nút DOM bạn đã cung cấp</CodeStep>. -Without a portal, the second `<p>` would be placed inside the parent `<div>`, but the portal "teleported" it into the [`document.body`:](https://developer.mozilla.org/en-US/docs/Web/API/Document/body) +Nếu không có portal, `<p>` thứ hai sẽ được đặt bên trong `<div>` cha, nhưng portal đã "dịch chuyển" nó vào [`document.body`:](https://developer.mozilla.org/en-US/docs/Web/API/Document/body) <Sandpack> @@ -98,9 +97,9 @@ import { createPortal } from 'react-dom'; export default function MyComponent() { return ( <div style={{ border: '2px solid black' }}> - <p>This child is placed in the parent div.</p> + <p>Child này được đặt trong div cha.</p> {createPortal( - <p>This child is placed in the document body.</p>, + <p>Child này được đặt trong phần body của tài liệu.</p>, document.body )} </div> @@ -110,30 +109,30 @@ export default function MyComponent() { </Sandpack> -Notice how the second paragraph visually appears outside the parent `<div>` with the border. If you inspect the DOM structure with developer tools, you'll see that the second `<p>` got placed directly into the `<body>`: +Lưu ý cách đoạn văn thứ hai xuất hiện trực quan bên ngoài `<div>` cha có đường viền. Nếu bạn kiểm tra cấu trúc DOM bằng các công cụ dành cho nhà phát triển, bạn sẽ thấy rằng `<p>` thứ hai đã được đặt trực tiếp vào `<body>`: ```html {4-6,9} <body> <div id="root"> ... <div style="border: 2px solid black"> - <p>This child is placed inside the parent div.</p> + <p>Child này được đặt bên trong div cha.</p> </div> ... </div> - <p>This child is placed in the document body.</p> + <p>Child này được đặt trong phần body của tài liệu.</p> </body> ``` -A portal only changes the physical placement of the DOM node. In every other way, the JSX you render into a portal acts as a child node of the React component that renders it. For example, the child can access the context provided by the parent tree, and events still bubble up from children to parents according to the React tree. +Một portal chỉ thay đổi vị trí vật lý của nút DOM. Về mọi mặt khác, JSX bạn kết xuất vào một portal hoạt động như một nút con của thành phần React kết xuất nó. Ví dụ: child có thể truy cập ngữ cảnh được cung cấp bởi cây cha và các sự kiện vẫn nổi lên từ children lên cha theo cây React. --- -### Rendering a modal dialog with a portal {/*rendering-a-modal-dialog-with-a-portal*/} +### Kết xuất hộp thoại phương thức bằng portal {/*rendering-a-modal-dialog-with-a-portal*/} -You can use a portal to create a modal dialog that floats above the rest of the page, even if the component that summons the dialog is inside a container with `overflow: hidden` or other styles that interfere with the dialog. +Bạn có thể sử dụng một portal để tạo một hộp thoại phương thức nổi phía trên phần còn lại của trang, ngay cả khi thành phần triệu hồi hộp thoại nằm bên trong một vùng chứa có `overflow: hidden` hoặc các kiểu khác gây trở ngại cho hộp thoại. -In this example, the two containers have styles that disrupt the modal dialog, but the one rendered into a portal is unaffected because, in the DOM, the modal is not contained within the parent JSX elements. +Trong ví dụ này, hai vùng chứa có các kiểu làm gián đoạn hộp thoại phương thức, nhưng vùng chứa được kết xuất vào một portal không bị ảnh hưởng vì, trong DOM, phương thức không nằm trong các phần tử JSX cha. <Sandpack> @@ -164,7 +163,7 @@ export default function NoPortalExample() { return ( <> <button onClick={() => setShowModal(true)}> - Show modal without a portal + Hiển thị phương thức mà không cần portal </button> {showModal && ( <ModalContent onClose={() => setShowModal(false)} /> @@ -184,7 +183,7 @@ export default function PortalExample() { return ( <> <button onClick={() => setShowModal(true)}> - Show modal using a portal + Hiển thị phương thức bằng portal </button> {showModal && createPortal( <ModalContent onClose={() => setShowModal(false)} />, @@ -199,8 +198,8 @@ export default function PortalExample() { export default function ModalContent({ onClose }) { return ( <div className="modal"> - <div>I'm a modal dialog</div> - <button onClick={onClose}>Close</button> + <div>Tôi là một hộp thoại phương thức</div> + <button onClick={onClose}>Đóng</button> </div> ); } @@ -238,17 +237,17 @@ export default function ModalContent({ onClose }) { <Pitfall> -It's important to make sure that your app is accessible when using portals. For instance, you may need to manage keyboard focus so that the user can move the focus in and out of the portal in a natural way. +Điều quan trọng là đảm bảo rằng ứng dụng của bạn có thể truy cập được khi sử dụng cổng. Ví dụ: bạn có thể cần quản lý tiêu điểm bàn phím để người dùng có thể di chuyển tiêu điểm vào và ra khỏi cổng một cách tự nhiên. -Follow the [WAI-ARIA Modal Authoring Practices](https://www.w3.org/WAI/ARIA/apg/#dialog_modal) when creating modals. If you use a community package, ensure that it is accessible and follows these guidelines. +Hãy tuân theo [Các phương pháp tạo phương thức WAI-ARIA](https://www.w3.org/WAI/ARIA/apg/#dialog_modal) khi tạo phương thức. Nếu bạn sử dụng một gói cộng đồng, hãy đảm bảo rằng nó có thể truy cập được và tuân theo các nguyên tắc này. </Pitfall> --- -### Rendering React components into non-React server markup {/*rendering-react-components-into-non-react-server-markup*/} +### Kết xuất các thành phần React vào mã đánh dấu máy chủ không phải React {/*rendering-react-components-into-non-react-server-markup*/} -Portals can be useful if your React root is only part of a static or server-rendered page that isn't built with React. For example, if your page is built with a server framework like Rails, you can create areas of interactivity within static areas such as sidebars. Compared with having [multiple separate React roots,](/reference/react-dom/client/createRoot#rendering-a-page-partially-built-with-react) portals let you treat the app as a single React tree with shared state even though its parts render to different parts of the DOM. +Portals có thể hữu ích nếu gốc React của bạn chỉ là một phần của trang tĩnh hoặc được kết xuất trên máy chủ không được xây dựng bằng React. Ví dụ: nếu trang của bạn được xây dựng bằng một framework máy chủ như Rails, bạn có thể tạo các khu vực tương tác trong các khu vực tĩnh như thanh bên. So với việc có [nhiều gốc React riêng biệt,](/reference/react-dom/client/createRoot#rendering-a-page-partially-built-with-react) portals cho phép bạn coi ứng dụng như một cây React duy nhất với trạng thái được chia sẻ ngay cả khi các phần của nó kết xuất đến các phần khác nhau của DOM. <Sandpack> @@ -342,15 +341,15 @@ p { --- -### Rendering React components into non-React DOM nodes {/*rendering-react-components-into-non-react-dom-nodes*/} +### Kết xuất các thành phần React vào các nút DOM không phải React {/*rendering-react-components-into-non-react-dom-nodes*/} -You can also use a portal to manage the content of a DOM node that's managed outside of React. For example, suppose you're integrating with a non-React map widget and you want to render React content inside a popup. To do this, declare a `popupContainer` state variable to store the DOM node you're going to render into: +Bạn cũng có thể sử dụng cổng để quản lý nội dung của một nút DOM được quản lý bên ngoài React. Ví dụ: giả sử bạn đang tích hợp với một tiện ích bản đồ không phải React và bạn muốn kết xuất nội dung React bên trong một cửa sổ bật lên. Để thực hiện việc này, hãy khai báo một biến trạng thái `popupContainer` để lưu trữ nút DOM mà bạn sẽ kết xuất vào: ```js const [popupContainer, setPopupContainer] = useState(null); ``` -When you create the third-party widget, store the DOM node returned by the widget so you can render into it: +Khi bạn tạo tiện ích của bên thứ ba, hãy lưu trữ nút DOM được trả về bởi tiện ích để bạn có thể kết xuất vào nó: ```js {5-6} useEffect(() => { @@ -363,7 +362,7 @@ useEffect(() => { }, []); ``` -This lets you use `createPortal` to render React content into `popupContainer` once it becomes available: +Điều này cho phép bạn sử dụng `createPortal` để kết xuất nội dung React vào `popupContainer` sau khi nó khả dụng: ```js {3-6} return ( @@ -376,7 +375,8 @@ return ( ); ``` -Here is a complete example you can play with: +Dưới đây là một ví dụ hoàn chỉnh mà bạn có thể thử: + <Sandpack> diff --git a/src/content/reference/react-dom/flushSync.md b/src/content/reference/react-dom/flushSync.md index e23ef4eac..6cc2a8426 100644 --- a/src/content/reference/react-dom/flushSync.md +++ b/src/content/reference/react-dom/flushSync.md @@ -4,13 +4,13 @@ title: flushSync <Pitfall> -Using `flushSync` is uncommon and can hurt the performance of your app. +Việc sử dụng `flushSync` là không phổ biến và có thể làm giảm hiệu suất ứng dụng của bạn. </Pitfall> <Intro> -`flushSync` lets you force React to flush any updates inside the provided callback synchronously. This ensures that the DOM is updated immediately. +`flushSync` cho phép bạn buộc React đồng bộ hóa mọi cập nhật bên trong callback được cung cấp. Điều này đảm bảo rằng DOM được cập nhật ngay lập tức. ```js flushSync(callback) @@ -22,11 +22,11 @@ flushSync(callback) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `flushSync(callback)` {/*flushsync*/} -Call `flushSync` to force React to flush any pending work and update the DOM synchronously. +Gọi `flushSync` để buộc React đồng bộ hóa mọi công việc đang chờ xử lý và cập nhật DOM một cách đồng bộ. ```js import { flushSync } from 'react-dom'; @@ -36,50 +36,49 @@ flushSync(() => { }); ``` -Most of the time, `flushSync` can be avoided. Use `flushSync` as last resort. +Hầu hết thời gian, có thể tránh sử dụng `flushSync`. Chỉ sử dụng `flushSync` như là giải pháp cuối cùng. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} +* `callback`: Một hàm. React sẽ gọi ngay lập tức callback này và đồng bộ hóa mọi cập nhật mà nó chứa. Nó cũng có thể đồng bộ hóa bất kỳ cập nhật đang chờ xử lý nào, hoặc Effects, hoặc các cập nhật bên trong Effects. Nếu một cập nhật tạm ngưng do kết quả của lệnh gọi `flushSync` này, thì các fallback có thể được hiển thị lại. -* `callback`: A function. React will immediately call this callback and flush any updates it contains synchronously. It may also flush any pending updates, or Effects, or updates inside of Effects. If an update suspends as a result of this `flushSync` call, the fallbacks may be re-shown. +#### Giá trị trả về {/*returns*/} -#### Returns {/*returns*/} +`flushSync` trả về `undefined`. -`flushSync` returns `undefined`. +#### Lưu ý {/*caveats*/} -#### Caveats {/*caveats*/} - -* `flushSync` can significantly hurt performance. Use sparingly. -* `flushSync` may force pending Suspense boundaries to show their `fallback` state. -* `flushSync` may run pending Effects and synchronously apply any updates they contain before returning. -* `flushSync` may flush updates outside the callback when necessary to flush the updates inside the callback. For example, if there are pending updates from a click, React may flush those before flushing the updates inside the callback. +* `flushSync` có thể làm giảm đáng kể hiệu suất. Sử dụng một cách tiết kiệm. +* `flushSync` có thể buộc các Suspense boundary đang chờ xử lý hiển thị trạng thái `fallback` của chúng. +* `flushSync` có thể chạy các Effects đang chờ xử lý và áp dụng đồng bộ bất kỳ cập nhật nào chúng chứa trước khi trả về. +* `flushSync` có thể đồng bộ hóa các cập nhật bên ngoài callback khi cần thiết để đồng bộ hóa các cập nhật bên trong callback. Ví dụ: nếu có các cập nhật đang chờ xử lý từ một cú nhấp chuột, React có thể đồng bộ hóa chúng trước khi đồng bộ hóa các cập nhật bên trong callback. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Flushing updates for third-party integrations {/*flushing-updates-for-third-party-integrations*/} +### Đồng bộ hóa các cập nhật cho tích hợp của bên thứ ba {/*flushing-updates-for-third-party-integrations*/} -When integrating with third-party code such as browser APIs or UI libraries, it may be necessary to force React to flush updates. Use `flushSync` to force React to flush any <CodeStep step={1}>state updates</CodeStep> inside the callback synchronously: +Khi tích hợp với mã của bên thứ ba, chẳng hạn như API trình duyệt hoặc thư viện giao diện người dùng, có thể cần thiết phải buộc React đồng bộ hóa các cập nhật. Sử dụng `flushSync` để buộc React đồng bộ hóa mọi <CodeStep step={1}>cập nhật trạng thái</CodeStep> bên trong callback một cách đồng bộ: ```js [[1, 2, "setSomething(123)"]] flushSync(() => { setSomething(123); }); -// By this line, the DOM is updated. +// Đến dòng này, DOM đã được cập nhật. ``` -This ensures that, by the time the next line of code runs, React has already updated the DOM. +Điều này đảm bảo rằng, vào thời điểm dòng mã tiếp theo chạy, React đã cập nhật DOM. -**Using `flushSync` is uncommon, and using it often can significantly hurt the performance of your app.** If your app only uses React APIs, and does not integrate with third-party libraries, `flushSync` should be unnecessary. +**Sử dụng `flushSync` là không phổ biến và sử dụng nó thường xuyên có thể làm giảm đáng kể hiệu suất ứng dụng của bạn.** Nếu ứng dụng của bạn chỉ sử dụng API React và không tích hợp với thư viện của bên thứ ba, thì không cần thiết phải sử dụng `flushSync`. -However, it can be helpful for integrating with third-party code like browser APIs. +Tuy nhiên, nó có thể hữu ích cho việc tích hợp với mã của bên thứ ba như API trình duyệt. -Some browser APIs expect results inside of callbacks to be written to the DOM synchronously, by the end of the callback, so the browser can do something with the rendered DOM. In most cases, React handles this for you automatically. But in some cases it may be necessary to force a synchronous update. +Một số API trình duyệt mong đợi kết quả bên trong callback được ghi vào DOM một cách đồng bộ, vào cuối callback, để trình duyệt có thể làm gì đó với DOM đã được hiển thị. Trong hầu hết các trường hợp, React tự động xử lý việc này cho bạn. Nhưng trong một số trường hợp, có thể cần thiết phải buộc cập nhật đồng bộ. -For example, the browser `onbeforeprint` API allows you to change the page immediately before the print dialog opens. This is useful for applying custom print styles that allow the document to display better for printing. In the example below, you use `flushSync` inside of the `onbeforeprint` callback to immediately "flush" the React state to the DOM. Then, by the time the print dialog opens, `isPrinting` displays "yes": +Ví dụ: API `onbeforeprint` của trình duyệt cho phép bạn thay đổi trang ngay trước khi hộp thoại in mở ra. Điều này hữu ích cho việc áp dụng các kiểu in tùy chỉnh cho phép tài liệu hiển thị tốt hơn để in. Trong ví dụ dưới đây, bạn sử dụng `flushSync` bên trong callback `onbeforeprint` để ngay lập tức "đồng bộ hóa" trạng thái React với DOM. Sau đó, vào thời điểm hộp thoại in mở ra, `isPrinting` hiển thị "yes": <Sandpack> @@ -122,12 +121,12 @@ export default function PrintApp() { </Sandpack> -Without `flushSync`, the print dialog will display `isPrinting` as "no". This is because React batches the updates asynchronously and the print dialog is displayed before the state is updated. +Nếu không có `flushSync`, hộp thoại in sẽ hiển thị `isPrinting` là "no". Điều này là do React xử lý các cập nhật không đồng bộ và hộp thoại in được hiển thị trước khi trạng thái được cập nhật. <Pitfall> -`flushSync` can significantly hurt performance, and may unexpectedly force pending Suspense boundaries to show their fallback state. +`flushSync` có thể làm giảm đáng kể hiệu suất và có thể bất ngờ buộc các Suspense boundary đang chờ xử lý hiển thị trạng thái fallback của chúng. -Most of the time, `flushSync` can be avoided, so use `flushSync` as a last resort. +Hầu hết thời gian, có thể tránh sử dụng `flushSync`, vì vậy hãy sử dụng `flushSync` như một giải pháp cuối cùng. </Pitfall> diff --git a/src/content/reference/react-dom/hooks/index.md b/src/content/reference/react-dom/hooks/index.md index 5dfb07d82..3bc193f49 100644 --- a/src/content/reference/react-dom/hooks/index.md +++ b/src/content/reference/react-dom/hooks/index.md @@ -1,20 +1,20 @@ --- -title: "Built-in React DOM Hooks" +title: "Các Hook React DOM Tích Hợp" --- <Intro> -The `react-dom` package contains Hooks that are only supported for web applications (which run in the browser DOM environment). These Hooks are not supported in non-browser environments like iOS, Android, or Windows applications. If you are looking for Hooks that are supported in web browsers *and other environments* see [the React Hooks page](/reference/react). This page lists all the Hooks in the `react-dom` package. +Gói `react-dom` chứa các Hook chỉ được hỗ trợ cho các ứng dụng web (chạy trong môi trường DOM của trình duyệt). Các Hook này không được hỗ trợ trong các môi trường không phải trình duyệt như ứng dụng iOS, Android hoặc Windows. Nếu bạn đang tìm kiếm các Hook được hỗ trợ trong trình duyệt web *và các môi trường khác*, hãy xem [trang React Hooks](/reference/react). Trang này liệt kê tất cả các Hook trong gói `react-dom`. </Intro> --- -## Form Hooks {/*form-hooks*/} +## Các Hook Biểu Mẫu {/*form-hooks*/} -*Forms* let you create interactive controls for submitting information. To manage forms in your components, use one of these Hooks: +*Biểu mẫu* cho phép bạn tạo các điều khiển tương tác để gửi thông tin. Để quản lý biểu mẫu trong các thành phần của bạn, hãy sử dụng một trong các Hook sau: -* [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) allows you to make updates to the UI based on the status of a form. +* [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) cho phép bạn thực hiện các cập nhật cho UI dựa trên trạng thái của biểu mẫu. ```js function Form({ action }) { diff --git a/src/content/reference/react-dom/hooks/useFormStatus.md b/src/content/reference/react-dom/hooks/useFormStatus.md index 0fc83e3e1..86a4fde7f 100644 --- a/src/content/reference/react-dom/hooks/useFormStatus.md +++ b/src/content/reference/react-dom/hooks/useFormStatus.md @@ -4,7 +4,7 @@ title: useFormStatus <Intro> -`useFormStatus` is a Hook that gives you status information of the last form submission. +`useFormStatus` là một Hook cung cấp cho bạn thông tin trạng thái của lần gửi biểu mẫu cuối cùng. ```js const { pending, data, method, action } = useFormStatus(); @@ -16,11 +16,11 @@ const { pending, data, method, action } = useFormStatus(); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useFormStatus()` {/*use-form-status*/} -The `useFormStatus` Hook provides status information of the last form submission. +Hook `useFormStatus` cung cấp thông tin trạng thái của lần gửi biểu mẫu cuối cùng. ```js {5},[[1, 6, "status.pending"]] import { useFormStatus } from "react-dom"; @@ -40,42 +40,42 @@ export default function App() { } ``` -To get status information, the `Submit` component must be rendered within a `<form>`. The Hook returns information like the <CodeStep step={1}>`pending`</CodeStep> property which tells you if the form is actively submitting. +Để lấy thông tin trạng thái, component `Submit` phải được render bên trong một `<form>`. Hook trả về thông tin như thuộc tính <CodeStep step={1}>`pending`</CodeStep> cho bạn biết nếu biểu mẫu đang được gửi. -In the above example, `Submit` uses this information to disable `<button>` presses while the form is submitting. +Trong ví dụ trên, `Submit` sử dụng thông tin này để vô hiệu hóa các lần nhấn `<button>` trong khi biểu mẫu đang được gửi. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -`useFormStatus` does not take any parameters. +`useFormStatus` không nhận bất kỳ tham số nào. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -A `status` object with the following properties: +Một đối tượng `status` với các thuộc tính sau: -* `pending`: A boolean. If `true`, this means the parent `<form>` is pending submission. Otherwise, `false`. +* `pending`: Một giá trị boolean. Nếu `true`, điều này có nghĩa là `<form>` cha đang chờ gửi. Ngược lại, `false`. -* `data`: An object implementing the [`FormData interface`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) that contains the data the parent `<form>` is submitting. If there is no active submission or no parent `<form>`, it will be `null`. +* `data`: Một đối tượng triển khai [`FormData interface`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) chứa dữ liệu mà `<form>` cha đang gửi. Nếu không có gửi hoạt động hoặc không có `<form>` cha, nó sẽ là `null`. -* `method`: A string value of either `'get'` or `'post'`. This represents whether the parent `<form>` is submitting with either a `GET` or `POST` [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). By default, a `<form>` will use the `GET` method and can be specified by the [`method`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method) property. +* `method`: Một giá trị chuỗi là `'get'` hoặc `'post'`. Điều này thể hiện việc `<form>` cha đang gửi bằng phương thức [HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) `GET` hoặc `POST`. Theo mặc định, một `<form>` sẽ sử dụng phương thức `GET` và có thể được chỉ định bởi thuộc tính [`method`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method). -[//]: # (Link to `<form>` documentation. "Read more on the `action` prop on `<form>`.") -* `action`: A reference to the function passed to the `action` prop on the parent `<form>`. If there is no parent `<form>`, the property is `null`. If there is a URI value provided to the `action` prop, or no `action` prop specified, `status.action` will be `null`. +[//]: # (Liên kết đến tài liệu `<form>`. "Đọc thêm về prop `action` trên `<form>`.") +* `action`: Một tham chiếu đến hàm được truyền cho prop `action` trên `<form>` cha. Nếu không có `<form>` cha, thuộc tính là `null`. Nếu có một giá trị URI được cung cấp cho prop `action` hoặc không có prop `action` nào được chỉ định, `status.action` sẽ là `null`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* The `useFormStatus` Hook must be called from a component that is rendered inside a `<form>`. -* `useFormStatus` will only return status information for a parent `<form>`. It will not return status information for any `<form>` rendered in that same component or children components. +* Hook `useFormStatus` phải được gọi từ một component được render bên trong một `<form>`. +* `useFormStatus` sẽ chỉ trả về thông tin trạng thái cho một `<form>` cha. Nó sẽ không trả về thông tin trạng thái cho bất kỳ `<form>` nào được render trong cùng một component hoặc các component con. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Display a pending state during form submission {/*display-a-pending-state-during-form-submission*/} -To display a pending state while a form is submitting, you can call the `useFormStatus` Hook in a component rendered in a `<form>` and read the `pending` property returned. +### Hiển thị trạng thái chờ trong khi gửi biểu mẫu {/*display-a-pending-state-during-form-submission*/} +Để hiển thị trạng thái chờ trong khi biểu mẫu đang được gửi, bạn có thể gọi Hook `useFormStatus` trong một component được render trong một `<form>` và đọc thuộc tính `pending` được trả về. -Here, we use the `pending` property to indicate the form is submitting. +Ở đây, chúng ta sử dụng thuộc tính `pending` để chỉ ra rằng biểu mẫu đang được gửi. <Sandpack> @@ -87,7 +87,7 @@ function Submit() { const { pending } = useFormStatus(); return ( <button type="submit" disabled={pending}> - {pending ? "Submitting..." : "Submit"} + {pending ? "Đang gửi..." : "Gửi"} </button> ); } @@ -110,34 +110,34 @@ export async function submitForm(query) { await new Promise((res) => setTimeout(res, 1000)); } ``` -</Sandpack> +</Sandpack> <Pitfall> -##### `useFormStatus` will not return status information for a `<form>` rendered in the same component. {/*useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component*/} +##### `useFormStatus` sẽ không trả về thông tin trạng thái cho một `<form>` được render trong cùng một component. {/*useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component*/} -The `useFormStatus` Hook only returns status information for a parent `<form>` and not for any `<form>` rendered in the same component calling the Hook, or child components. +Hook `useFormStatus` chỉ trả về thông tin trạng thái cho một `<form>` cha và không cho bất kỳ `<form>` nào được render trong cùng một component gọi Hook hoặc các component con. ```js function Form() { - // 🚩 `pending` will never be true - // useFormStatus does not track the form rendered in this component + // 🚩 `pending` sẽ không bao giờ là true + // useFormStatus không theo dõi biểu mẫu được render trong component này const { pending } = useFormStatus(); return <form action={submit}></form>; } ``` -Instead call `useFormStatus` from inside a component that is located inside `<form>`. +Thay vào đó, hãy gọi `useFormStatus` từ bên trong một component nằm bên trong `<form>`. ```js function Submit() { - // ✅ `pending` will be derived from the form that wraps the Submit component - const { pending } = useFormStatus(); + // ✅ `pending` sẽ được lấy từ biểu mẫu bao bọc component Submit + const { pending } = useFormStatus(); return <button disabled={pending}>...</button>; } function Form() { - // This is the <form> `useFormStatus` tracks + // Đây là `<form>` mà `useFormStatus` theo dõi return ( <form action={submit}> <Submit /> @@ -148,11 +148,11 @@ function Form() { </Pitfall> -### Read the form data being submitted {/*read-form-data-being-submitted*/} +### Đọc dữ liệu biểu mẫu đang được gửi {/*read-form-data-being-submitted*/} -You can use the `data` property of the status information returned from `useFormStatus` to display what data is being submitted by the user. +Bạn có thể sử dụng thuộc tính `data` của thông tin trạng thái được trả về từ `useFormStatus` để hiển thị dữ liệu đang được người dùng gửi. -Here, we have a form where users can request a username. We can use `useFormStatus` to display a temporary status message confirming what username they have requested. +Ở đây, chúng ta có một biểu mẫu nơi người dùng có thể yêu cầu tên người dùng. Chúng ta có thể sử dụng `useFormStatus` để hiển thị một thông báo trạng thái tạm thời xác nhận tên người dùng mà họ đã yêu cầu. <Sandpack> @@ -165,13 +165,13 @@ export default function UsernameForm() { return ( <div> - <h3>Request a Username: </h3> + <h3>Yêu cầu tên người dùng: </h3> <input type="text" name="username" disabled={pending}/> <button type="submit" disabled={pending}> - Submit + Gửi </button> <br /> - <p>{data ? `Requesting ${data?.get("username")}...`: ''}</p> + <p>{data ? `Đang yêu cầu ${data?.get("username")}...`: ''}</p> </div> ); } @@ -215,16 +215,16 @@ button { ``` -</Sandpack> +</Sandpack> --- -## Troubleshooting {/*troubleshooting*/} +## Gỡ rối {/*troubleshooting*/} -### `status.pending` is never `true` {/*pending-is-never-true*/} +### `status.pending` không bao giờ là `true` {/*pending-is-never-true*/} -`useFormStatus` will only return status information for a parent `<form>`. +`useFormStatus` sẽ chỉ trả về thông tin trạng thái cho một `<form>` cha. -If the component that calls `useFormStatus` is not nested in a `<form>`, `status.pending` will always return `false`. Verify `useFormStatus` is called in a component that is a child of a `<form>` element. +Nếu component gọi `useFormStatus` không được lồng trong một `<form>`, `status.pending` sẽ luôn trả về `false`. Xác minh `useFormStatus` được gọi trong một component là con của một phần tử `<form>`. -`useFormStatus` will not track the status of a `<form>` rendered in the same component. See [Pitfall](#useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component) for more details. +`useFormStatus` sẽ không theo dõi trạng thái của một `<form>` được render trong cùng một component. Xem [Pitfall](#useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component) để biết thêm chi tiết. diff --git a/src/content/reference/react-dom/index.md b/src/content/reference/react-dom/index.md index b79b16db6..7b1d0046a 100644 --- a/src/content/reference/react-dom/index.md +++ b/src/content/reference/react-dom/index.md @@ -1,53 +1,53 @@ --- -title: React DOM APIs +title: Các API của React DOM --- <Intro> -The `react-dom` package contains methods that are only supported for the web applications (which run in the browser DOM environment). They are not supported for React Native. +Gói `react-dom` chứa các phương thức chỉ được hỗ trợ cho các ứng dụng web (chạy trong môi trường DOM của trình duyệt). Chúng không được hỗ trợ cho React Native. </Intro> --- -## APIs {/*apis*/} +## Các API {/*apis*/} -These APIs can be imported from your components. They are rarely used: +Các API này có thể được nhập từ các component của bạn. Chúng hiếm khi được sử dụng: -* [`createPortal`](/reference/react-dom/createPortal) lets you render child components in a different part of the DOM tree. -* [`flushSync`](/reference/react-dom/flushSync) lets you force React to flush a state update and update the DOM synchronously. +* [`createPortal`](/reference/react-dom/createPortal) cho phép bạn render các component con ở một phần khác của cây DOM. +* [`flushSync`](/reference/react-dom/flushSync) cho phép bạn buộc React cập nhật trạng thái và cập nhật DOM một cách đồng bộ. -## Resource Preloading APIs {/*resource-preloading-apis*/} +## Các API tải trước tài nguyên {/*resource-preloading-apis*/} -These APIs can be used to make apps faster by pre-loading resources such as scripts, stylesheets, and fonts as soon as you know you need them, for example before navigating to another page where the resources will be used. +Các API này có thể được sử dụng để làm cho ứng dụng nhanh hơn bằng cách tải trước các tài nguyên như script, stylesheet và font ngay khi bạn biết bạn cần chúng, ví dụ: trước khi điều hướng đến một trang khác nơi các tài nguyên sẽ được sử dụng. -[React-based frameworks](/learn/start-a-new-react-project) frequently handle resource loading for you, so you might not have to call these APIs yourself. Consult your framework's documentation for details. +[Các framework dựa trên React](/learn/start-a-new-react-project) thường xử lý việc tải tài nguyên cho bạn, vì vậy bạn có thể không phải tự gọi các API này. Tham khảo tài liệu của framework để biết thêm chi tiết. -* [`prefetchDNS`](/reference/react-dom/prefetchDNS) lets you prefetch the IP address of a DNS domain name that you expect to connect to. -* [`preconnect`](/reference/react-dom/preconnect) lets you connect to a server you expect to request resources from, even if you don't know what resources you'll need yet. -* [`preload`](/reference/react-dom/preload) lets you fetch a stylesheet, font, image, or external script that you expect to use. -* [`preloadModule`](/reference/react-dom/preloadModule) lets you fetch an ESM module that you expect to use. -* [`preinit`](/reference/react-dom/preinit) lets you fetch and evaluate an external script or fetch and insert a stylesheet. -* [`preinitModule`](/reference/react-dom/preinitModule) lets you fetch and evaluate an ESM module. +* [`prefetchDNS`](/reference/react-dom/prefetchDNS) cho phép bạn tìm nạp trước địa chỉ IP của một tên miền DNS mà bạn dự kiến sẽ kết nối. +* [`preconnect`](/reference/react-dom/preconnect) cho phép bạn kết nối với một máy chủ mà bạn dự kiến sẽ yêu cầu tài nguyên từ đó, ngay cả khi bạn chưa biết tài nguyên nào bạn sẽ cần. +* [`preload`](/reference/react-dom/preload) cho phép bạn tìm nạp một stylesheet, font, hình ảnh hoặc script bên ngoài mà bạn dự kiến sẽ sử dụng. +* [`preloadModule`](/reference/react-dom/preloadModule) cho phép bạn tìm nạp một module ESM mà bạn dự kiến sẽ sử dụng. +* [`preinit`](/reference/react-dom/preinit) cho phép bạn tìm nạp và đánh giá một script bên ngoài hoặc tìm nạp và chèn một stylesheet. +* [`preinitModule`](/reference/react-dom/preinitModule) cho phép bạn tìm nạp và đánh giá một module ESM. --- -## Entry points {/*entry-points*/} +## Các điểm vào {/*entry-points*/} -The `react-dom` package provides two additional entry points: +Gói `react-dom` cung cấp hai điểm vào bổ sung: -* [`react-dom/client`](/reference/react-dom/client) contains APIs to render React components on the client (in the browser). -* [`react-dom/server`](/reference/react-dom/server) contains APIs to render React components on the server. +* [`react-dom/client`](/reference/react-dom/client) chứa các API để render các component React trên client (trong trình duyệt). +* [`react-dom/server`](/reference/react-dom/server) chứa các API để render các component React trên server. --- -## Removed APIs {/*removed-apis*/} +## Các API đã bị loại bỏ {/*removed-apis*/} -These APIs were removed in React 19: +Các API này đã bị loại bỏ trong React 19: -* [`findDOMNode`](https://18.react.dev/reference/react-dom/findDOMNode): see [alternatives](https://18.react.dev/reference/react-dom/findDOMNode#alternatives). -* [`hydrate`](https://18.react.dev/reference/react-dom/hydrate): use [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) instead. -* [`render`](https://18.react.dev/reference/react-dom/render): use [`createRoot`](/reference/react-dom/client/createRoot) instead. -* [`unmountComponentAtNode`](/reference/react-dom/unmountComponentAtNode): use [`root.unmount()`](/reference/react-dom/client/createRoot#root-unmount) instead. -* [`renderToNodeStream`](https://18.react.dev/reference/react-dom/server/renderToNodeStream): use [`react-dom/server`](/reference/react-dom/server) APIs instead. -* [`renderToStaticNodeStream`](https://18.react.dev/reference/react-dom/server/renderToStaticNodeStream): use [`react-dom/server`](/reference/react-dom/server) APIs instead. +* [`findDOMNode`](https://18.react.dev/reference/react-dom/findDOMNode): xem [các lựa chọn thay thế](https://18.react.dev/reference/react-dom/findDOMNode#alternatives). +* [`hydrate`](https://18.react.dev/reference/react-dom/hydrate): sử dụng [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) thay thế. +* [`render`](https://18.react.dev/reference/react-dom/render): sử dụng [`createRoot`](/reference/react-dom/client/createRoot) thay thế. +* [`unmountComponentAtNode`](/reference/react-dom/unmountComponentAtNode): sử dụng [`root.unmount()`](/reference/react-dom/client/createRoot#root-unmount) thay thế. +* [`renderToNodeStream`](https://18.react.dev/reference/react-dom/server/renderToNodeStream): sử dụng [`react-dom/server`](/reference/react-dom/server) APIs thay thế. +* [`renderToStaticNodeStream`](https://18.react.dev/reference/react-dom/server/renderToStaticNodeStream): sử dụng [`react-dom/server`](/reference/react-dom/server) APIs thay thế. diff --git a/src/content/reference/react-dom/preconnect.md b/src/content/reference/react-dom/preconnect.md index 047b1fcac..773f2d144 100644 --- a/src/content/reference/react-dom/preconnect.md +++ b/src/content/reference/react-dom/preconnect.md @@ -4,7 +4,7 @@ title: preconnect <Intro> -`preconnect` lets you eagerly connect to a server that you expect to load resources from. +`preconnect` cho phép bạn chủ động kết nối đến một máy chủ mà bạn dự kiến sẽ tải tài nguyên từ đó. ```js preconnect("https://example.com"); @@ -16,11 +16,11 @@ preconnect("https://example.com"); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `preconnect(href)` {/*preconnect*/} -To preconnect to a host, call the `preconnect` function from `react-dom`. +Để preconnect đến một host, gọi hàm `preconnect` từ `react-dom`. ```js import { preconnect } from 'react-dom'; @@ -32,34 +32,33 @@ function AppRoot() { ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -The `preconnect` function provides the browser with a hint that it should open a connection to the given server. If the browser chooses to do so, this can speed up the loading of resources from that server. +Hàm `preconnect` cung cấp cho trình duyệt một gợi ý rằng nó nên mở một kết nối đến máy chủ đã cho. Nếu trình duyệt chọn làm như vậy, điều này có thể tăng tốc độ tải tài nguyên từ máy chủ đó. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `href`: a string. The URL of the server you want to connect to. +* `href`: một chuỗi. URL của máy chủ bạn muốn kết nối đến. +#### Giá trị trả về {/*returns*/} -#### Returns {/*returns*/} +`preconnect` không trả về gì cả. -`preconnect` returns nothing. +#### Lưu ý {/*caveats*/} -#### Caveats {/*caveats*/} - -* Multiple calls to `preconnect` with the same server have the same effect as a single call. -* In the browser, you can call `preconnect` in any situation: while rendering a component, in an Effect, in an event handler, and so on. -* In server-side rendering or when rendering Server Components, `preconnect` only has an effect if you call it while rendering a component or in an async context originating from rendering a component. Any other calls will be ignored. -* If you know the specific resources you'll need, you can call [other functions](/reference/react-dom/#resource-preloading-apis) instead that will start loading the resources right away. -* There is no benefit to preconnecting to the same server the webpage itself is hosted from because it's already been connected to by the time the hint would be given. +* Nhiều lệnh gọi đến `preconnect` với cùng một máy chủ có cùng hiệu ứng như một lệnh gọi duy nhất. +* Trong trình duyệt, bạn có thể gọi `preconnect` trong bất kỳ tình huống nào: trong khi render một component, trong một Effect, trong một trình xử lý sự kiện, v.v. +* Trong quá trình render phía máy chủ hoặc khi render Server Components, `preconnect` chỉ có hiệu lực nếu bạn gọi nó trong khi render một component hoặc trong một ngữ cảnh không đồng bộ bắt nguồn từ việc render một component. Bất kỳ lệnh gọi nào khác sẽ bị bỏ qua. +* Nếu bạn biết các tài nguyên cụ thể bạn sẽ cần, bạn có thể gọi [các hàm khác](/reference/react-dom/#resource-preloading-apis) thay vào đó sẽ bắt đầu tải tài nguyên ngay lập tức. +* Không có lợi ích gì khi preconnect đến cùng một máy chủ mà trang web đang được lưu trữ vì nó đã được kết nối vào thời điểm gợi ý được đưa ra. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Preconnecting when rendering {/*preconnecting-when-rendering*/} +### Preconnect khi render {/*preconnecting-when-rendering*/} -Call `preconnect` when rendering a component if you know that its children will load external resources from that host. +Gọi `preconnect` khi render một component nếu bạn biết rằng các component con của nó sẽ tải tài nguyên bên ngoài từ host đó. ```js import { preconnect } from 'react-dom'; @@ -70,9 +69,9 @@ function AppRoot() { } ``` -### Preconnecting in an event handler {/*preconnecting-in-an-event-handler*/} +### Preconnect trong một trình xử lý sự kiện {/*preconnecting-in-an-event-handler*/} -Call `preconnect` in an event handler before transitioning to a page or state where external resources will be needed. This gets the process started earlier than if you call it during the rendering of the new page or state. +Gọi `preconnect` trong một trình xử lý sự kiện trước khi chuyển sang một trang hoặc trạng thái nơi các tài nguyên bên ngoài sẽ cần thiết. Điều này giúp quá trình bắt đầu sớm hơn so với khi bạn gọi nó trong quá trình render trang hoặc trạng thái mới. ```js import { preconnect } from 'react-dom'; diff --git a/src/content/reference/react-dom/prefetchDNS.md b/src/content/reference/react-dom/prefetchDNS.md index ef11aa3e5..da94e292d 100644 --- a/src/content/reference/react-dom/prefetchDNS.md +++ b/src/content/reference/react-dom/prefetchDNS.md @@ -4,7 +4,7 @@ title: prefetchDNS <Intro> -`prefetchDNS` lets you eagerly look up the IP of a server that you expect to load resources from. +`prefetchDNS` cho phép bạn tìm kiếm trước địa chỉ IP của một máy chủ mà bạn dự kiến sẽ tải tài nguyên từ đó. ```js prefetchDNS("https://example.com"); @@ -16,11 +16,11 @@ prefetchDNS("https://example.com"); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `prefetchDNS(href)` {/*prefetchdns*/} -To look up a host, call the `prefetchDNS` function from `react-dom`. +Để tìm kiếm một máy chủ, hãy gọi hàm `prefetchDNS` từ `react-dom`. ```js import { prefetchDNS } from 'react-dom'; @@ -32,34 +32,34 @@ function AppRoot() { ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -The prefetchDNS function provides the browser with a hint that it should look up the IP address of a given server. If the browser chooses to do so, this can speed up the loading of resources from that server. +Hàm prefetchDNS cung cấp cho trình duyệt một gợi ý rằng nó nên tìm kiếm địa chỉ IP của một máy chủ nhất định. Nếu trình duyệt chọn làm như vậy, điều này có thể tăng tốc độ tải tài nguyên từ máy chủ đó. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `href`: a string. The URL of the server you want to connect to. +* `href`: một chuỗi. URL của máy chủ bạn muốn kết nối. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`prefetchDNS` returns nothing. +`prefetchDNS` không trả về gì cả. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Multiple calls to `prefetchDNS` with the same server have the same effect as a single call. -* In the browser, you can call `prefetchDNS` in any situation: while rendering a component, in an Effect, in an event handler, and so on. -* In server-side rendering or when rendering Server Components, `prefetchDNS` only has an effect if you call it while rendering a component or in an async context originating from rendering a component. Any other calls will be ignored. -* If you know the specific resources you'll need, you can call [other functions](/reference/react-dom/#resource-preloading-apis) instead that will start loading the resources right away. -* There is no benefit to prefetching the same server the webpage itself is hosted from because it's already been looked up by the time the hint would be given. -* Compared with [`preconnect`](/reference/react-dom/preconnect), `prefetchDNS` may be better if you are speculatively connecting to a large number of domains, in which case the overhead of preconnections might outweigh the benefit. +* Nhiều lệnh gọi đến `prefetchDNS` với cùng một máy chủ có tác dụng tương tự như một lệnh gọi duy nhất. +* Trong trình duyệt, bạn có thể gọi `prefetchDNS` trong mọi tình huống: trong khi hiển thị một component, trong một Effect, trong một trình xử lý sự kiện, v.v. +* Trong quá trình render phía máy chủ hoặc khi render Server Components, `prefetchDNS` chỉ có hiệu lực nếu bạn gọi nó trong khi render một component hoặc trong một ngữ cảnh không đồng bộ bắt nguồn từ việc render một component. Bất kỳ lệnh gọi nào khác sẽ bị bỏ qua. +* Nếu bạn biết các tài nguyên cụ thể bạn sẽ cần, bạn có thể gọi [các hàm khác](/reference/react-dom/#resource-preloading-apis) thay vào đó để bắt đầu tải tài nguyên ngay lập tức. +* Không có lợi ích gì khi tìm nạp trước cùng một máy chủ mà trang web đang được lưu trữ vì nó đã được tìm kiếm vào thời điểm gợi ý được đưa ra. +* So với [`preconnect`](/reference/react-dom/preconnect), `prefetchDNS` có thể tốt hơn nếu bạn đang kết nối một cách suy đoán với một số lượng lớn các tên miền, trong trường hợp đó, chi phí của việc thiết lập trước các kết nối có thể lớn hơn lợi ích. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Prefetching DNS when rendering {/*prefetching-dns-when-rendering*/} +### Tìm nạp trước DNS khi render {/*prefetching-dns-when-rendering*/} -Call `prefetchDNS` when rendering a component if you know that its children will load external resources from that host. +Gọi `prefetchDNS` khi render một component nếu bạn biết rằng các component con của nó sẽ tải tài nguyên bên ngoài từ máy chủ đó. ```js import { prefetchDNS } from 'react-dom'; @@ -70,9 +70,9 @@ function AppRoot() { } ``` -### Prefetching DNS in an event handler {/*prefetching-dns-in-an-event-handler*/} +### Tìm nạp trước DNS trong một trình xử lý sự kiện {/*prefetching-dns-in-an-event-handler*/} -Call `prefetchDNS` in an event handler before transitioning to a page or state where external resources will be needed. This gets the process started earlier than if you call it during the rendering of the new page or state. +Gọi `prefetchDNS` trong một trình xử lý sự kiện trước khi chuyển sang một trang hoặc trạng thái nơi các tài nguyên bên ngoài sẽ cần thiết. Điều này giúp quá trình bắt đầu sớm hơn so với việc bạn gọi nó trong quá trình render trang hoặc trạng thái mới. ```js import { prefetchDNS } from 'react-dom'; diff --git a/src/content/reference/react-dom/preinit.md b/src/content/reference/react-dom/preinit.md index 0ecd1972d..0a3036b17 100644 --- a/src/content/reference/react-dom/preinit.md +++ b/src/content/reference/react-dom/preinit.md @@ -4,13 +4,13 @@ title: preinit <Note> -[React-based frameworks](/learn/start-a-new-react-project) frequently handle resource loading for you, so you might not have to call this API yourself. Consult your framework's documentation for details. +[Các framework dựa trên React](/learn/start-a-new-react-project) thường tự động xử lý việc tải tài nguyên cho bạn, vì vậy bạn có thể không cần phải tự gọi API này. Tham khảo tài liệu của framework để biết thêm chi tiết. </Note> <Intro> -`preinit` lets you eagerly fetch and evaluate a stylesheet or external script. +`preinit` cho phép bạn tìm nạp và thực thi một cách chủ động một stylesheet hoặc script bên ngoài. ```js preinit("https://example.com/script.js", {as: "script"}); @@ -22,11 +22,11 @@ preinit("https://example.com/script.js", {as: "script"}); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `preinit(href, options)` {/*preinit*/} -To preinit a script or stylesheet, call the `preinit` function from `react-dom`. +Để preinit một script hoặc stylesheet, hãy gọi hàm `preinit` từ `react-dom`. ```js import { preinit } from 'react-dom'; @@ -38,42 +38,42 @@ function AppRoot() { ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -The `preinit` function provides the browser with a hint that it should start downloading and executing the given resource, which can save time. Scripts that you `preinit` are executed when they finish downloading. Stylesheets that you preinit are inserted into the document, which causes them to go into effect right away. +Hàm `preinit` cung cấp cho trình duyệt một gợi ý rằng nó nên bắt đầu tải xuống và thực thi tài nguyên đã cho, điều này có thể tiết kiệm thời gian. Các script mà bạn `preinit` sẽ được thực thi khi chúng tải xuống xong. Các stylesheet mà bạn preinit sẽ được chèn vào tài liệu, điều này khiến chúng có hiệu lực ngay lập tức. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `href`: a string. The URL of the resource you want to download and execute. -* `options`: an object. It contains the following properties: - * `as`: a required string. The type of resource. Its possible values are `script` and `style`. - * `precedence`: a string. Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. The possible values are `reset`, `low`, `medium`, `high`. - * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. It is required when `as` is set to `"fetch"`. - * `integrity`: a string. A cryptographic hash of the resource, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. - * `fetchPriority`: a string. Suggests a relative priority for fetching the resource. The possible values are `auto` (the default), `high`, and `low`. +* `href`: một chuỗi. URL của tài nguyên bạn muốn tải xuống và thực thi. +* `options`: một đối tượng. Nó chứa các thuộc tính sau: + * `as`: một chuỗi bắt buộc. Loại tài nguyên. Các giá trị có thể là `script` và `style`. + * `precedence`: một chuỗi. Bắt buộc với stylesheet. Cho biết vị trí chèn stylesheet so với các stylesheet khác. Các stylesheet có độ ưu tiên cao hơn có thể ghi đè các stylesheet có độ ưu tiên thấp hơn. Các giá trị có thể là `reset`, `low`, `medium`, `high`. + * `crossOrigin`: một chuỗi. [Chính sách CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) để sử dụng. Các giá trị có thể là `anonymous` và `use-credentials`. Nó là bắt buộc khi `as` được đặt thành `"fetch"`. + * `integrity`: một chuỗi. Một hàm băm mật mã của tài nguyên, để [xác minh tính xác thực của nó](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). + * `nonce`: một chuỗi. Một [nonce mật mã để cho phép tài nguyên](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) khi sử dụng Chính sách bảo mật nội dung nghiêm ngặt. + * `fetchPriority`: một chuỗi. Đề xuất mức độ ưu tiên tương đối để tìm nạp tài nguyên. Các giá trị có thể là `auto` (mặc định), `high` và `low`. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`preinit` returns nothing. +`preinit` không trả về gì cả. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Multiple calls to `preinit` with the same `href` have the same effect as a single call. -* In the browser, you can call `preinit` in any situation: while rendering a component, in an Effect, in an event handler, and so on. -* In server-side rendering or when rendering Server Components, `preinit` only has an effect if you call it while rendering a component or in an async context originating from rendering a component. Any other calls will be ignored. +* Nhiều lệnh gọi đến `preinit` với cùng một `href` có cùng hiệu ứng như một lệnh gọi duy nhất. +* Trong trình duyệt, bạn có thể gọi `preinit` trong mọi tình huống: trong khi hiển thị một component, trong một Effect, trong một trình xử lý sự kiện, v.v. +* Trong quá trình hiển thị phía máy chủ hoặc khi hiển thị Server Components, `preinit` chỉ có hiệu lực nếu bạn gọi nó trong khi hiển thị một component hoặc trong một ngữ cảnh không đồng bộ bắt nguồn từ việc hiển thị một component. Bất kỳ lệnh gọi nào khác sẽ bị bỏ qua. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Preiniting when rendering {/*preiniting-when-rendering*/} +### Preinit khi hiển thị {/*preiniting-when-rendering*/} -Call `preinit` when rendering a component if you know that it or its children will use a specific resource, and you're OK with the resource being evaluated and thereby taking effect immediately upon being downloaded. +Gọi `preinit` khi hiển thị một component nếu bạn biết rằng nó hoặc các component con của nó sẽ sử dụng một tài nguyên cụ thể và bạn đồng ý với việc tài nguyên được đánh giá và do đó có hiệu lực ngay lập tức sau khi được tải xuống. -<Recipes titleText="Examples of preiniting"> +<Recipes titleText="Ví dụ về preinit"> -#### Preiniting an external script {/*preiniting-an-external-script*/} +#### Preinit một script bên ngoài {/*preiniting-an-external-script*/} ```js import { preinit } from 'react-dom'; @@ -84,11 +84,11 @@ function AppRoot() { } ``` -If you want the browser to download the script but not to execute it right away, use [`preload`](/reference/react-dom/preload) instead. If you want to load an ESM module, use [`preinitModule`](/reference/react-dom/preinitModule). +Nếu bạn muốn trình duyệt tải xuống script nhưng không thực thi nó ngay lập tức, hãy sử dụng [`preload`](/reference/react-dom/preload) thay thế. Nếu bạn muốn tải một mô-đun ESM, hãy sử dụng [`preinitModule`](/reference/react-dom/preinitModule). <Solution /> -#### Preiniting a stylesheet {/*preiniting-a-stylesheet*/} +#### Preinit một stylesheet {/*preiniting-a-stylesheet*/} ```js import { preinit } from 'react-dom'; @@ -99,17 +99,17 @@ function AppRoot() { } ``` -The `precedence` option, which is required, lets you control the order of stylesheets within the document. Stylesheets with higher precedence can overrule those with lower precedence. +Tùy chọn `precedence`, là bắt buộc, cho phép bạn kiểm soát thứ tự của các stylesheet trong tài liệu. Các stylesheet có độ ưu tiên cao hơn có thể ghi đè các stylesheet có độ ưu tiên thấp hơn. -If you want to download the stylesheet but not to insert it into the document right away, use [`preload`](/reference/react-dom/preload) instead. +Nếu bạn muốn tải xuống stylesheet nhưng không chèn nó vào tài liệu ngay lập tức, hãy sử dụng [`preload`](/reference/react-dom/preload) thay thế. <Solution /> </Recipes> -### Preiniting in an event handler {/*preiniting-in-an-event-handler*/} +### Preinit trong một trình xử lý sự kiện {/*preiniting-in-an-event-handler*/} -Call `preinit` in an event handler before transitioning to a page or state where external resources will be needed. This gets the process started earlier than if you call it during the rendering of the new page or state. +Gọi `preinit` trong một trình xử lý sự kiện trước khi chuyển sang một trang hoặc trạng thái nơi các tài nguyên bên ngoài sẽ cần thiết. Điều này giúp quá trình bắt đầu sớm hơn so với khi bạn gọi nó trong quá trình hiển thị trang hoặc trạng thái mới. ```js import { preinit } from 'react-dom'; diff --git a/src/content/reference/react-dom/preinitModule.md b/src/content/reference/react-dom/preinitModule.md index 97bb4dbc7..7058447bd 100644 --- a/src/content/reference/react-dom/preinitModule.md +++ b/src/content/reference/react-dom/preinitModule.md @@ -4,13 +4,13 @@ title: preinitModule <Note> -[React-based frameworks](/learn/start-a-new-react-project) frequently handle resource loading for you, so you might not have to call this API yourself. Consult your framework's documentation for details. +[Các framework dựa trên React](/learn/start-a-new-react-project) thường tự động xử lý việc tải tài nguyên cho bạn, vì vậy bạn có thể không cần phải tự gọi API này. Tham khảo tài liệu của framework để biết thêm chi tiết. </Note> <Intro> -`preinitModule` lets you eagerly fetch and evaluate an ESM module. +`preinitModule` cho phép bạn tìm nạp và thực thi một module ESM một cách chủ động. ```js preinitModule("https://example.com/module.js", {as: "script"}); @@ -22,11 +22,11 @@ preinitModule("https://example.com/module.js", {as: "script"}); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `preinitModule(href, options)` {/*preinitmodule*/} -To preinit an ESM module, call the `preinitModule` function from `react-dom`. +Để khởi tạo trước một module ESM, hãy gọi hàm `preinitModule` từ `react-dom`. ```js import { preinitModule } from 'react-dom'; @@ -38,36 +38,36 @@ function AppRoot() { ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -The `preinitModule` function provides the browser with a hint that it should start downloading and executing the given module, which can save time. Modules that you `preinit` are executed when they finish downloading. +Hàm `preinitModule` cung cấp cho trình duyệt một gợi ý rằng nó nên bắt đầu tải xuống và thực thi module đã cho, điều này có thể tiết kiệm thời gian. Các module mà bạn `preinit` sẽ được thực thi khi chúng tải xuống xong. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `href`: a string. The URL of the module you want to download and execute. -* `options`: an object. It contains the following properties: - * `as`: a required string. It must be `'script'`. - * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. - * `integrity`: a string. A cryptographic hash of the module, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `nonce`: a string. A cryptographic [nonce to allow the module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. +* `href`: một chuỗi. URL của module bạn muốn tải xuống và thực thi. +* `options`: một đối tượng. Nó chứa các thuộc tính sau: + * `as`: một chuỗi bắt buộc. Nó phải là `'script'`. + * `crossOrigin`: một chuỗi. [Chính sách CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) để sử dụng. Các giá trị có thể là `anonymous` và `use-credentials`. + * `integrity`: một chuỗi. Một hàm băm mật mã của module, để [xác minh tính xác thực của nó](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). + * `nonce`: một chuỗi. Một [nonce mật mã để cho phép module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) khi sử dụng Chính sách Bảo mật Nội dung nghiêm ngặt. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`preinitModule` returns nothing. +`preinitModule` không trả về gì cả. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Multiple calls to `preinitModule` with the same `href` have the same effect as a single call. -* In the browser, you can call `preinitModule` in any situation: while rendering a component, in an Effect, in an event handler, and so on. -* In server-side rendering or when rendering Server Components, `preinitModule` only has an effect if you call it while rendering a component or in an async context originating from rendering a component. Any other calls will be ignored. +* Nhiều lệnh gọi đến `preinitModule` với cùng một `href` có cùng hiệu ứng như một lệnh gọi duy nhất. +* Trong trình duyệt, bạn có thể gọi `preinitModule` trong mọi tình huống: trong khi render một component, trong một Effect, trong một trình xử lý sự kiện, v.v. +* Trong quá trình render phía máy chủ hoặc khi render Server Components, `preinitModule` chỉ có hiệu lực nếu bạn gọi nó trong khi render một component hoặc trong một ngữ cảnh không đồng bộ bắt nguồn từ việc render một component. Bất kỳ lệnh gọi nào khác sẽ bị bỏ qua. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Preloading when rendering {/*preloading-when-rendering*/} +### Tải trước khi render {/*preloading-when-rendering*/} -Call `preinitModule` when rendering a component if you know that it or its children will use a specific module and you're OK with the module being evaluated and thereby taking effect immediately upon being downloaded. +Gọi `preinitModule` khi render một component nếu bạn biết rằng nó hoặc các component con của nó sẽ sử dụng một module cụ thể và bạn đồng ý với việc module được thực thi và do đó có hiệu lực ngay lập tức sau khi được tải xuống. ```js import { preinitModule } from 'react-dom'; @@ -78,11 +78,11 @@ function AppRoot() { } ``` -If you want the browser to download the module but not to execute it right away, use [`preloadModule`](/reference/react-dom/preloadModule) instead. If you want to preinit a script that isn't an ESM module, use [`preinit`](/reference/react-dom/preinit). +Nếu bạn muốn trình duyệt tải xuống module nhưng không thực thi nó ngay lập tức, hãy sử dụng [`preloadModule`](/reference/react-dom/preloadModule) thay thế. Nếu bạn muốn khởi tạo trước một script không phải là module ESM, hãy sử dụng [`preinit`](/reference/react-dom/preinit). -### Preloading in an event handler {/*preloading-in-an-event-handler*/} +### Tải trước trong một trình xử lý sự kiện {/*preloading-in-an-event-handler*/} -Call `preinitModule` in an event handler before transitioning to a page or state where the module will be needed. This gets the process started earlier than if you call it during the rendering of the new page or state. +Gọi `preinitModule` trong một trình xử lý sự kiện trước khi chuyển sang một trang hoặc trạng thái nơi module sẽ cần thiết. Điều này giúp quá trình bắt đầu sớm hơn so với khi bạn gọi nó trong quá trình render trang hoặc trạng thái mới. ```js import { preinitModule } from 'react-dom'; diff --git a/src/content/reference/react-dom/preload.md b/src/content/reference/react-dom/preload.md index 5dcba10f7..774ed3667 100644 --- a/src/content/reference/react-dom/preload.md +++ b/src/content/reference/react-dom/preload.md @@ -4,13 +4,13 @@ title: preload <Note> -[React-based frameworks](/learn/start-a-new-react-project) frequently handle resource loading for you, so you might not have to call this API yourself. Consult your framework's documentation for details. +[Các framework dựa trên React](/learn/start-a-new-react-project) thường tự động xử lý việc tải tài nguyên cho bạn, vì vậy bạn có thể không cần phải gọi API này. Tham khảo tài liệu của framework để biết thêm chi tiết. </Note> <Intro> -`preload` lets you eagerly fetch a resource such as a stylesheet, font, or external script that you expect to use. +`preload` cho phép bạn chủ động tìm nạp một tài nguyên như stylesheet, font hoặc script bên ngoài mà bạn dự định sử dụng. ```js preload("https://example.com/font.woff2", {as: "font"}); @@ -22,11 +22,11 @@ preload("https://example.com/font.woff2", {as: "font"}); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `preload(href, options)` {/*preload*/} -To preload a resource, call the `preload` function from `react-dom`. +Để tải trước một tài nguyên, hãy gọi hàm `preload` từ `react-dom`. ```js import { preload } from 'react-dom'; @@ -38,47 +38,47 @@ function AppRoot() { ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -The `preload` function provides the browser with a hint that it should start downloading the given resource, which can save time. +Hàm `preload` cung cấp cho trình duyệt một gợi ý rằng nó nên bắt đầu tải xuống tài nguyên đã cho, điều này có thể tiết kiệm thời gian. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `href`: a string. The URL of the resource you want to download. -* `options`: an object. It contains the following properties: - * `as`: a required string. The type of resource. Its [possible values](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#as) are `audio`, `document`, `embed`, `fetch`, `font`, `image`, `object`, `script`, `style`, `track`, `video`, `worker`. - * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. It is required when `as` is set to `"fetch"`. - * `referrerPolicy`: a string. The [Referrer header](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy) to send when fetching. Its possible values are `no-referrer-when-downgrade` (the default), `no-referrer`, `origin`, `origin-when-cross-origin`, and `unsafe-url`. - * `integrity`: a string. A cryptographic hash of the resource, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `type`: a string. The MIME type of the resource. - * `nonce`: a string. A cryptographic [nonce to allow the resource](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. - * `fetchPriority`: a string. Suggests a relative priority for fetching the resource. The possible values are `auto` (the default), `high`, and `low`. - * `imageSrcSet`: a string. For use only with `as: "image"`. Specifies the [source set of the image](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). - * `imageSizes`: a string. For use only with `as: "image"`. Specifies the [sizes of the image](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). +* `href`: một chuỗi. URL của tài nguyên bạn muốn tải xuống. +* `options`: một đối tượng. Nó chứa các thuộc tính sau: + * `as`: một chuỗi bắt buộc. Loại tài nguyên. Các [giá trị có thể](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#as) của nó là `audio`, `document`, `embed`, `fetch`, `font`, `image`, `object`, `script`, `style`, `track`, `video`, `worker`. + * `crossOrigin`: một chuỗi. [Chính sách CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) để sử dụng. Các giá trị có thể của nó là `anonymous` và `use-credentials`. Nó là bắt buộc khi `as` được đặt thành `"fetch"`. + * `referrerPolicy`: một chuỗi. [Tiêu đề Referrer](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#referrerpolicy) để gửi khi tìm nạp. Các giá trị có thể của nó là `no-referrer-when-downgrade` (mặc định), `no-referrer`, `origin`, `origin-when-cross-origin` và `unsafe-url`. + * `integrity`: một chuỗi. Một hàm băm mật mã của tài nguyên, để [xác minh tính xác thực của nó](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). + * `type`: một chuỗi. MIME type của tài nguyên. + * `nonce`: một chuỗi. Một [nonce mật mã để cho phép tài nguyên](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) khi sử dụng Content Security Policy nghiêm ngặt. + * `fetchPriority`: một chuỗi. Đề xuất mức độ ưu tiên tương đối để tìm nạp tài nguyên. Các giá trị có thể là `auto` (mặc định), `high` và `low`. + * `imageSrcSet`: một chuỗi. Chỉ sử dụng với `as: "image"`. Chỉ định [tập hợp nguồn của hình ảnh](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). + * `imageSizes`: một chuỗi. Chỉ sử dụng với `as: "image"`. Chỉ định [kích thước của hình ảnh](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`preload` returns nothing. +`preload` không trả về gì cả. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Multiple equivalent calls to `preload` have the same effect as a single call. Calls to `preload` are considered equivalent according to the following rules: - * Two calls are equivalent if they have the same `href`, except: - * If `as` is set to `image`, two calls are equivalent if they have the same `href`, `imageSrcSet`, and `imageSizes`. -* In the browser, you can call `preload` in any situation: while rendering a component, in an Effect, in an event handler, and so on. -* In server-side rendering or when rendering Server Components, `preload` only has an effect if you call it while rendering a component or in an async context originating from rendering a component. Any other calls will be ignored. +* Nhiều lệnh gọi tương đương đến `preload` có cùng hiệu ứng như một lệnh gọi duy nhất. Các lệnh gọi đến `preload` được coi là tương đương theo các quy tắc sau: + * Hai lệnh gọi là tương đương nếu chúng có cùng `href`, ngoại trừ: + * Nếu `as` được đặt thành `image`, hai lệnh gọi là tương đương nếu chúng có cùng `href`, `imageSrcSet` và `imageSizes`. +* Trong trình duyệt, bạn có thể gọi `preload` trong mọi tình huống: trong khi hiển thị một component, trong một Effect, trong một trình xử lý sự kiện, v.v. +* Trong quá trình render phía máy chủ hoặc khi render Server Components, `preload` chỉ có hiệu lực nếu bạn gọi nó trong khi render một component hoặc trong một ngữ cảnh không đồng bộ bắt nguồn từ việc render một component. Bất kỳ lệnh gọi nào khác sẽ bị bỏ qua. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Preloading when rendering {/*preloading-when-rendering*/} +### Tải trước khi render {/*preloading-when-rendering*/} -Call `preload` when rendering a component if you know that it or its children will use a specific resource. +Gọi `preload` khi render một component nếu bạn biết rằng nó hoặc các component con của nó sẽ sử dụng một tài nguyên cụ thể. -<Recipes titleText="Examples of preloading"> +<Recipes titleText="Ví dụ về tải trước"> -#### Preloading an external script {/*preloading-an-external-script*/} +#### Tải trước một script bên ngoài {/*preloading-an-external-script*/} ```js import { preload } from 'react-dom'; @@ -89,11 +89,11 @@ function AppRoot() { } ``` -If you want the browser to start executing the script immediately (rather than just downloading it), use [`preinit`](/reference/react-dom/preinit) instead. If you want to load an ESM module, use [`preloadModule`](/reference/react-dom/preloadModule). +Nếu bạn muốn trình duyệt bắt đầu thực thi script ngay lập tức (thay vì chỉ tải xuống), hãy sử dụng [`preinit`](/reference/react-dom/preinit) thay thế. Nếu bạn muốn tải một mô-đun ESM, hãy sử dụng [`preloadModule`](/reference/react-dom/preloadModule). <Solution /> -#### Preloading a stylesheet {/*preloading-a-stylesheet*/} +#### Tải trước một stylesheet {/*preloading-a-stylesheet*/} ```js import { preload } from 'react-dom'; @@ -104,11 +104,11 @@ function AppRoot() { } ``` -If you want the stylesheet to be inserted into the document immediately (which means the browser will start parsing it immediately rather than just downloading it), use [`preinit`](/reference/react-dom/preinit) instead. +Nếu bạn muốn stylesheet được chèn vào tài liệu ngay lập tức (có nghĩa là trình duyệt sẽ bắt đầu phân tích cú pháp nó ngay lập tức thay vì chỉ tải xuống), hãy sử dụng [`preinit`](/reference/react-dom/preinit) thay thế. <Solution /> -#### Preloading a font {/*preloading-a-font*/} +#### Tải trước một font {/*preloading-a-font*/} ```js import { preload } from 'react-dom'; @@ -120,11 +120,11 @@ function AppRoot() { } ``` -If you preload a stylesheet, it's smart to also preload any fonts that the stylesheet refers to. That way, the browser can start downloading the font before it's downloaded and parsed the stylesheet. +Nếu bạn tải trước một stylesheet, bạn cũng nên tải trước bất kỳ font nào mà stylesheet đó tham chiếu đến. Bằng cách đó, trình duyệt có thể bắt đầu tải xuống font trước khi nó tải xuống và phân tích cú pháp stylesheet. <Solution /> -#### Preloading an image {/*preloading-an-image*/} +#### Tải trước một hình ảnh {/*preloading-an-image*/} ```js import { preload } from 'react-dom'; @@ -139,15 +139,15 @@ function AppRoot() { } ``` -When preloading an image, the `imageSrcSet` and `imageSizes` options help the browser [fetch the correctly sized image for the size of the screen](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). +Khi tải trước một hình ảnh, các tùy chọn `imageSrcSet` và `imageSizes` giúp trình duyệt [tìm nạp hình ảnh có kích thước chính xác cho kích thước của màn hình](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images). <Solution /> </Recipes> -### Preloading in an event handler {/*preloading-in-an-event-handler*/} +### Tải trước trong một trình xử lý sự kiện {/*preloading-in-an-event-handler*/} -Call `preload` in an event handler before transitioning to a page or state where external resources will be needed. This gets the process started earlier than if you call it during the rendering of the new page or state. +Gọi `preload` trong một trình xử lý sự kiện trước khi chuyển sang một trang hoặc trạng thái nơi các tài nguyên bên ngoài sẽ cần thiết. Điều này giúp quá trình bắt đầu sớm hơn so với việc bạn gọi nó trong quá trình render trang hoặc trạng thái mới. ```js import { preload } from 'react-dom'; diff --git a/src/content/reference/react-dom/preloadModule.md b/src/content/reference/react-dom/preloadModule.md index ebc2fa6d0..31846aa6b 100644 --- a/src/content/reference/react-dom/preloadModule.md +++ b/src/content/reference/react-dom/preloadModule.md @@ -4,13 +4,13 @@ title: preloadModule <Note> -[React-based frameworks](/learn/start-a-new-react-project) frequently handle resource loading for you, so you might not have to call this API yourself. Consult your framework's documentation for details. +Các [framework dựa trên React](/learn/start-a-new-react-project) thường tự động xử lý việc tải tài nguyên cho bạn, vì vậy bạn có thể không cần phải gọi API này. Tham khảo tài liệu của framework để biết thêm chi tiết. </Note> <Intro> -`preloadModule` lets you eagerly fetch an ESM module that you expect to use. +`preloadModule` cho phép bạn chủ động tìm nạp một module ESM mà bạn dự định sử dụng. ```js preloadModule("https://example.com/module.js", {as: "script"}); @@ -22,11 +22,11 @@ preloadModule("https://example.com/module.js", {as: "script"}); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `preloadModule(href, options)` {/*preloadmodule*/} -To preload an ESM module, call the `preloadModule` function from `react-dom`. +Để tải trước một module ESM, hãy gọi hàm `preloadModule` từ `react-dom`. ```js import { preloadModule } from 'react-dom'; @@ -38,37 +38,36 @@ function AppRoot() { ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -The `preloadModule` function provides the browser with a hint that it should start downloading the given module, which can save time. +Hàm `preloadModule` cung cấp cho trình duyệt một gợi ý rằng nó nên bắt đầu tải xuống module đã cho, điều này có thể tiết kiệm thời gian. -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `href`: a string. The URL of the module you want to download. -* `options`: an object. It contains the following properties: - * `as`: a required string. It must be `'script'`. - * `crossOrigin`: a string. The [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) to use. Its possible values are `anonymous` and `use-credentials`. - * `integrity`: a string. A cryptographic hash of the module, to [verify its authenticity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). - * `nonce`: a string. A cryptographic [nonce to allow the module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) when using a strict Content Security Policy. +* `href`: một chuỗi. URL của module bạn muốn tải xuống. +* `options`: một đối tượng. Nó chứa các thuộc tính sau: + * `as`: một chuỗi bắt buộc. Nó phải là `'script'`. + * `crossOrigin`: một chuỗi. [Chính sách CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin) để sử dụng. Các giá trị có thể là `anonymous` và `use-credentials`. + * `integrity`: một chuỗi. Một hàm băm mật mã của module, để [xác minh tính xác thực của nó](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). + * `nonce`: một chuỗi. Một [nonce mật mã để cho phép module](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) khi sử dụng Chính sách Bảo mật Nội dung nghiêm ngặt. +#### Giá trị trả về {/*returns*/} -#### Returns {/*returns*/} +`preloadModule` không trả về gì cả. -`preloadModule` returns nothing. +#### Lưu ý {/*caveats*/} -#### Caveats {/*caveats*/} - -* Multiple calls to `preloadModule` with the same `href` have the same effect as a single call. -* In the browser, you can call `preloadModule` in any situation: while rendering a component, in an Effect, in an event handler, and so on. -* In server-side rendering or when rendering Server Components, `preloadModule` only has an effect if you call it while rendering a component or in an async context originating from rendering a component. Any other calls will be ignored. +* Nhiều lệnh gọi đến `preloadModule` với cùng một `href` có cùng hiệu ứng như một lệnh gọi duy nhất. +* Trong trình duyệt, bạn có thể gọi `preloadModule` trong mọi tình huống: trong khi hiển thị một component, trong một Effect, trong một trình xử lý sự kiện, v.v. +* Trong quá trình hiển thị phía máy chủ hoặc khi hiển thị Server Components, `preloadModule` chỉ có hiệu lực nếu bạn gọi nó trong khi hiển thị một component hoặc trong một ngữ cảnh không đồng bộ bắt nguồn từ việc hiển thị một component. Bất kỳ lệnh gọi nào khác sẽ bị bỏ qua. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Preloading when rendering {/*preloading-when-rendering*/} +### Tải trước khi hiển thị {/*preloading-when-rendering*/} -Call `preloadModule` when rendering a component if you know that it or its children will use a specific module. +Gọi `preloadModule` khi hiển thị một component nếu bạn biết rằng nó hoặc các component con của nó sẽ sử dụng một module cụ thể. ```js import { preloadModule } from 'react-dom'; @@ -79,11 +78,11 @@ function AppRoot() { } ``` -If you want the browser to start executing the module immediately (rather than just downloading it), use [`preinitModule`](/reference/react-dom/preinitModule) instead. If you want to load a script that isn't an ESM module, use [`preload`](/reference/react-dom/preload). +Nếu bạn muốn trình duyệt bắt đầu thực thi module ngay lập tức (thay vì chỉ tải xuống), hãy sử dụng [`preinitModule`](/reference/react-dom/preinitModule) thay thế. Nếu bạn muốn tải một script không phải là module ESM, hãy sử dụng [`preload`](/reference/react-dom/preload). -### Preloading in an event handler {/*preloading-in-an-event-handler*/} +### Tải trước trong một trình xử lý sự kiện {/*preloading-in-an-event-handler*/} -Call `preloadModule` in an event handler before transitioning to a page or state where the module will be needed. This gets the process started earlier than if you call it during the rendering of the new page or state. +Gọi `preloadModule` trong một trình xử lý sự kiện trước khi chuyển sang một trang hoặc trạng thái nơi module sẽ được cần đến. Điều này giúp quá trình bắt đầu sớm hơn so với việc bạn gọi nó trong quá trình hiển thị trang hoặc trạng thái mới. ```js import { preloadModule } from 'react-dom'; diff --git a/src/content/reference/react-dom/server/index.md b/src/content/reference/react-dom/server/index.md index bea11603d..230b46342 100644 --- a/src/content/reference/react-dom/server/index.md +++ b/src/content/reference/react-dom/server/index.md @@ -1,36 +1,36 @@ --- -title: Server React DOM APIs +title: Các API React DOM phía máy chủ --- <Intro> -The `react-dom/server` APIs let you server-side render React components to HTML. These APIs are only used on the server at the top level of your app to generate the initial HTML. A [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) may call them for you. Most of your components don't need to import or use them. +Các API `react-dom/server` cho phép bạn hiển thị phía máy chủ các thành phần React thành HTML. Các API này chỉ được sử dụng trên máy chủ ở cấp cao nhất của ứng dụng để tạo HTML ban đầu. Một [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) có thể gọi chúng cho bạn. Hầu hết các thành phần của bạn không cần nhập hoặc sử dụng chúng. </Intro> --- -## Server APIs for Node.js Streams {/*server-apis-for-nodejs-streams*/} +## Các API máy chủ cho Node.js Streams {/*server-apis-for-nodejs-streams*/} -These methods are only available in the environments with [Node.js Streams:](https://nodejs.org/api/stream.html) +Các phương thức này chỉ khả dụng trong các môi trường có [Node.js Streams:](https://nodejs.org/api/stream.html) -* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) renders a React tree to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html) +* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) hiển thị một cây React thành một [Node.js Stream](https://nodejs.org/api/stream.html) có thể truyền tải được. --- -## Server APIs for Web Streams {/*server-apis-for-web-streams*/} +## Các API máy chủ cho Web Streams {/*server-apis-for-web-streams*/} -These methods are only available in the environments with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), which includes browsers, Deno, and some modern edge runtimes: +Các phương thức này chỉ khả dụng trong các môi trường có [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), bao gồm trình duyệt, Deno và một số runtime edge hiện đại: -* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) renders a React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) hiển thị một cây React thành một [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) --- -## Legacy Server APIs for non-streaming environments {/*legacy-server-apis-for-non-streaming-environments*/} +## Các API máy chủ cũ cho các môi trường không hỗ trợ streaming {/*legacy-server-apis-for-non-streaming-environments*/} -These methods can be used in the environments that don't support streams: +Các phương thức này có thể được sử dụng trong các môi trường không hỗ trợ streams: -* [`renderToString`](/reference/react-dom/server/renderToString) renders a React tree to a string. -* [`renderToStaticMarkup`](/reference/react-dom/server/renderToStaticMarkup) renders a non-interactive React tree to a string. +* [`renderToString`](/reference/react-dom/server/renderToString) hiển thị một cây React thành một chuỗi. +* [`renderToStaticMarkup`](/reference/react-dom/server/renderToStaticMarkup) hiển thị một cây React không tương tác thành một chuỗi. -They have limited functionality compared to the streaming APIs. +Chúng có chức năng giới hạn so với các API streaming. diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md index 7d0d1ab3d..26d4467c5 100644 --- a/src/content/reference/react-dom/server/renderToPipeableStream.md +++ b/src/content/reference/react-dom/server/renderToPipeableStream.md @@ -4,7 +4,7 @@ title: renderToPipeableStream <Intro> -`renderToPipeableStream` renders a React tree to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html) +`renderToPipeableStream` kết xuất một cây React thành một [Node.js Stream](https://nodejs.org/api/stream.html) có thể truyền tải được. ```js const { pipe, abort } = renderToPipeableStream(reactNode, options?) @@ -16,17 +16,17 @@ const { pipe, abort } = renderToPipeableStream(reactNode, options?) <Note> -This API is specific to Node.js. Environments with [Web Streams,](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) like Deno and modern edge runtimes, should use [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) instead. +API này dành riêng cho Node.js. Các môi trường có [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), như Deno và các runtime edge hiện đại, nên sử dụng [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) thay thế. </Note> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `renderToPipeableStream(reactNode, options?)` {/*rendertopipeablestream*/} -Call `renderToPipeableStream` to render your React tree as HTML into a [Node.js Stream.](https://nodejs.org/api/stream.html#writable-streams) +Gọi `renderToPipeableStream` để kết xuất cây React của bạn thành HTML vào một [Node.js Stream.](https://nodejs.org/api/stream.html#writable-streams) ```js import { renderToPipeableStream } from 'react-dom/server'; @@ -40,47 +40,47 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive. +Trên client, gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để làm cho HTML được tạo từ server trở nên tương tác. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reactNode`: A React node you want to render to HTML. For example, a JSX element like `<App />`. It is expected to represent the entire document, so the `App` component should render the `<html>` tag. +* `reactNode`: Một node React mà bạn muốn kết xuất thành HTML. Ví dụ: một phần tử JSX như `<App />`. Nó được mong đợi đại diện cho toàn bộ tài liệu, vì vậy component `App` sẽ kết xuất thẻ `<html>`. -* **optional** `options`: An object with streaming options. - * **optional** `bootstrapScriptContent`: If specified, this string will be placed in an inline `<script>` tag. - * **optional** `bootstrapScripts`: An array of string URLs for the `<script>` tags to emit on the page. Use this to include the `<script>` that calls [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Omit it if you don't want to run React on the client at all. - * **optional** `bootstrapModules`: Like `bootstrapScripts`, but emits [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) instead. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) - * **optional** `namespaceURI`: A string with the root [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) for the stream. Defaults to regular HTML. Pass `'http://www.w3.org/2000/svg'` for SVG or `'http://www.w3.org/1998/Math/MathML'` for MathML. - * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). - * **optional** `onAllReady`: A callback that fires when all rendering is complete, including both the [shell](#specifying-what-goes-into-the-shell) and all additional [content.](#streaming-more-content-as-it-loads) You can use this instead of `onShellReady` [for crawlers and static generation.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you start streaming here, you won't get any progressive loading. The stream will contain the final HTML. - * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`. You can also use it to [adjust the status code](#setting-the-status-code) before the shell is emitted. - * **optional** `onShellReady`: A callback that fires right after the [initial shell](#specifying-what-goes-into-the-shell) has been rendered. You can [set the status code](#setting-the-status-code) and call `pipe` here to start streaming. React will [stream the additional content](#streaming-more-content-as-it-loads) after the shell along with the inline `<script>` tags that replace the HTML loading fallbacks with the content. - * **optional** `onShellError`: A callback that fires if there was an error rendering the initial shell. It receives the error as an argument. No bytes were emitted from the stream yet, and neither `onShellReady` nor `onAllReady` will get called, so you can [output a fallback HTML shell.](#recovering-from-errors-inside-the-shell) - * **optional** `progressiveChunkSize`: The number of bytes in a chunk. [Read more about the default heuristic.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) +* **optional** `options`: Một đối tượng với các tùy chọn streaming. + * **optional** `bootstrapScriptContent`: Nếu được chỉ định, chuỗi này sẽ được đặt trong một thẻ `<script>` nội tuyến. + * **optional** `bootstrapScripts`: Một mảng các URL chuỗi cho các thẻ `<script>` để phát ra trên trang. Sử dụng cái này để bao gồm `<script>` gọi [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Bỏ qua nếu bạn không muốn chạy React trên client. + * **optional** `bootstrapModules`: Giống như `bootstrapScripts`, nhưng phát ra [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) thay thế. + * **optional** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều root trên cùng một trang. Phải là cùng một tiền tố như đã truyền cho [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) + * **optional** `namespaceURI`: Một chuỗi với [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) gốc cho stream. Mặc định là HTML thông thường. Truyền `'http://www.w3.org/2000/svg'` cho SVG hoặc `'http://www.w3.org/1998/Math/MathML'` cho MathML. + * **optional** `nonce`: Một chuỗi [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) để cho phép các script cho [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). + * **optional** `onAllReady`: Một callback kích hoạt khi tất cả quá trình kết xuất hoàn tất, bao gồm cả [shell](#specifying-what-goes-into-the-shell) và tất cả [nội dung bổ sung.](#streaming-more-content-as-it-loads) Bạn có thể sử dụng cái này thay vì `onShellReady` [cho trình thu thập thông tin và tạo tĩnh.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) Nếu bạn bắt đầu streaming ở đây, bạn sẽ không nhận được bất kỳ tải tuần tự nào. Stream sẽ chứa HTML cuối cùng. + * **optional** `onError`: Một callback kích hoạt bất cứ khi nào có lỗi server, cho dù [có thể khôi phục được](#recovering-from-errors-outside-the-shell) hay [không.](#recovering-from-errors-inside-the-shell) Theo mặc định, cái này chỉ gọi `console.error`. Nếu bạn ghi đè nó để [ghi lại các báo cáo sự cố,](#logging-crashes-on-the-server) hãy đảm bảo rằng bạn vẫn gọi `console.error`. Bạn cũng có thể sử dụng nó để [điều chỉnh mã trạng thái](#setting-the-status-code) trước khi shell được phát ra. + * **optional** `onShellReady`: Một callback kích hoạt ngay sau khi [shell ban đầu](#specifying-what-goes-into-the-shell) đã được kết xuất. Bạn có thể [đặt mã trạng thái](#setting-the-status-code) và gọi `pipe` ở đây để bắt đầu streaming. React sẽ [stream nội dung bổ sung](#streaming-more-content-as-it-loads) sau shell cùng với các thẻ `<script>` nội tuyến thay thế các fallback tải HTML bằng nội dung. + * **optional** `onShellError`: Một callback kích hoạt nếu có lỗi khi kết xuất shell ban đầu. Nó nhận lỗi làm đối số. Chưa có byte nào được phát ra từ stream và cả `onShellReady` lẫn `onAllReady` sẽ không được gọi, vì vậy bạn có thể [xuất ra một shell HTML fallback.](#recovering-from-errors-inside-the-shell) + * **optional** `progressiveChunkSize`: Số byte trong một chunk. [Đọc thêm về heuristic mặc định.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) #### Returns {/*returns*/} -`renderToPipeableStream` returns an object with two methods: +`renderToPipeableStream` trả về một đối tượng với hai phương thức: -* `pipe` outputs the HTML into the provided [Writable Node.js Stream.](https://nodejs.org/api/stream.html#writable-streams) Call `pipe` in `onShellReady` if you want to enable streaming, or in `onAllReady` for crawlers and static generation. -* `abort` lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client. +* `pipe` xuất HTML vào [Writable Node.js Stream](https://nodejs.org/api/stream.html#writable-streams) được cung cấp. Gọi `pipe` trong `onShellReady` nếu bạn muốn bật streaming hoặc trong `onAllReady` cho trình thu thập thông tin và tạo tĩnh. +* `abort` cho phép bạn [hủy bỏ kết xuất server](#aborting-server-rendering) và kết xuất phần còn lại trên client. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering a React tree as HTML to a Node.js Stream {/*rendering-a-react-tree-as-html-to-a-nodejs-stream*/} +### Kết xuất một cây React thành HTML vào một Node.js Stream {/*rendering-a-react-tree-as-html-to-a-nodejs-stream*/} -Call `renderToPipeableStream` to render your React tree as HTML into a [Node.js Stream:](https://nodejs.org/api/stream.html#writable-streams) +Gọi `renderToPipeableStream` để kết xuất cây React của bạn thành HTML vào một [Node.js Stream:](https://nodejs.org/api/stream.html#writable-streams) ```js [[1, 5, "<App />"], [2, 6, "['/main.js']"]] import { renderToPipeableStream } from 'react-dom/server'; -// The route handler syntax depends on your backend framework +// Cú pháp route handler phụ thuộc vào framework backend của bạn app.use('/', (request, response) => { const { pipe } = renderToPipeableStream(<App />, { bootstrapScripts: ['/main.js'], @@ -92,9 +92,9 @@ app.use('/', (request, response) => { }); ``` -Along with the <CodeStep step={1}>root component</CodeStep>, you need to provide a list of <CodeStep step={2}>bootstrap `<script>` paths</CodeStep>. Your root component should return **the entire document including the root `<html>` tag.** +Cùng với <CodeStep step={1}>component root</CodeStep>, bạn cần cung cấp một danh sách <CodeStep step={2}>các đường dẫn `<script>` bootstrap</CodeStep>. Component root của bạn sẽ trả về **toàn bộ tài liệu bao gồm thẻ `<html>` gốc.** -For example, it might look like this: +Ví dụ: nó có thể trông như thế này: ```js [[1, 1, "App"]] export default function App() { @@ -104,7 +104,7 @@ export default function App() { <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="/styles.css"></link> - <title>My app</title> + <title>Ứng dụng của tôi</title> </head> <body> <Router /> @@ -114,17 +114,17 @@ export default function App() { } ``` -React will inject the [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) and your <CodeStep step={2}>bootstrap `<script>` tags</CodeStep> into the resulting HTML stream: +React sẽ chèn [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) và <CodeStep step={2}>các thẻ `<script>` bootstrap</CodeStep> của bạn vào stream HTML kết quả: ```html [[2, 5, "/main.js"]] <!DOCTYPE html> <html> - <!-- ... HTML from your components ... --> + <!-- ... HTML từ các component của bạn ... --> </html> <script src="/main.js" async=""></script> ``` -On the client, your bootstrap script should [hydrate the entire `document` with a call to `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) +Trên client, script bootstrap của bạn sẽ [hydrate toàn bộ `document` bằng một lệnh gọi đến `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) ```js [[1, 4, "<App />"]] import { hydrateRoot } from 'react-dom/client'; @@ -133,15 +133,15 @@ import App from './App.js'; hydrateRoot(document, <App />); ``` -This will attach event listeners to the server-generated HTML and make it interactive. +Điều này sẽ đính kèm các trình xử lý sự kiện vào HTML được tạo từ server và làm cho nó có tính tương tác. <DeepDive> -#### Reading CSS and JS asset paths from the build output {/*reading-css-and-js-asset-paths-from-the-build-output*/} +#### Đọc các đường dẫn CSS và JS asset từ đầu ra bản dựng {/*reading-css-and-js-asset-paths-from-the-build-output*/} -The final asset URLs (like JavaScript and CSS files) are often hashed after the build. For example, instead of `styles.css` you might end up with `styles.123456.css`. Hashing static asset filenames guarantees that every distinct build of the same asset will have a different filename. This is useful because it lets you safely enable long-term caching for static assets: a file with a certain name would never change content. +Các URL asset cuối cùng (như các tệp JavaScript và CSS) thường được băm sau khi bản dựng. Ví dụ: thay vì `styles.css`, bạn có thể kết thúc với `styles.123456.css`. Băm tên tệp asset tĩnh đảm bảo rằng mọi bản dựng riêng biệt của cùng một asset sẽ có một tên tệp khác nhau. Điều này rất hữu ích vì nó cho phép bạn bật bộ nhớ đệm dài hạn một cách an toàn cho các asset tĩnh: một tệp có một tên nhất định sẽ không bao giờ thay đổi nội dung. -However, if you don't know the asset URLs until after the build, there's no way for you to put them in the source code. For example, hardcoding `"/styles.css"` into JSX like earlier wouldn't work. To keep them out of your source code, your root component can read the real filenames from a map passed as a prop: +Tuy nhiên, nếu bạn không biết các URL asset cho đến sau bản dựng, bạn không có cách nào để đưa chúng vào mã nguồn. Ví dụ: mã hóa cứng `"/styles.css"` vào JSX như trước đây sẽ không hoạt động. Để giữ chúng bên ngoài mã nguồn của bạn, component root của bạn có thể đọc tên tệp thực từ một map được truyền dưới dạng một prop: ```js {1,6} export default function App({ assetMap }) { @@ -158,10 +158,10 @@ export default function App({ assetMap }) { } ``` -On the server, render `<App assetMap={assetMap} />` and pass your `assetMap` with the asset URLs: +Trên server, kết xuất `<App assetMap={assetMap} />` và truyền `assetMap` của bạn với các URL asset: ```js {1-5,8,9} -// You'd need to get this JSON from your build tooling, e.g. read it from the build output. +// Bạn sẽ cần lấy JSON này từ công cụ bản dựng của bạn, ví dụ: đọc nó từ đầu ra bản dựng. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -178,10 +178,10 @@ app.use('/', (request, response) => { }); ``` -Since your server is now rendering `<App assetMap={assetMap} />`, you need to render it with `assetMap` on the client too to avoid hydration errors. You can serialize and pass `assetMap` to the client like this: +Vì server của bạn hiện đang kết xuất `<App assetMap={assetMap} />`, bạn cần kết xuất nó với `assetMap` trên client để tránh các lỗi hydration. Bạn có thể tuần tự hóa và truyền `assetMap` cho client như thế này: ```js {9-10} -// You'd need to get this JSON from your build tooling. +// Bạn sẽ cần lấy JSON này từ công cụ bản dựng của bạn. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -189,7 +189,7 @@ const assetMap = { app.use('/', (request, response) => { const { pipe } = renderToPipeableStream(<App assetMap={assetMap} />, { - // Careful: It's safe to stringify() this because this data isn't user-generated. + // Cẩn thận: An toàn để stringify() cái này vì dữ liệu này không phải do người dùng tạo. bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, bootstrapScripts: [assetMap['main.js']], onShellReady() { @@ -200,7 +200,7 @@ app.use('/', (request, response) => { }); ``` -In the example above, the `bootstrapScriptContent` option adds an extra inline `<script>` tag that sets the global `window.assetMap` variable on the client. This lets the client code read the same `assetMap`: +Trong ví dụ trên, tùy chọn `bootstrapScriptContent` thêm một thẻ `<script>` nội tuyến bổ sung đặt biến `window.assetMap` toàn cục trên client. Điều này cho phép mã client đọc cùng một `assetMap`: ```js {4} import { hydrateRoot } from 'react-dom/client'; @@ -209,15 +209,15 @@ import App from './App.js'; hydrateRoot(document, <App assetMap={window.assetMap} />); ``` -Both client and server render `App` with the same `assetMap` prop, so there are no hydration errors. +Cả client và server đều kết xuất `App` với cùng một prop `assetMap`, vì vậy không có lỗi hydration. </DeepDive> --- -### Streaming more content as it loads {/*streaming-more-content-as-it-loads*/} +### Streaming thêm nội dung khi nó tải {/*streaming-more-content-as-it-loads*/} -Streaming allows the user to start seeing the content even before all the data has loaded on the server. For example, consider a profile page that shows a cover, a sidebar with friends and photos, and a list of posts: +Streaming cho phép người dùng bắt đầu xem nội dung ngay cả trước khi tất cả dữ liệu đã được tải trên server. Ví dụ: hãy xem xét một trang hồ sơ hiển thị ảnh bìa, một sidebar với bạn bè và ảnh và một danh sách các bài đăng: ```js function ProfilePage() { @@ -234,7 +234,7 @@ function ProfilePage() { } ``` -Imagine that loading data for `<Posts />` takes some time. Ideally, you'd want to show the rest of the profile page content to the user without waiting for the posts. To do this, [wrap `Posts` in a `<Suspense>` boundary:](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading) +Hãy tưởng tượng rằng việc tải dữ liệu cho `<Posts />` mất một chút thời gian. Lý tưởng nhất là bạn muốn hiển thị phần còn lại của nội dung trang hồ sơ cho người dùng mà không cần đợi các bài đăng. Để làm điều này, [gói `Posts` trong một boundary `<Suspense>`:](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading) ```js {9,11} function ProfilePage() { @@ -253,9 +253,9 @@ function ProfilePage() { } ``` -This tells React to start streaming the HTML before `Posts` loads its data. React will send the HTML for the loading fallback (`PostsGlimmer`) first, and then, when `Posts` finishes loading its data, React will send the remaining HTML along with an inline `<script>` tag that replaces the loading fallback with that HTML. From the user's perspective, the page will first appear with the `PostsGlimmer`, later replaced by the `Posts`. +Điều này cho React biết để bắt đầu streaming HTML trước khi `Posts` tải dữ liệu của nó. React sẽ gửi HTML cho fallback tải (`PostsGlimmer`) trước, và sau đó, khi `Posts` hoàn tất việc tải dữ liệu của nó, React sẽ gửi HTML còn lại cùng với một thẻ `<script>` nội tuyến thay thế fallback tải bằng HTML đó. Từ góc độ của người dùng, trang sẽ xuất hiện đầu tiên với `PostsGlimmer`, sau đó được thay thế bằng `Posts`. -You can further [nest `<Suspense>` boundaries](/reference/react/Suspense#revealing-nested-content-as-it-loads) to create a more granular loading sequence: +Bạn có thể tiếp tục [lồng các boundary `<Suspense>`](/reference/react/Suspense#revealing-nested-content-as-it-loads) để tạo một chuỗi tải chi tiết hơn: ```js {5,13} function ProfilePage() { @@ -276,33 +276,33 @@ function ProfilePage() { } ``` -In this example, React can start streaming the page even earlier. Only `ProfileLayout` and `ProfileCover` must finish rendering first because they are not wrapped in any `<Suspense>` boundary. However, if `Sidebar`, `Friends`, or `Photos` need to load some data, React will send the HTML for the `BigSpinner` fallback instead. Then, as more data becomes available, more content will continue to be revealed until all of it becomes visible. +Trong ví dụ này, React có thể bắt đầu streaming trang thậm chí sớm hơn. Chỉ `ProfileLayout` và `ProfileCover` phải hoàn tất việc kết xuất trước vì chúng không được gói trong bất kỳ boundary `<Suspense>`. Tuy nhiên, nếu `Sidebar`, `Friends` hoặc `Photos` cần tải một số dữ liệu, React sẽ gửi HTML cho fallback `BigSpinner` thay thế. Sau đó, khi có thêm dữ liệu, nhiều nội dung sẽ tiếp tục được hiển thị cho đến khi tất cả đều hiển thị. -Streaming does not need to wait for React itself to load in the browser, or for your app to become interactive. The HTML content from the server will get progressively revealed before any of the `<script>` tags load. +Streaming không cần phải đợi React tải trong trình duyệt hoặc cho ứng dụng của bạn trở nên tương tác. Nội dung HTML từ server sẽ được hiển thị dần dần trước khi bất kỳ thẻ `<script>` nào tải. -[Read more about how streaming HTML works.](https://github.com/reactwg/react-18/discussions/37) +[Đọc thêm về cách streaming HTML hoạt động.](https://github.com/reactwg/react-18/discussions/37) <Note> -**Only Suspense-enabled data sources will activate the Suspense component.** They include: +**Chỉ các nguồn dữ liệu hỗ trợ Suspense mới kích hoạt component Suspense.** Chúng bao gồm: -- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) -- Lazy-loading component code with [`lazy`](/reference/react/lazy) -- Reading the value of a Promise with [`use`](/reference/react/use) +- Tìm nạp dữ liệu với các framework hỗ trợ Suspense như [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) và [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Tải mã component một cách lazy với [`lazy`](/reference/react/lazy) +- Đọc giá trị của một Promise với [`use`](/reference/react/use) -Suspense **does not** detect when data is fetched inside an Effect or event handler. +Suspense **không** phát hiện khi dữ liệu được tìm nạp bên trong một Effect hoặc trình xử lý sự kiện. -The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. +Cách chính xác bạn sẽ tải dữ liệu trong component `Posts` ở trên phụ thuộc vào framework của bạn. Nếu bạn sử dụng một framework hỗ trợ Suspense, bạn sẽ tìm thấy các chi tiết trong tài liệu tìm nạp dữ liệu của nó. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Tìm nạp dữ liệu hỗ trợ Suspense mà không sử dụng framework có ý kiến vẫn chưa được hỗ trợ. Các yêu cầu để triển khai một nguồn dữ liệu hỗ trợ Suspense là không ổn định và không được ghi lại. Một API chính thức để tích hợp các nguồn dữ liệu với Suspense sẽ được phát hành trong một phiên bản React trong tương lai. </Note> --- -### Specifying what goes into the shell {/*specifying-what-goes-into-the-shell*/} +### Chỉ định những gì đi vào shell {/*specifying-what-goes-into-the-shell*/} -The part of your app outside of any `<Suspense>` boundaries is called *the shell:* +Phần ứng dụng của bạn bên ngoài bất kỳ boundary `<Suspense>` nào được gọi là *shell:* ```js {3-5,13,14} function ProfilePage() { @@ -323,7 +323,7 @@ function ProfilePage() { } ``` -It determines the earliest loading state that the user may see: +Nó xác định trạng thái tải sớm nhất mà người dùng có thể thấy: ```js {3-5,13 <ProfileLayout> @@ -332,9 +332,9 @@ It determines the earliest loading state that the user may see: </ProfileLayout> ``` -If you wrap the whole app into a `<Suspense>` boundary at the root, the shell will only contain that spinner. However, that's not a pleasant user experience because seeing a big spinner on the screen can feel slower and more annoying than waiting a bit more and seeing the real layout. This is why usually you'll want to place the `<Suspense>` boundaries so that the shell feels *minimal but complete*--like a skeleton of the entire page layout. +Nếu bạn gói toàn bộ ứng dụng vào một boundary `<Suspense>` ở root, shell sẽ chỉ chứa spinner đó. Tuy nhiên, đó không phải là một trải nghiệm người dùng dễ chịu vì việc nhìn thấy một spinner lớn trên màn hình có thể cảm thấy chậm hơn và khó chịu hơn là chờ đợi thêm một chút và nhìn thấy bố cục thực tế. Đây là lý do tại sao bạn thường muốn đặt các boundary `<Suspense>` sao cho shell cảm thấy *tối thiểu nhưng hoàn chỉnh*--giống như một bộ xương của toàn bộ bố cục trang. -The `onShellReady` callback fires when the entire shell has been rendered. Usually, you'll start streaming then: +Callback `onShellReady` kích hoạt khi toàn bộ shell đã được kết xuất. Thông thường, bạn sẽ bắt đầu streaming sau đó: ```js {3-6} const { pipe } = renderToPipeableStream(<App />, { @@ -346,13 +346,13 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -By the time `onShellReady` fires, components in nested `<Suspense>` boundaries might still be loading data. +Vào thời điểm `onShellReady` kích hoạt, các component trong các boundary `<Suspense>` lồng nhau vẫn có thể đang tải dữ liệu. --- -### Logging crashes on the server {/*logging-crashes-on-the-server*/} +### Ghi lại các sự cố trên server {/*logging-crashes-on-the-server*/} -By default, all errors on the server are logged to console. You can override this behavior to log crash reports: +Theo mặc định, tất cả các lỗi trên server được ghi vào console. Bạn có thể ghi đè hành vi này để ghi lại các báo cáo sự cố: ```js {7-10} const { pipe } = renderToPipeableStream(<App />, { @@ -368,13 +368,13 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -If you provide a custom `onError` implementation, don't forget to also log errors to the console like above. +Nếu bạn cung cấp một triển khai `onError` tùy chỉnh, đừng quên ghi lại các lỗi vào console như trên. --- -### Recovering from errors inside the shell {/*recovering-from-errors-inside-the-shell*/} +### Khôi phục từ các lỗi bên trong shell {/*recovering-from-errors-inside-the-shell*/} -In this example, the shell contains `ProfileLayout`, `ProfileCover`, and `PostsGlimmer`: +Trong ví dụ này, shell chứa `ProfileLayout`, `ProfileCover` và `PostsGlimmer`: ```js {3-5,7-8} function ProfilePage() { @@ -389,7 +389,7 @@ function ProfilePage() { } ``` -If an error occurs while rendering those components, React won't have any meaningful HTML to send to the client. Override `onShellError` to send a fallback HTML that doesn't rely on server rendering as the last resort: +Nếu một lỗi xảy ra trong khi kết xuất các component đó, React sẽ không có bất kỳ HTML có ý nghĩa nào để gửi đến client. Ghi đè `onShellError` để gửi một HTML fallback không dựa vào kết xuất server như là phương sách cuối cùng: ```js {7-11} const { pipe } = renderToPipeableStream(<App />, { @@ -401,7 +401,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Đã xảy ra lỗi</h1>'); }, onError(error) { console.error(error); @@ -410,13 +410,13 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -If there is an error while generating the shell, both `onError` and `onShellError` will fire. Use `onError` for error reporting and use `onShellError` to send the fallback HTML document. Your fallback HTML does not have to be an error page. Instead, you may include an alternative shell that renders your app on the client only. +Nếu có lỗi trong khi tạo shell, cả `onError` và `onShellError` sẽ kích hoạt. Sử dụng `onError` để báo cáo lỗi và sử dụng `onShellError` để gửi tài liệu HTML fallback. HTML fallback của bạn không cần phải là một trang lỗi. Thay vào đó, bạn có thể bao gồm một shell thay thế chỉ kết xuất ứng dụng của bạn trên client. --- -### Recovering from errors outside the shell {/*recovering-from-errors-outside-the-shell*/} +### Khôi phục từ các lỗi bên ngoài shell {/*recovering-from-errors-outside-the-shell*/} -In this example, the `<Posts />` component is wrapped in `<Suspense>` so it is *not* a part of the shell: +Trong ví dụ này, component `<Posts />` được gói trong `<Suspense>` vì vậy nó *không* phải là một phần của shell: ```js {6} function ProfilePage() { @@ -431,23 +431,23 @@ function ProfilePage() { } ``` -If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) +Nếu một lỗi xảy ra trong component `Posts` hoặc ở đâu đó bên trong nó, React sẽ [cố gắng khôi phục từ nó:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) -1. It will emit the loading fallback for the closest `<Suspense>` boundary (`PostsGlimmer`) into the HTML. -2. It will "give up" on trying to render the `Posts` content on the server anymore. -3. When the JavaScript code loads on the client, React will *retry* rendering `Posts` on the client. +1. Nó sẽ phát ra fallback tải cho boundary `<Suspense>` gần nhất (`PostsGlimmer`) vào HTML. +2. Nó sẽ "từ bỏ" việc cố gắng kết xuất nội dung `Posts` trên server nữa. +3. Khi mã JavaScript tải trên client, React sẽ *thử lại* kết xuất `Posts` trên client. -If retrying rendering `Posts` on the client *also* fails, React will throw the error on the client. As with all the errors thrown during rendering, the [closest parent error boundary](/reference/react/Component#static-getderivedstatefromerror) determines how to present the error to the user. In practice, this means that the user will see a loading indicator until it is certain that the error is not recoverable. +Nếu thử lại kết xuất `Posts` trên client *cũng* không thành công, React sẽ ném lỗi trên client. Như với tất cả các lỗi được ném trong quá trình kết xuất, [boundary lỗi cha gần nhất](/reference/react/Component#static-getderivedstatefromerror) xác định cách trình bày lỗi cho người dùng. Trong thực tế, điều này có nghĩa là người dùng sẽ thấy một chỉ báo tải cho đến khi chắc chắn rằng lỗi không thể khôi phục được. -If retrying rendering `Posts` on the client succeeds, the loading fallback from the server will be replaced with the client rendering output. The user will not know that there was a server error. However, the server `onError` callback and the client [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) callbacks will fire so that you can get notified about the error. +Nếu thử lại kết xuất `Posts` trên client thành công, fallback tải từ server sẽ được thay thế bằng đầu ra kết xuất client. Người dùng sẽ không biết rằng có một lỗi server. Tuy nhiên, callback `onError` của server và callback [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) của client sẽ kích hoạt để bạn có thể được thông báo về lỗi. --- -### Setting the status code {/*setting-the-status-code*/} +### Đặt mã trạng thái {/*setting-the-status-code*/} -Streaming introduces a tradeoff. You want to start streaming the page as early as possible so that the user can see the content sooner. However, once you start streaming, you can no longer set the response status code. +Streaming giới thiệu một sự đánh đổi. Bạn muốn bắt đầu streaming trang càng sớm càng tốt để người dùng có thể thấy nội dung sớm hơn. Tuy nhiên, khi bạn bắt đầu streaming, bạn không còn có thể đặt mã trạng thái phản hồi. -By [dividing your app](#specifying-what-goes-into-the-shell) into the shell (above all `<Suspense>` boundaries) and the rest of the content, you've already solved a part of this problem. If the shell errors, you'll get the `onShellError` callback which lets you set the error status code. Otherwise, you know that the app may recover on the client, so you can send "OK". +Bằng cách [chia ứng dụng của bạn](#specifying-what-goes-into-the-shell) thành shell (phía trên tất cả các boundary `<Suspense>`) và phần còn lại của nội dung, bạn đã giải quyết một phần của vấn đề này. Nếu shell bị lỗi, bạn sẽ nhận được callback `onShellError` cho phép bạn đặt mã trạng thái lỗi. Nếu không, bạn biết rằng ứng dụng có thể khôi phục trên client, vì vậy bạn có thể gửi "OK". ```js {4} const { pipe } = renderToPipeableStream(<App />, { @@ -460,7 +460,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Đã xảy ra lỗi</h1>'); }, onError(error) { console.error(error); @@ -469,9 +469,9 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -If a component *outside* the shell (i.e. inside a `<Suspense>` boundary) throws an error, React will not stop rendering. This means that the `onError` callback will fire, but you will still get `onShellReady` instead of `onShellError`. This is because React will try to recover from that error on the client, [as described above.](#recovering-from-errors-outside-the-shell) +Nếu một component *bên ngoài* shell (tức là bên trong một boundary `<Suspense>`) ném một lỗi, React sẽ không ngừng kết xuất. Điều này có nghĩa là callback `onError` sẽ kích hoạt, nhưng bạn vẫn sẽ nhận được `onShellReady` thay vì `onShellError`. Điều này là do React sẽ cố gắng khôi phục từ lỗi đó trên client, [như được mô tả ở trên.](#recovering-from-errors-outside-the-shell) -However, if you'd like, you can use the fact that something has errored to set the status code: +Tuy nhiên, nếu bạn muốn, bạn có thể sử dụng thực tế là một cái gì đó đã bị lỗi để đặt mã trạng thái: ```js {1,6,16} let didError = false; @@ -486,7 +486,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Đã xảy ra lỗi</h1>'); }, onError(error) { didError = true; @@ -496,13 +496,13 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -This will only catch errors outside the shell that happened while generating the initial shell content, so it's not exhaustive. If knowing whether an error occurred for some content is critical, you can move it up into the shell. +Điều này sẽ chỉ bắt các lỗi bên ngoài shell đã xảy ra trong khi tạo nội dung shell ban đầu, vì vậy nó không phải là đầy đủ. Nếu việc biết liệu một lỗi có xảy ra đối với một số nội dung là rất quan trọng, bạn có thể di chuyển nó lên shell. --- -### Handling different errors in different ways {/*handling-different-errors-in-different-ways*/} +### Xử lý các lỗi khác nhau theo những cách khác nhau {/*handling-different-errors-in-different-ways*/} -You can [create your own `Error` subclasses](https://javascript.info/custom-errors) and use the [`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) operator to check which error is thrown. For example, you can define a custom `NotFoundError` and throw it from your component. Then your `onError`, `onShellReady`, and `onShellError` callbacks can do something different depending on the error type: +Bạn có thể [tạo các lớp con `Error` của riêng bạn](https://javascript.info/custom-errors) và sử dụng toán tử [`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) để kiểm tra lỗi nào được ném. Ví dụ: bạn có thể xác định một `NotFoundError` tùy chỉnh và ném nó từ component của bạn. Sau đó, các callback `onError`, `onShellReady` và `onShellError` của bạn có thể làm một cái gì đó khác nhau tùy thuộc vào loại lỗi: ```js {2,4-14,19,24,30} let didError = false; @@ -530,7 +530,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = getStatusCode(); response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Đã xảy ra lỗi</h1>'); }, onError(error) { didError = true; @@ -541,22 +541,22 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -Keep in mind that once you emit the shell and start streaming, you can't change the status code. +Hãy nhớ rằng khi bạn phát ra shell và bắt đầu streaming, bạn không thể thay đổi mã trạng thái. --- -### Waiting for all content to load for crawlers and static generation {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/} +### Chờ tất cả nội dung tải cho trình thu thập thông tin và tạo tĩnh {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/} -Streaming offers a better user experience because the user can see the content as it becomes available. +Streaming cung cấp trải nghiệm người dùng tốt hơn vì người dùng có thể thấy nội dung khi nó có sẵn. -However, when a crawler visits your page, or if you're generating the pages at the build time, you might want to let all of the content load first and then produce the final HTML output instead of revealing it progressively. +Tuy nhiên, khi một trình thu thập thông tin truy cập trang của bạn hoặc nếu bạn đang tạo các trang tại thời điểm bản dựng, bạn có thể muốn cho phép tất cả nội dung tải trước và sau đó tạo ra đầu ra HTML cuối cùng thay vì hiển thị nó một cách tuần tự. -You can wait for all the content to load using the `onAllReady` callback: +Bạn có thể đợi tất cả nội dung tải bằng callback `onAllReady`: ```js {2,7,11,18-24} let didError = false; -let isCrawler = // ... depends on your bot detection strategy ... +let isCrawler = // ... phụ thuộc vào chiến lược phát hiện bot của bạn ... const { pipe } = renderToPipeableStream(<App />, { bootstrapScripts: ['/main.js'], @@ -570,7 +570,7 @@ const { pipe } = renderToPipeableStream(<App />, { onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); - response.send('<h1>Something went wrong</h1>'); + response.send('<h1>Đã xảy ra lỗi</h1>'); }, onAllReady() { if (isCrawler) { @@ -587,13 +587,13 @@ const { pipe } = renderToPipeableStream(<App />, { }); ``` -A regular visitor will get a stream of progressively loaded content. A crawler will receive the final HTML output after all the data loads. However, this also means that the crawler will have to wait for *all* data, some of which might be slow to load or error. Depending on your app, you could choose to send the shell to the crawlers too. +Một khách truy cập thông thường sẽ nhận được một stream nội dung được tải tuần tự. Một trình thu thập thông tin sẽ nhận được đầu ra HTML cuối cùng sau khi tất cả dữ liệu tải. Tuy nhiên, điều này cũng có nghĩa là trình thu thập thông tin sẽ phải đợi *tất cả* dữ liệu, một số trong đó có thể tải chậm hoặc bị lỗi. Tùy thuộc vào ứng dụng của bạn, bạn có thể chọn gửi shell cho trình thu thập thông tin. --- -### Aborting server rendering {/*aborting-server-rendering*/} +### Hủy bỏ kết xuất server {/*aborting-server-rendering*/} -You can force the server rendering to "give up" after a timeout: +Bạn có thể buộc kết xuất server "từ bỏ" sau một thời gian chờ: ```js {1,5-7} const { pipe, abort } = renderToPipeableStream(<App />, { @@ -605,4 +605,4 @@ setTimeout(() => { }, 10000); ``` -React will flush the remaining loading fallbacks as HTML, and will attempt to render the rest on the client. +React sẽ flush các fallback tải còn lại dưới dạng HTML và sẽ cố gắng kết xuất phần còn lại trên client. diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md index f407f2245..d9ed4b1d4 100644 --- a/src/content/reference/react-dom/server/renderToReadableStream.md +++ b/src/content/reference/react-dom/server/renderToReadableStream.md @@ -4,7 +4,7 @@ title: renderToReadableStream <Intro> -`renderToReadableStream` renders a React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +`renderToReadableStream` hiển thị một cây React thành một [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) ```js const stream = await renderToReadableStream(reactNode, options?) @@ -16,17 +16,17 @@ const stream = await renderToReadableStream(reactNode, options?) <Note> -This API depends on [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) For Node.js, use [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) instead. +API này phụ thuộc vào [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) Đối với Node.js, hãy sử dụng [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) thay thế. </Note> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `renderToReadableStream(reactNode, options?)` {/*rendertoreadablestream*/} -Call `renderToReadableStream` to render your React tree as HTML into a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +Gọi `renderToReadableStream` để hiển thị cây React của bạn thành HTML vào một [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) ```js import { renderToReadableStream } from 'react-dom/server'; @@ -41,44 +41,44 @@ async function handler(request) { } ``` -On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive. +Trên máy khách, gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để làm cho HTML được tạo từ máy chủ trở nên tương tác. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reactNode`: A React node you want to render to HTML. For example, a JSX element like `<App />`. It is expected to represent the entire document, so the `App` component should render the `<html>` tag. +* `reactNode`: Một nút React mà bạn muốn hiển thị thành HTML. Ví dụ: một phần tử JSX như `<App />`. Nó được mong đợi đại diện cho toàn bộ tài liệu, vì vậy thành phần `App` sẽ hiển thị thẻ `<html>`. -* **optional** `options`: An object with streaming options. - * **optional** `bootstrapScriptContent`: If specified, this string will be placed in an inline `<script>` tag. - * **optional** `bootstrapScripts`: An array of string URLs for the `<script>` tags to emit on the page. Use this to include the `<script>` that calls [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Omit it if you don't want to run React on the client at all. - * **optional** `bootstrapModules`: Like `bootstrapScripts`, but emits [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) instead. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) - * **optional** `namespaceURI`: A string with the root [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) for the stream. Defaults to regular HTML. Pass `'http://www.w3.org/2000/svg'` for SVG or `'http://www.w3.org/1998/Math/MathML'` for MathML. - * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). - * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`. You can also use it to [adjust the status code](#setting-the-status-code) before the shell is emitted. - * **optional** `progressiveChunkSize`: The number of bytes in a chunk. [Read more about the default heuristic.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) - * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client. +* **tùy chọn** `options`: Một đối tượng với các tùy chọn phát trực tuyến. + * **tùy chọn** `bootstrapScriptContent`: Nếu được chỉ định, chuỗi này sẽ được đặt trong một thẻ `<script>` nội tuyến. + * **tùy chọn** `bootstrapScripts`: Một mảng các URL chuỗi cho các thẻ `<script>` để phát ra trên trang. Sử dụng cái này để bao gồm `<script>` gọi [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Bỏ qua nó nếu bạn không muốn chạy React trên máy khách. + * **tùy chọn** `bootstrapModules`: Giống như `bootstrapScripts`, nhưng phát ra [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) thay thế. + * **tùy chọn** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều gốc trên cùng một trang. Phải là cùng một tiền tố như được chuyển đến [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) + * **tùy chọn** `namespaceURI`: Một chuỗi với [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) gốc cho luồng. Mặc định là HTML thông thường. Chuyển `'http://www.w3.org/2000/svg'` cho SVG hoặc `'http://www.w3.org/1998/Math/MathML'` cho MathML. + * **tùy chọn** `nonce`: Một chuỗi [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) để cho phép các tập lệnh cho [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). + * **tùy chọn** `onError`: Một callback kích hoạt bất cứ khi nào có lỗi máy chủ, cho dù [có thể khôi phục được](#recovering-from-errors-outside-the-shell) hay [không.](#recovering-from-errors-inside-the-shell) Theo mặc định, điều này chỉ gọi `console.error`. Nếu bạn ghi đè nó để [ghi lại báo cáo sự cố,](#logging-crashes-on-the-server) hãy đảm bảo rằng bạn vẫn gọi `console.error`. Bạn cũng có thể sử dụng nó để [điều chỉnh mã trạng thái](#setting-the-status-code) trước khi shell được phát ra. + * **tùy chọn** `progressiveChunkSize`: Số byte trong một chunk. [Đọc thêm về heuristic mặc định.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) + * **tùy chọn** `signal`: Một [tín hiệu hủy bỏ](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) cho phép bạn [hủy bỏ quá trình hiển thị máy chủ](#aborting-server-rendering) và hiển thị phần còn lại trên máy khách. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`renderToReadableStream` returns a Promise: +`renderToReadableStream` trả về một Promise: -- If rendering the [shell](#specifying-what-goes-into-the-shell) is successful, that Promise will resolve to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) -- If rendering the shell fails, the Promise will be rejected. [Use this to output a fallback shell.](#recovering-from-errors-inside-the-shell) +- Nếu hiển thị [shell](#specifying-what-goes-into-the-shell) thành công, Promise đó sẽ phân giải thành một [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +- Nếu hiển thị shell không thành công, Promise sẽ bị từ chối. [Sử dụng cái này để xuất một shell dự phòng.](#recovering-from-errors-inside-the-shell) -The returned stream has an additional property: +Luồng trả về có một thuộc tính bổ sung: -* `allReady`: A Promise that resolves when all rendering is complete, including both the [shell](#specifying-what-goes-into-the-shell) and all additional [content.](#streaming-more-content-as-it-loads) You can `await stream.allReady` before returning a response [for crawlers and static generation.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you do that, you won't get any progressive loading. The stream will contain the final HTML. +* `allReady`: Một Promise phân giải khi tất cả quá trình hiển thị hoàn tất, bao gồm cả [shell](#specifying-what-goes-into-the-shell) và tất cả [nội dung bổ sung.](#streaming-more-content-as-it-loads) Bạn có thể `await stream.allReady` trước khi trả về một phản hồi [cho trình thu thập thông tin và tạo tĩnh.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) Nếu bạn làm điều đó, bạn sẽ không nhận được bất kỳ tải lũy tiến nào. Luồng sẽ chứa HTML cuối cùng. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering a React tree as HTML to a Readable Web Stream {/*rendering-a-react-tree-as-html-to-a-readable-web-stream*/} +### Hiển thị một cây React thành HTML thành một Readable Web Stream {/*rendering-a-react-tree-as-html-to-a-readable-web-stream*/} -Call `renderToReadableStream` to render your React tree as HTML into a [Readable Web Stream:](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +Gọi `renderToReadableStream` để hiển thị cây React của bạn thành HTML vào một [Readable Web Stream:](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) ```js [[1, 4, "<App />"], [2, 5, "['/main.js']"]] import { renderToReadableStream } from 'react-dom/server'; @@ -93,9 +93,9 @@ async function handler(request) { } ``` -Along with the <CodeStep step={1}>root component</CodeStep>, you need to provide a list of <CodeStep step={2}>bootstrap `<script>` paths</CodeStep>. Your root component should return **the entire document including the root `<html>` tag.** +Cùng với <CodeStep step={1}>thành phần gốc</CodeStep>, bạn cần cung cấp một danh sách <CodeStep step={2}>đường dẫn `<script>` bootstrap</CodeStep>. Thành phần gốc của bạn sẽ trả về **toàn bộ tài liệu bao gồm thẻ `<html>` gốc.** -For example, it might look like this: +Ví dụ: nó có thể trông như thế này: ```js [[1, 1, "App"]] export default function App() { @@ -105,7 +105,7 @@ export default function App() { <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="/styles.css"></link> - <title>My app</title> + <title>Ứng dụng của tôi</title> </head> <body> <Router /> @@ -115,17 +115,17 @@ export default function App() { } ``` -React will inject the [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) and your <CodeStep step={2}>bootstrap `<script>` tags</CodeStep> into the resulting HTML stream: +React sẽ chèn [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) và <CodeStep step={2}>thẻ `<script>` bootstrap</CodeStep> của bạn vào luồng HTML kết quả: ```html [[2, 5, "/main.js"]] <!DOCTYPE html> <html> - <!-- ... HTML from your components ... --> + {/* ... HTML từ các thành phần của bạn ... */} </html> <script src="/main.js" async=""></script> ``` -On the client, your bootstrap script should [hydrate the entire `document` with a call to `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) +Trên máy khách, tập lệnh bootstrap của bạn sẽ [hydrate toàn bộ `document` bằng một lệnh gọi đến `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) ```js [[1, 4, "<App />"]] import { hydrateRoot } from 'react-dom/client'; @@ -134,22 +134,22 @@ import App from './App.js'; hydrateRoot(document, <App />); ``` -This will attach event listeners to the server-generated HTML and make it interactive. +Điều này sẽ đính kèm các trình nghe sự kiện vào HTML được tạo từ máy chủ và làm cho nó có tính tương tác. <DeepDive> -#### Reading CSS and JS asset paths from the build output {/*reading-css-and-js-asset-paths-from-the-build-output*/} +#### Đọc đường dẫn CSS và JS asset từ đầu ra bản dựng {/*reading-css-and-js-asset-paths-from-the-build-output*/} -The final asset URLs (like JavaScript and CSS files) are often hashed after the build. For example, instead of `styles.css` you might end up with `styles.123456.css`. Hashing static asset filenames guarantees that every distinct build of the same asset will have a different filename. This is useful because it lets you safely enable long-term caching for static assets: a file with a certain name would never change content. +Các URL asset cuối cùng (như tệp JavaScript và CSS) thường được băm sau khi xây dựng. Ví dụ: thay vì `styles.css`, bạn có thể kết thúc với `styles.123456.css`. Băm tên tệp asset tĩnh đảm bảo rằng mọi bản dựng riêng biệt của cùng một asset sẽ có một tên tệp khác nhau. Điều này rất hữu ích vì nó cho phép bạn bật bộ nhớ cache dài hạn một cách an toàn cho các asset tĩnh: một tệp có tên nhất định sẽ không bao giờ thay đổi nội dung. -However, if you don't know the asset URLs until after the build, there's no way for you to put them in the source code. For example, hardcoding `"/styles.css"` into JSX like earlier wouldn't work. To keep them out of your source code, your root component can read the real filenames from a map passed as a prop: +Tuy nhiên, nếu bạn không biết URL asset cho đến sau khi xây dựng, bạn không có cách nào để đưa chúng vào mã nguồn. Ví dụ: mã hóa cứng `"/styles.css"` vào JSX như trước đây sẽ không hoạt động. Để giữ chúng bên ngoài mã nguồn của bạn, thành phần gốc của bạn có thể đọc tên tệp thực từ một bản đồ được truyền dưới dạng một prop: ```js {1,6} export default function App({ assetMap }) { return ( <html> <head> - <title>My app</title> + <title>Ứng dụng của tôi</title> <link rel="stylesheet" href={assetMap['styles.css']}></link> </head> ... @@ -158,10 +158,10 @@ export default function App({ assetMap }) { } ``` -On the server, render `<App assetMap={assetMap} />` and pass your `assetMap` with the asset URLs: +Trên máy chủ, hiển thị `<App assetMap={assetMap} />` và chuyển `assetMap` của bạn với các URL asset: ```js {1-5,8,9} -// You'd need to get this JSON from your build tooling, e.g. read it from the build output. +// Bạn cần lấy JSON này từ công cụ xây dựng của bạn, ví dụ: đọc nó từ đầu ra bản dựng. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -177,10 +177,10 @@ async function handler(request) { } ``` -Since your server is now rendering `<App assetMap={assetMap} />`, you need to render it with `assetMap` on the client too to avoid hydration errors. You can serialize and pass `assetMap` to the client like this: +Vì máy chủ của bạn hiện đang hiển thị `<App assetMap={assetMap} />`, bạn cần hiển thị nó với `assetMap` trên máy khách để tránh lỗi hydration. Bạn có thể tuần tự hóa và chuyển `assetMap` cho máy khách như thế này: ```js {9-10} -// You'd need to get this JSON from your build tooling. +// Bạn cần lấy JSON này từ công cụ xây dựng của bạn. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -188,7 +188,7 @@ const assetMap = { async function handler(request) { const stream = await renderToReadableStream(<App assetMap={assetMap} />, { - // Careful: It's safe to stringify() this because this data isn't user-generated. + // Cẩn thận: An toàn để stringify() cái này vì dữ liệu này không phải do người dùng tạo. bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, bootstrapScripts: [assetMap['/main.js']], }); @@ -198,7 +198,7 @@ async function handler(request) { } ``` -In the example above, the `bootstrapScriptContent` option adds an extra inline `<script>` tag that sets the global `window.assetMap` variable on the client. This lets the client code read the same `assetMap`: +Trong ví dụ trên, tùy chọn `bootstrapScriptContent` thêm một thẻ `<script>` nội tuyến bổ sung đặt biến `window.assetMap` toàn cục trên máy khách. Điều này cho phép mã máy khách đọc cùng một `assetMap`: ```js {4} import { hydrateRoot } from 'react-dom/client'; @@ -207,15 +207,15 @@ import App from './App.js'; hydrateRoot(document, <App assetMap={window.assetMap} />); ``` -Both client and server render `App` with the same `assetMap` prop, so there are no hydration errors. +Cả máy khách và máy chủ đều hiển thị `App` với cùng một prop `assetMap`, vì vậy không có lỗi hydration. </DeepDive> --- -### Streaming more content as it loads {/*streaming-more-content-as-it-loads*/} +### Phát trực tuyến thêm nội dung khi nó tải {/*streaming-more-content-as-it-loads*/} -Streaming allows the user to start seeing the content even before all the data has loaded on the server. For example, consider a profile page that shows a cover, a sidebar with friends and photos, and a list of posts: +Phát trực tuyến cho phép người dùng bắt đầu xem nội dung ngay cả trước khi tất cả dữ liệu đã được tải trên máy chủ. Ví dụ: hãy xem xét một trang hồ sơ hiển thị ảnh bìa, một thanh bên với bạn bè và ảnh và một danh sách các bài đăng: ```js function ProfilePage() { @@ -232,7 +232,7 @@ function ProfilePage() { } ``` -Imagine that loading data for `<Posts />` takes some time. Ideally, you'd want to show the rest of the profile page content to the user without waiting for the posts. To do this, [wrap `Posts` in a `<Suspense>` boundary:](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading) +Hãy tưởng tượng rằng việc tải dữ liệu cho `<Posts />` mất một chút thời gian. Lý tưởng nhất là bạn muốn hiển thị phần còn lại của nội dung trang hồ sơ cho người dùng mà không cần chờ các bài đăng. Để làm điều này, [gói `Posts` trong một ranh giới `<Suspense>`:](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading) ```js {9,11} function ProfilePage() { @@ -251,9 +251,9 @@ function ProfilePage() { } ``` -This tells React to start streaming the HTML before `Posts` loads its data. React will send the HTML for the loading fallback (`PostsGlimmer`) first, and then, when `Posts` finishes loading its data, React will send the remaining HTML along with an inline `<script>` tag that replaces the loading fallback with that HTML. From the user's perspective, the page will first appear with the `PostsGlimmer`, later replaced by the `Posts`. +Điều này cho React biết để bắt đầu phát trực tuyến HTML trước khi `Posts` tải dữ liệu của nó. React sẽ gửi HTML cho fallback tải (`PostsGlimmer`) trước, và sau đó, khi `Posts` hoàn tất việc tải dữ liệu của nó, React sẽ gửi HTML còn lại cùng với một thẻ `<script>` nội tuyến thay thế fallback tải bằng HTML đó. Từ quan điểm của người dùng, trang sẽ xuất hiện đầu tiên với `PostsGlimmer`, sau đó được thay thế bằng `Posts`. -You can further [nest `<Suspense>` boundaries](/reference/react/Suspense#revealing-nested-content-as-it-loads) to create a more granular loading sequence: +Bạn có thể tiếp tục [lồng các ranh giới `<Suspense>`](/reference/react/Suspense#revealing-nested-content-as-it-loads) để tạo một chuỗi tải chi tiết hơn: ```js {5,13} function ProfilePage() { @@ -275,33 +275,33 @@ function ProfilePage() { ``` -In this example, React can start streaming the page even earlier. Only `ProfileLayout` and `ProfileCover` must finish rendering first because they are not wrapped in any `<Suspense>` boundary. However, if `Sidebar`, `Friends`, or `Photos` need to load some data, React will send the HTML for the `BigSpinner` fallback instead. Then, as more data becomes available, more content will continue to be revealed until all of it becomes visible. +Trong ví dụ này, React có thể bắt đầu phát trực tuyến trang thậm chí sớm hơn. Chỉ `ProfileLayout` và `ProfileCover` phải hoàn thành hiển thị trước vì chúng không được gói trong bất kỳ ranh giới `<Suspense>` nào. Tuy nhiên, nếu `Sidebar`, `Friends` hoặc `Photos` cần tải một số dữ liệu, React sẽ gửi HTML cho fallback `BigSpinner` thay thế. Sau đó, khi có thêm dữ liệu, nhiều nội dung sẽ tiếp tục được hiển thị cho đến khi tất cả đều trở nên hiển thị. -Streaming does not need to wait for React itself to load in the browser, or for your app to become interactive. The HTML content from the server will get progressively revealed before any of the `<script>` tags load. +Phát trực tuyến không cần phải đợi React tự tải trong trình duyệt hoặc cho ứng dụng của bạn trở nên tương tác. Nội dung HTML từ máy chủ sẽ được hiển thị dần dần trước khi bất kỳ thẻ `<script>` nào tải. -[Read more about how streaming HTML works.](https://github.com/reactwg/react-18/discussions/37) +[Đọc thêm về cách hoạt động của HTML phát trực tuyến.](https://github.com/reactwg/react-18/discussions/37) <Note> -**Only Suspense-enabled data sources will activate the Suspense component.** They include: +**Chỉ các nguồn dữ liệu hỗ trợ Suspense mới kích hoạt thành phần Suspense.** Chúng bao gồm: -- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) -- Lazy-loading component code with [`lazy`](/reference/react/lazy) -- Reading the value of a Promise with [`use`](/reference/react/use) +- Tìm nạp dữ liệu với các framework hỗ trợ Suspense như [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) và [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Tải mã thành phần lười biếng với [`lazy`](/reference/react/lazy) +- Đọc giá trị của một Promise với [`use`](/reference/react/use) -Suspense **does not** detect when data is fetched inside an Effect or event handler. +Suspense **không** phát hiện khi dữ liệu được tìm nạp bên trong một Effect hoặc trình xử lý sự kiện. -The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. +Cách chính xác bạn sẽ tải dữ liệu trong thành phần `Posts` ở trên phụ thuộc vào framework của bạn. Nếu bạn sử dụng một framework hỗ trợ Suspense, bạn sẽ tìm thấy các chi tiết trong tài liệu tìm nạp dữ liệu của nó. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Tìm nạp dữ liệu hỗ trợ Suspense mà không sử dụng một framework có ý kiến vẫn chưa được hỗ trợ. Các yêu cầu để triển khai một nguồn dữ liệu hỗ trợ Suspense là không ổn định và không được ghi lại. Một API chính thức để tích hợp các nguồn dữ liệu với Suspense sẽ được phát hành trong một phiên bản React trong tương lai. </Note> --- -### Specifying what goes into the shell {/*specifying-what-goes-into-the-shell*/} +### Chỉ định những gì đi vào shell {/*specifying-what-goes-into-the-shell*/} -The part of your app outside of any `<Suspense>` boundaries is called *the shell:* +Phần ứng dụng của bạn bên ngoài bất kỳ ranh giới `<Suspense>` nào được gọi là *shell:* ```js {3-5,13,14} function ProfilePage() { @@ -322,7 +322,7 @@ function ProfilePage() { } ``` -It determines the earliest loading state that the user may see: +Nó xác định trạng thái tải sớm nhất mà người dùng có thể thấy: ```js {3-5,13 <ProfileLayout> @@ -331,9 +331,9 @@ It determines the earliest loading state that the user may see: </ProfileLayout> ``` -If you wrap the whole app into a `<Suspense>` boundary at the root, the shell will only contain that spinner. However, that's not a pleasant user experience because seeing a big spinner on the screen can feel slower and more annoying than waiting a bit more and seeing the real layout. This is why usually you'll want to place the `<Suspense>` boundaries so that the shell feels *minimal but complete*--like a skeleton of the entire page layout. +Nếu bạn gói toàn bộ ứng dụng vào một ranh giới `<Suspense>` ở gốc, shell sẽ chỉ chứa spinner đó. Tuy nhiên, đó không phải là một trải nghiệm người dùng dễ chịu vì việc nhìn thấy một spinner lớn trên màn hình có thể cảm thấy chậm hơn và khó chịu hơn là chờ đợi thêm một chút và nhìn thấy bố cục thực tế. Đây là lý do tại sao bạn thường muốn đặt các ranh giới `<Suspense>` để shell cảm thấy *tối thiểu nhưng hoàn chỉnh*--giống như một bộ xương của toàn bộ bố cục trang. -The async call to `renderToReadableStream` will resolve to a `stream` as soon as the entire shell has been rendered. Usually, you'll start streaming then by creating and returning a response with that `stream`: +Lệnh gọi không đồng bộ đến `renderToReadableStream` sẽ phân giải thành một `stream` ngay sau khi toàn bộ shell đã được hiển thị. Thông thường, bạn sẽ bắt đầu phát trực tuyến sau đó bằng cách tạo và trả về một phản hồi với `stream` đó: ```js {5} async function handler(request) { @@ -346,13 +346,13 @@ async function handler(request) { } ``` -By the time the `stream` is returned, components in nested `<Suspense>` boundaries might still be loading data. +Vào thời điểm `stream` được trả về, các thành phần trong các ranh giới `<Suspense>` lồng nhau có thể vẫn đang tải dữ liệu. --- -### Logging crashes on the server {/*logging-crashes-on-the-server*/} +### Ghi lại sự cố trên máy chủ {/*logging-crashes-on-the-server*/} -By default, all errors on the server are logged to console. You can override this behavior to log crash reports: +Theo mặc định, tất cả các lỗi trên máy chủ được ghi vào console. Bạn có thể ghi đè hành vi này để ghi lại báo cáo sự cố: ```js {4-7} async function handler(request) { @@ -369,13 +369,13 @@ async function handler(request) { } ``` -If you provide a custom `onError` implementation, don't forget to also log errors to the console like above. +Nếu bạn cung cấp một triển khai `onError` tùy chỉnh, đừng quên ghi lại các lỗi vào console như trên. --- -### Recovering from errors inside the shell {/*recovering-from-errors-inside-the-shell*/} +### Khôi phục từ các lỗi bên trong shell {/*recovering-from-errors-inside-the-shell*/} -In this example, the shell contains `ProfileLayout`, `ProfileCover`, and `PostsGlimmer`: +Trong ví dụ này, shell chứa `ProfileLayout`, `ProfileCover` và `PostsGlimmer`: ```js {3-5,7-8} function ProfilePage() { @@ -390,7 +390,7 @@ function ProfilePage() { } ``` -If an error occurs while rendering those components, React won't have any meaningful HTML to send to the client. Wrap your `renderToReadableStream` call in a `try...catch` to send a fallback HTML that doesn't rely on server rendering as the last resort: +Nếu một lỗi xảy ra trong khi hiển thị các thành phần đó, React sẽ không có bất kỳ HTML có ý nghĩa nào để gửi đến máy khách. Gói lệnh gọi `renderToReadableStream` của bạn trong một `try...catch` để gửi một HTML fallback không dựa vào hiển thị máy chủ như là phương sách cuối cùng: ```js {2,13-18} async function handler(request) { @@ -406,7 +406,7 @@ async function handler(request) { headers: { 'content-type': 'text/html' }, }); } catch (error) { - return new Response('<h1>Something went wrong</h1>', { + return new Response('<h1>Đã xảy ra lỗi</h1>', { status: 500, headers: { 'content-type': 'text/html' }, }); @@ -414,13 +414,13 @@ async function handler(request) { } ``` -If there is an error while generating the shell, both `onError` and your `catch` block will fire. Use `onError` for error reporting and use the `catch` block to send the fallback HTML document. Your fallback HTML does not have to be an error page. Instead, you may include an alternative shell that renders your app on the client only. +Nếu có một lỗi trong khi tạo shell, cả `onError` và khối `catch` của bạn sẽ kích hoạt. Sử dụng `onError` để báo cáo lỗi và sử dụng khối `catch` để gửi tài liệu HTML fallback. HTML fallback của bạn không cần phải là một trang lỗi. Thay vào đó, bạn có thể bao gồm một shell thay thế chỉ hiển thị ứng dụng của bạn trên máy khách. --- -### Recovering from errors outside the shell {/*recovering-from-errors-outside-the-shell*/} +### Khôi phục từ các lỗi bên ngoài shell {/*recovering-from-errors-outside-the-shell*/} -In this example, the `<Posts />` component is wrapped in `<Suspense>` so it is *not* a part of the shell: +Trong ví dụ này, thành phần `<Posts />` được gói trong `<Suspense>` vì vậy nó *không* phải là một phần của shell: ```js {6} function ProfilePage() { @@ -435,23 +435,23 @@ function ProfilePage() { } ``` -If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) +Nếu một lỗi xảy ra trong thành phần `Posts` hoặc đâu đó bên trong nó, React sẽ [cố gắng khôi phục từ nó:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) -1. It will emit the loading fallback for the closest `<Suspense>` boundary (`PostsGlimmer`) into the HTML. -2. It will "give up" on trying to render the `Posts` content on the server anymore. -3. When the JavaScript code loads on the client, React will *retry* rendering `Posts` on the client. +1. Nó sẽ phát ra fallback tải cho ranh giới `<Suspense>` gần nhất (`PostsGlimmer`) vào HTML. +2. Nó sẽ "từ bỏ" việc cố gắng hiển thị nội dung `Posts` trên máy chủ nữa. +3. Khi mã JavaScript tải trên máy khách, React sẽ *thử lại* hiển thị `Posts` trên máy khách. -If retrying rendering `Posts` on the client *also* fails, React will throw the error on the client. As with all the errors thrown during rendering, the [closest parent error boundary](/reference/react/Component#static-getderivedstatefromerror) determines how to present the error to the user. In practice, this means that the user will see a loading indicator until it is certain that the error is not recoverable. +Nếu thử lại hiển thị `Posts` trên máy khách *cũng* không thành công, React sẽ ném lỗi trên máy khách. Như với tất cả các lỗi được ném trong quá trình hiển thị, [ranh giới lỗi mẹ gần nhất](/reference/react/Component#static-getderivedstatefromerror) xác định cách trình bày lỗi cho người dùng. Trong thực tế, điều này có nghĩa là người dùng sẽ thấy một chỉ báo tải cho đến khi chắc chắn rằng lỗi không thể khôi phục được. -If retrying rendering `Posts` on the client succeeds, the loading fallback from the server will be replaced with the client rendering output. The user will not know that there was a server error. However, the server `onError` callback and the client [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) callbacks will fire so that you can get notified about the error. +Nếu thử lại hiển thị `Posts` trên máy khách thành công, fallback tải từ máy chủ sẽ được thay thế bằng đầu ra hiển thị máy khách. Người dùng sẽ không biết rằng có một lỗi máy chủ. Tuy nhiên, callback `onError` máy chủ và callback [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) máy khách sẽ kích hoạt để bạn có thể được thông báo về lỗi. --- -### Setting the status code {/*setting-the-status-code*/} +### Đặt mã trạng thái {/*setting-the-status-code*/} -Streaming introduces a tradeoff. You want to start streaming the page as early as possible so that the user can see the content sooner. However, once you start streaming, you can no longer set the response status code. +Phát trực tuyến giới thiệu một sự đánh đổi. Bạn muốn bắt đầu phát trực tuyến trang càng sớm càng tốt để người dùng có thể thấy nội dung sớm hơn. Tuy nhiên, khi bạn bắt đầu phát trực tuyến, bạn không còn có thể đặt mã trạng thái phản hồi. -By [dividing your app](#specifying-what-goes-into-the-shell) into the shell (above all `<Suspense>` boundaries) and the rest of the content, you've already solved a part of this problem. If the shell errors, your `catch` block will run which lets you set the error status code. Otherwise, you know that the app may recover on the client, so you can send "OK". +Bằng cách [chia ứng dụng của bạn](#specifying-what-goes-into-the-shell) thành shell (trên tất cả các ranh giới `<Suspense>`) và phần còn lại của nội dung, bạn đã giải quyết một phần của vấn đề này. Nếu shell bị lỗi, khối `catch` của bạn sẽ chạy cho phép bạn đặt mã trạng thái lỗi. Nếu không, bạn biết rằng ứng dụng có thể khôi phục trên máy khách, vì vậy bạn có thể gửi "OK". ```js {11} async function handler(request) { @@ -468,7 +468,7 @@ async function handler(request) { headers: { 'content-type': 'text/html' }, }); } catch (error) { - return new Response('<h1>Something went wrong</h1>', { + return new Response('<h1>Đã xảy ra lỗi</h1>', { status: 500, headers: { 'content-type': 'text/html' }, }); @@ -476,9 +476,9 @@ async function handler(request) { } ``` -If a component *outside* the shell (i.e. inside a `<Suspense>` boundary) throws an error, React will not stop rendering. This means that the `onError` callback will fire, but your code will continue running without getting into the `catch` block. This is because React will try to recover from that error on the client, [as described above.](#recovering-from-errors-outside-the-shell) +Nếu một thành phần *bên ngoài* shell (tức là bên trong một ranh giới `<Suspense>`) ném một lỗi, React sẽ không ngừng hiển thị. Điều này có nghĩa là callback `onError` sẽ kích hoạt, nhưng mã của bạn sẽ tiếp tục chạy mà không đi vào khối `catch`. Điều này là do React sẽ cố gắng khôi phục từ lỗi đó trên máy khách, [như được mô tả ở trên.](#recovering-from-errors-outside-the-shell) -However, if you'd like, you can use the fact that something has errored to set the status code: +Tuy nhiên, nếu bạn muốn, bạn có thể sử dụng thực tế là một cái gì đó đã bị lỗi để đặt mã trạng thái: ```js {3,7,13} async function handler(request) { @@ -497,7 +497,7 @@ async function handler(request) { headers: { 'content-type': 'text/html' }, }); } catch (error) { - return new Response('<h1>Something went wrong</h1>', { + return new Response('<h1>Đã xảy ra lỗi</h1>', { status: 500, headers: { 'content-type': 'text/html' }, }); @@ -505,13 +505,13 @@ async function handler(request) { } ``` -This will only catch errors outside the shell that happened while generating the initial shell content, so it's not exhaustive. If knowing whether an error occurred for some content is critical, you can move it up into the shell. +Điều này sẽ chỉ bắt các lỗi bên ngoài shell đã xảy ra trong khi tạo nội dung shell ban đầu, vì vậy nó không đầy đủ. Nếu biết liệu một lỗi có xảy ra đối với một số nội dung là rất quan trọng, bạn có thể di chuyển nó lên shell. --- -### Handling different errors in different ways {/*handling-different-errors-in-different-ways*/} +### Xử lý các lỗi khác nhau theo những cách khác nhau {/*handling-different-errors-in-different-ways*/} -You can [create your own `Error` subclasses](https://javascript.info/custom-errors) and use the [`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) operator to check which error is thrown. For example, you can define a custom `NotFoundError` and throw it from your component. Then you can save the error in `onError` and do something different before returning the response depending on the error type: +Bạn có thể [tạo các lớp con `Error` của riêng bạn](https://javascript.info/custom-errors) và sử dụng toán tử [`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) để kiểm tra lỗi nào được ném. Ví dụ: bạn có thể xác định một `NotFoundError` tùy chỉnh và ném nó từ thành phần của bạn. Sau đó, bạn có thể lưu lỗi trong `onError` và làm điều gì đó khác trước khi trả về phản hồi tùy thuộc vào loại lỗi: ```js {2-3,5-15,22,28,33} async function handler(request) { @@ -545,7 +545,7 @@ async function handler(request) { headers: { 'content-type': 'text/html' }, }); } catch (error) { - return new Response('<h1>Something went wrong</h1>', { + return new Response('<h1>Đã xảy ra lỗi</h1>', { status: getStatusCode(), headers: { 'content-type': 'text/html' }, }); @@ -553,17 +553,17 @@ async function handler(request) { } ``` -Keep in mind that once you emit the shell and start streaming, you can't change the status code. +Hãy nhớ rằng khi bạn phát ra shell và bắt đầu phát trực tuyến, bạn không thể thay đổi mã trạng thái. --- -### Waiting for all content to load for crawlers and static generation {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/} +### Chờ tất cả nội dung tải cho trình thu thập thông tin và tạo tĩnh {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/} -Streaming offers a better user experience because the user can see the content as it becomes available. +Phát trực tuyến cung cấp một trải nghiệm người dùng tốt hơn vì người dùng có thể thấy nội dung khi nó có sẵn. -However, when a crawler visits your page, or if you're generating the pages at the build time, you might want to let all of the content load first and then produce the final HTML output instead of revealing it progressively. +Tuy nhiên, khi một trình thu thập thông tin truy cập trang của bạn, hoặc nếu bạn đang tạo các trang tại thời điểm xây dựng, bạn có thể muốn để tất cả nội dung tải trước và sau đó tạo ra đầu ra HTML cuối cùng thay vì hiển thị nó dần dần. -You can wait for all the content to load by awaiting the `stream.allReady` Promise: +Bạn có thể đợi tất cả nội dung tải bằng cách chờ Promise `stream.allReady`: ```js {12-15} async function handler(request) { @@ -577,7 +577,7 @@ async function handler(request) { logServerCrashReport(error); } }); - let isCrawler = // ... depends on your bot detection strategy ... + let isCrawler = // ... phụ thuộc vào chiến lược phát hiện bot của bạn ... if (isCrawler) { await stream.allReady; } @@ -586,7 +586,7 @@ async function handler(request) { headers: { 'content-type': 'text/html' }, }); } catch (error) { - return new Response('<h1>Something went wrong</h1>', { + return new Response('<h1>Đã xảy ra lỗi</h1>', { status: 500, headers: { 'content-type': 'text/html' }, }); @@ -594,13 +594,13 @@ async function handler(request) { } ``` -A regular visitor will get a stream of progressively loaded content. A crawler will receive the final HTML output after all the data loads. However, this also means that the crawler will have to wait for *all* data, some of which might be slow to load or error. Depending on your app, you could choose to send the shell to the crawlers too. +Một khách truy cập thông thường sẽ nhận được một luồng nội dung được tải dần dần. Một trình thu thập thông tin sẽ nhận được đầu ra HTML cuối cùng sau khi tất cả dữ liệu tải. Tuy nhiên, điều này cũng có nghĩa là trình thu thập thông tin sẽ phải đợi *tất cả* dữ liệu, một số trong đó có thể tải chậm hoặc bị lỗi. Tùy thuộc vào ứng dụng của bạn, bạn có thể chọn gửi shell cho trình thu thập thông tin. --- -### Aborting server rendering {/*aborting-server-rendering*/} +### Hủy bỏ quá trình hiển thị máy chủ {/*aborting-server-rendering*/} -You can force the server rendering to "give up" after a timeout: +Bạn có thể buộc quá trình hiển thị máy chủ "từ bỏ" sau một thời gian chờ: ```js {3,4-6,9} async function handler(request) { @@ -622,4 +622,4 @@ async function handler(request) { // ... ``` -React will flush the remaining loading fallbacks as HTML, and will attempt to render the rest on the client. +React sẽ xả các fallback tải còn lại dưới dạng HTML và sẽ cố gắng hiển thị phần còn lại trên máy khách. diff --git a/src/content/reference/react-dom/server/renderToStaticMarkup.md b/src/content/reference/react-dom/server/renderToStaticMarkup.md index 2b9178d55..66aea6168 100644 --- a/src/content/reference/react-dom/server/renderToStaticMarkup.md +++ b/src/content/reference/react-dom/server/renderToStaticMarkup.md @@ -4,7 +4,7 @@ title: renderToStaticMarkup <Intro> -`renderToStaticMarkup` renders a non-interactive React tree to an HTML string. +`renderToStaticMarkup` hiển thị một cây React không tương tác thành một chuỗi HTML. ```js const html = renderToStaticMarkup(reactNode, options?) @@ -16,11 +16,11 @@ const html = renderToStaticMarkup(reactNode, options?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `renderToStaticMarkup(reactNode, options?)` {/*rendertostaticmarkup*/} -On the server, call `renderToStaticMarkup` to render your app to HTML. +Trên máy chủ, gọi `renderToStaticMarkup` để hiển thị ứng dụng của bạn thành HTML. ```js import { renderToStaticMarkup } from 'react-dom/server'; @@ -28,52 +28,52 @@ import { renderToStaticMarkup } from 'react-dom/server'; const html = renderToStaticMarkup(<Page />); ``` -It will produce non-interactive HTML output of your React components. +Nó sẽ tạo ra đầu ra HTML không tương tác của các thành phần React của bạn. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reactNode`: A React node you want to render to HTML. For example, a JSX node like `<Page />`. -* **optional** `options`: An object for server render. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. +* `reactNode`: Một nút React mà bạn muốn hiển thị thành HTML. Ví dụ: một nút JSX như `<Page />`. +* **tùy chọn** `options`: Một đối tượng cho quá trình hiển thị trên máy chủ. + * **tùy chọn** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều gốc trên cùng một trang. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -An HTML string. +Một chuỗi HTML. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `renderToStaticMarkup` output cannot be hydrated. +* Đầu ra của `renderToStaticMarkup` không thể hydrate. -* `renderToStaticMarkup` has limited Suspense support. If a component suspends, `renderToStaticMarkup` immediately sends its fallback as HTML. +* `renderToStaticMarkup` có hỗ trợ Suspense hạn chế. Nếu một thành phần tạm ngưng, `renderToStaticMarkup` sẽ gửi ngay lập tức fallback của nó dưới dạng HTML. -* `renderToStaticMarkup` works in the browser, but using it in the client code is not recommended. If you need to render a component to HTML in the browser, [get the HTML by rendering it into a DOM node.](/reference/react-dom/server/renderToString#removing-rendertostring-from-the-client-code) +* `renderToStaticMarkup` hoạt động trong trình duyệt, nhưng không nên sử dụng nó trong mã phía máy khách. Nếu bạn cần hiển thị một thành phần thành HTML trong trình duyệt, [lấy HTML bằng cách hiển thị nó vào một nút DOM.](/reference/react-dom/server/renderToString#removing-rendertostring-from-the-client-code) --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering a non-interactive React tree as HTML to a string {/*rendering-a-non-interactive-react-tree-as-html-to-a-string*/} +### Hiển thị một cây React không tương tác thành HTML thành một chuỗi {/*rendering-a-non-interactive-react-tree-as-html-to-a-string*/} -Call `renderToStaticMarkup` to render your app to an HTML string which you can send with your server response: +Gọi `renderToStaticMarkup` để hiển thị ứng dụng của bạn thành một chuỗi HTML mà bạn có thể gửi cùng với phản hồi của máy chủ: ```js {5-6} import { renderToStaticMarkup } from 'react-dom/server'; -// The route handler syntax depends on your backend framework +// Cú pháp trình xử lý tuyến đường phụ thuộc vào framework backend của bạn app.use('/', (request, response) => { const html = renderToStaticMarkup(<Page />); response.send(html); }); ``` -This will produce the initial non-interactive HTML output of your React components. +Điều này sẽ tạo ra đầu ra HTML không tương tác ban đầu của các thành phần React của bạn. <Pitfall> -This method renders **non-interactive HTML that cannot be hydrated.** This is useful if you want to use React as a simple static page generator, or if you're rendering completely static content like emails. +Phương pháp này hiển thị **HTML không tương tác và không thể hydrate.** Điều này hữu ích nếu bạn muốn sử dụng React như một trình tạo trang tĩnh đơn giản hoặc nếu bạn đang hiển thị nội dung hoàn toàn tĩnh như email. -Interactive apps should use [`renderToString`](/reference/react-dom/server/renderToString) on the server and [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) on the client. +Các ứng dụng tương tác nên sử dụng [`renderToString`](/reference/react-dom/server/renderToString) trên máy chủ và [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) trên máy khách. </Pitfall> diff --git a/src/content/reference/react-dom/server/renderToString.md b/src/content/reference/react-dom/server/renderToString.md index b89c65c0c..f70e7a4f8 100644 --- a/src/content/reference/react-dom/server/renderToString.md +++ b/src/content/reference/react-dom/server/renderToString.md @@ -4,13 +4,13 @@ title: renderToString <Pitfall> -`renderToString` does not support streaming or waiting for data. [See the alternatives.](#alternatives) +`renderToString` không hỗ trợ truyền trực tuyến hoặc chờ dữ liệu. [Xem các lựa chọn thay thế.](#alternatives) </Pitfall> <Intro> -`renderToString` renders a React tree to an HTML string. +`renderToString` hiển thị một cây React thành một chuỗi HTML. ```js const html = renderToString(reactNode, options?) @@ -22,11 +22,11 @@ const html = renderToString(reactNode, options?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `renderToString(reactNode, options?)` {/*rendertostring*/} -On the server, call `renderToString` to render your app to HTML. +Trên máy chủ, gọi `renderToString` để hiển thị ứng dụng của bạn thành HTML. ```js import { renderToString } from 'react-dom/server'; @@ -34,97 +34,96 @@ import { renderToString } from 'react-dom/server'; const html = renderToString(<App />); ``` -On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive. +Trên máy khách, gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để làm cho HTML được tạo từ máy chủ trở nên tương tác. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reactNode`: A React node you want to render to HTML. For example, a JSX node like `<App />`. +* `reactNode`: Một nút React mà bạn muốn hiển thị thành HTML. Ví dụ: một nút JSX như `<App />`. -* **optional** `options`: An object for server render. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) +* **tùy chọn** `options`: Một đối tượng cho hiển thị phía máy chủ. + * **tùy chọn** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều gốc trên cùng một trang. Phải là cùng một tiền tố như đã truyền cho [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -An HTML string. +Một chuỗi HTML. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `renderToString` has limited Suspense support. If a component suspends, `renderToString` immediately sends its fallback as HTML. +* `renderToString` có hỗ trợ Suspense hạn chế. Nếu một thành phần tạm dừng, `renderToString` sẽ gửi ngay lập tức phần dự phòng của nó dưới dạng HTML. -* `renderToString` works in the browser, but using it in the client code is [not recommended.](#removing-rendertostring-from-the-client-code) +* `renderToString` hoạt động trong trình duyệt, nhưng việc sử dụng nó trong mã phía máy khách là [không được khuyến nghị.](#removing-rendertostring-from-the-client-code) --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering a React tree as HTML to a string {/*rendering-a-react-tree-as-html-to-a-string*/} +### Hiển thị một cây React thành HTML thành một chuỗi {/*rendering-a-react-tree-as-html-to-a-string*/} -Call `renderToString` to render your app to an HTML string which you can send with your server response: +Gọi `renderToString` để hiển thị ứng dụng của bạn thành một chuỗi HTML mà bạn có thể gửi cùng với phản hồi của máy chủ: ```js {5-6} import { renderToString } from 'react-dom/server'; -// The route handler syntax depends on your backend framework +// Cú pháp trình xử lý tuyến đường phụ thuộc vào khung máy chủ của bạn app.use('/', (request, response) => { const html = renderToString(<App />); response.send(html); }); ``` -This will produce the initial non-interactive HTML output of your React components. On the client, you will need to call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to *hydrate* that server-generated HTML and make it interactive. - +Điều này sẽ tạo ra đầu ra HTML không tương tác ban đầu của các thành phần React của bạn. Trên máy khách, bạn sẽ cần gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để *hydrate* HTML được tạo từ máy chủ đó và làm cho nó có tính tương tác. <Pitfall> -`renderToString` does not support streaming or waiting for data. [See the alternatives.](#alternatives) +`renderToString` không hỗ trợ truyền trực tuyến hoặc chờ dữ liệu. [Xem các lựa chọn thay thế.](#alternatives) </Pitfall> --- -## Alternatives {/*alternatives*/} +## Các lựa chọn thay thế {/*alternatives*/} -### Migrating from `renderToString` to a streaming render on the server {/*migrating-from-rendertostring-to-a-streaming-method-on-the-server*/} +### Di chuyển từ `renderToString` sang hiển thị phát trực tuyến trên máy chủ {/*migrating-from-rendertostring-to-a-streaming-method-on-the-server*/} -`renderToString` returns a string immediately, so it does not support streaming content as it loads. +`renderToString` trả về một chuỗi ngay lập tức, vì vậy nó không hỗ trợ truyền trực tuyến nội dung khi nó tải. -When possible, we recommend using these fully-featured alternatives: +Khi có thể, chúng tôi khuyên bạn nên sử dụng các giải pháp thay thế đầy đủ tính năng này: -* If you use Node.js, use [`renderToPipeableStream`.](/reference/react-dom/server/renderToPipeableStream) -* If you use Deno or a modern edge runtime with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), use [`renderToReadableStream`.](/reference/react-dom/server/renderToReadableStream) +* Nếu bạn sử dụng Node.js, hãy sử dụng [`renderToPipeableStream`.](/reference/react-dom/server/renderToPipeableStream) +* Nếu bạn sử dụng Deno hoặc một thời gian chạy biên hiện đại với [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), hãy sử dụng [`renderToReadableStream`.](/reference/react-dom/server/renderToReadableStream) -You can continue using `renderToString` if your server environment does not support streams. +Bạn có thể tiếp tục sử dụng `renderToString` nếu môi trường máy chủ của bạn không hỗ trợ luồng. --- -### Migrating from `renderToString` to a static prerender on the server {/*migrating-from-rendertostring-to-a-static-prerender-on-the-server*/} +### Di chuyển từ `renderToString` sang kết xuất trước tĩnh trên máy chủ {/*migrating-from-rendertostring-to-a-static-prerender-on-the-server*/} -`renderToString` returns a string immediately, so it does not support waiting for data to load for static HTML generation. +`renderToString` trả về một chuỗi ngay lập tức, vì vậy nó không hỗ trợ chờ dữ liệu tải để tạo HTML tĩnh. -We recommend using these fully-featured alternatives: +Chúng tôi khuyên bạn nên sử dụng các giải pháp thay thế đầy đủ tính năng này: -* If you use Node.js, use [`prerenderToNodeStream`.](/reference/react-dom/static/prerenderToNodeStream) -* If you use Deno or a modern edge runtime with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), use [`prerender`.](/reference/react-dom/static/prerender) +* Nếu bạn sử dụng Node.js, hãy sử dụng [`prerenderToNodeStream`.](/reference/react-dom/static/prerenderToNodeStream) +* Nếu bạn sử dụng Deno hoặc một thời gian chạy biên hiện đại với [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), hãy sử dụng [`prerender`.](/reference/react-dom/static/prerender) -You can continue using `renderToString` if your static site generation environment does not support streams. +Bạn có thể tiếp tục sử dụng `renderToString` nếu môi trường tạo trang web tĩnh của bạn không hỗ trợ luồng. --- -### Removing `renderToString` from the client code {/*removing-rendertostring-from-the-client-code*/} +### Loại bỏ `renderToString` khỏi mã phía máy khách {/*removing-rendertostring-from-the-client-code*/} -Sometimes, `renderToString` is used on the client to convert some component to HTML. +Đôi khi, `renderToString` được sử dụng trên máy khách để chuyển đổi một số thành phần sang HTML. ```js {1-2} -// 🚩 Unnecessary: using renderToString on the client +// 🚩 Không cần thiết: sử dụng renderToString trên máy khách import { renderToString } from 'react-dom/server'; const html = renderToString(<MyIcon />); -console.log(html); // For example, "<svg>...</svg>" +console.log(html); // Ví dụ: "<svg>...</svg>" ``` -Importing `react-dom/server` **on the client** unnecessarily increases your bundle size and should be avoided. If you need to render some component to HTML in the browser, use [`createRoot`](/reference/react-dom/client/createRoot) and read HTML from the DOM: +Nhập `react-dom/server` **trên máy khách** làm tăng kích thước gói của bạn một cách không cần thiết và nên tránh. Nếu bạn cần hiển thị một số thành phần sang HTML trong trình duyệt, hãy sử dụng [`createRoot`](/reference/react-dom/client/createRoot) và đọc HTML từ DOM: ```js import { createRoot } from 'react-dom/client'; @@ -135,20 +134,19 @@ const root = createRoot(div); flushSync(() => { root.render(<MyIcon />); }); -console.log(div.innerHTML); // For example, "<svg>...</svg>" +console.log(div.innerHTML); // Ví dụ: "<svg>...</svg>" ``` -The [`flushSync`](/reference/react-dom/flushSync) call is necessary so that the DOM is updated before reading its [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) property. +Lệnh gọi [`flushSync`](/reference/react-dom/flushSync) là cần thiết để DOM được cập nhật trước khi đọc thuộc tính [`innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML) của nó. --- -## Troubleshooting {/*troubleshooting*/} - -### When a component suspends, the HTML always contains a fallback {/*when-a-component-suspends-the-html-always-contains-a-fallback*/} +## Khắc phục sự cố {/*troubleshooting*/} -`renderToString` does not fully support Suspense. +### Khi một thành phần tạm dừng, HTML luôn chứa một dự phòng {/*when-a-component-suspends-the-html-always-contains-a-fallback*/} -If some component suspends (for example, because it's defined with [`lazy`](/reference/react/lazy) or fetches data), `renderToString` will not wait for its content to resolve. Instead, `renderToString` will find the closest [`<Suspense>`](/reference/react/Suspense) boundary above it and render its `fallback` prop in the HTML. The content will not appear until the client code loads. +`renderToString` không hỗ trợ đầy đủ Suspense. -To solve this, use one of the [recommended streaming solutions.](#alternatives) For server side rendering, they can stream content in chunks as it resolves on the server so that the user sees the page being progressively filled in before the client code loads. For static site generation, they can wait for all the content to resolve before generating the static HTML. +Nếu một số thành phần tạm dừng (ví dụ: vì nó được xác định bằng [`lazy`](/reference/react/lazy) hoặc tìm nạp dữ liệu), `renderToString` sẽ không đợi nội dung của nó được giải quyết. Thay vào đó, `renderToString` sẽ tìm ranh giới [`<Suspense>`](/reference/react/Suspense) gần nhất ở trên nó và hiển thị thuộc tính `fallback` của nó trong HTML. Nội dung sẽ không xuất hiện cho đến khi mã phía máy khách tải. +Để giải quyết vấn đề này, hãy sử dụng một trong các [giải pháp phát trực tuyến được đề xuất.](#alternatives) Đối với kết xuất phía máy chủ, chúng có thể truyền trực tuyến nội dung theo các đoạn khi nó được giải quyết trên máy chủ để người dùng thấy trang được điền dần dần trước khi mã phía máy khách tải. Đối với tạo trang web tĩnh, chúng có thể đợi tất cả nội dung được giải quyết trước khi tạo HTML tĩnh. diff --git a/src/content/reference/react-dom/static/index.md b/src/content/reference/react-dom/static/index.md index 988ec85dc..1f143ad09 100644 --- a/src/content/reference/react-dom/static/index.md +++ b/src/content/reference/react-dom/static/index.md @@ -1,28 +1,26 @@ --- -title: Static React DOM APIs +title: Các API React DOM Tĩnh --- <Intro> -The `react-dom/static` APIs let you generate static HTML for React components. They have limited functionality compared to the streaming APIs. A [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) may call them for you. Most of your components don't need to import or use them. +Các API `react-dom/static` cho phép bạn tạo HTML tĩnh cho các thành phần React. Chúng có chức năng giới hạn so với các API streaming. Một [framework](/learn/start-a-new-react-project#production-grade-react-frameworks) có thể gọi chúng cho bạn. Hầu hết các thành phần của bạn không cần nhập hoặc sử dụng chúng. </Intro> --- -## Static APIs for Web Streams {/*static-apis-for-web-streams*/} +## Các API Tĩnh cho Web Streams {/*static-apis-for-web-streams*/} -These methods are only available in the environments with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), which includes browsers, Deno, and some modern edge runtimes: +Các phương thức này chỉ khả dụng trong các môi trường có [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), bao gồm trình duyệt, Deno và một số runtime edge hiện đại: -* [`prerender`](/reference/react-dom/static/prerender) renders a React tree to static HTML with a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +* [`prerender`](/reference/react-dom/static/prerender) hiển thị một cây React thành HTML tĩnh với một [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) --- -## Static APIs for Node.js Streams {/*static-apis-for-nodejs-streams*/} - -These methods are only available in the environments with [Node.js Streams](https://nodejs.org/api/stream.html): - -* [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) renders a React tree to static HTML with a [Node.js Stream.](https://nodejs.org/api/stream.html) +## Các API Tĩnh cho Node.js Streams {/*static-apis-for-nodejs-streams*/} +Các phương thức này chỉ khả dụng trong các môi trường có [Node.js Streams](https://nodejs.org/api/stream.html): +* [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) hiển thị một cây React thành HTML tĩnh với một [Node.js Stream.](https://nodejs.org/api/stream.html) diff --git a/src/content/reference/react-dom/static/prerender.md b/src/content/reference/react-dom/static/prerender.md index f1ef38b44..827d135db 100644 --- a/src/content/reference/react-dom/static/prerender.md +++ b/src/content/reference/react-dom/static/prerender.md @@ -4,7 +4,7 @@ title: prerender <Intro> -`prerender` renders a React tree to a static HTML string using a [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API). +`prerender` kết xuất một cây React thành một chuỗi HTML tĩnh bằng cách sử dụng [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API). ```js const {prelude} = await prerender(reactNode, options?) @@ -16,17 +16,17 @@ const {prelude} = await prerender(reactNode, options?) <Note> -This API depends on [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) For Node.js, use [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) instead. +API này phụ thuộc vào [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) Đối với Node.js, hãy sử dụng [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) thay thế. </Note> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `prerender(reactNode, options?)` {/*prerender*/} -Call `prerender` to render your app to static HTML. +Gọi `prerender` để kết xuất ứng dụng của bạn thành HTML tĩnh. ```js import { prerender } from 'react-dom/static'; @@ -41,49 +41,46 @@ async function handler(request) { } ``` -On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive. +Trên máy khách, hãy gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để làm cho HTML do máy chủ tạo ra trở nên tương tác. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reactNode`: A React node you want to render to HTML. For example, a JSX node like `<App />`. It is expected to represent the entire document, so the App component should render the `<html>` tag. - -* **optional** `options`: An object with static generation options. - * **optional** `bootstrapScriptContent`: If specified, this string will be placed in an inline `<script>` tag. - * **optional** `bootstrapScripts`: An array of string URLs for the `<script>` tags to emit on the page. Use this to include the `<script>` that calls [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Omit it if you don't want to run React on the client at all. - * **optional** `bootstrapModules`: Like `bootstrapScripts`, but emits [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) instead. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) - * **optional** `namespaceURI`: A string with the root [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) for the stream. Defaults to regular HTML. Pass `'http://www.w3.org/2000/svg'` for SVG or `'http://www.w3.org/1998/Math/MathML'` for MathML. - * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell) or [not.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server) make sure that you still call `console.error`. You can also use it to [adjust the status code](/reference/react-dom/server/renderToReadableStream#setting-the-status-code) before the shell is emitted. - * **optional** `progressiveChunkSize`: The number of bytes in a chunk. [Read more about the default heuristic.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) - * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](/reference/react-dom/server/renderToReadableStream#aborting-server-rendering) and render the rest on the client. - -#### Returns {/*returns*/} - -`prerender` returns a Promise: -- If rendering the is successful, the Promise will resolve to an object containing: - - `prelude`: a [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) of HTML. You can use this stream to send a response in chunks, or you can read the entire stream into a string. -- If rendering fails, the Promise will be rejected. [Use this to output a fallback shell.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) +* `reactNode`: Một nút React mà bạn muốn kết xuất thành HTML. Ví dụ: một nút JSX như `<App />`. Nó được mong đợi đại diện cho toàn bộ tài liệu, vì vậy thành phần App sẽ kết xuất thẻ `<html>`. +* **tùy chọn** `options`: Một đối tượng với các tùy chọn tạo tĩnh. + * **tùy chọn** `bootstrapScriptContent`: Nếu được chỉ định, chuỗi này sẽ được đặt trong một thẻ `<script>` nội tuyến. + * **tùy chọn** `bootstrapScripts`: Một mảng các URL chuỗi cho các thẻ `<script>` để phát ra trên trang. Sử dụng cái này để bao gồm `<script>` gọi [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Bỏ qua nó nếu bạn không muốn chạy React trên máy khách. + * **tùy chọn** `bootstrapModules`: Giống như `bootstrapScripts`, nhưng phát ra [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) thay thế. + * **tùy chọn** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều gốc trên cùng một trang. Phải là tiền tố giống như đã truyền cho [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) + * **tùy chọn** `namespaceURI`: Một chuỗi với [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) gốc cho luồng. Mặc định là HTML thông thường. Truyền `'http://www.w3.org/2000/svg'` cho SVG hoặc `'http://www.w3.org/1998/Math/MathML'` cho MathML. + * **tùy chọn** `onError`: Một lệnh gọi lại kích hoạt bất cứ khi nào có lỗi máy chủ, cho dù [có thể khôi phục được](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-outside-the-shell) hay [không.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) Theo mặc định, điều này chỉ gọi `console.error`. Nếu bạn ghi đè nó để [ghi nhật ký báo cáo sự cố,](/reference/react-dom/server/renderToReadableStream#logging-crashes-on-the-server) hãy đảm bảo rằng bạn vẫn gọi `console.error`. Bạn cũng có thể sử dụng nó để [điều chỉnh mã trạng thái](/reference/react-dom/server/renderToReadableStream#setting-the-status-code) trước khi shell được phát ra. + * **tùy chọn** `progressiveChunkSize`: Số byte trong một đoạn. [Đọc thêm về heuristic mặc định.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) + * **tùy chọn** `signal`: Một [tín hiệu hủy bỏ](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) cho phép bạn [hủy bỏ kết xuất máy chủ](/reference/react-dom/server/renderToReadableStream#aborting-server-rendering) và kết xuất phần còn lại trên máy khách. +#### Trả về {/*returns*/} +`prerender` trả về một Promise: +- Nếu kết xuất thành công, Promise sẽ phân giải thành một đối tượng chứa: + - `prelude`: một [Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) của HTML. Bạn có thể sử dụng luồng này để gửi phản hồi theo các đoạn hoặc bạn có thể đọc toàn bộ luồng vào một chuỗi. +- Nếu kết xuất không thành công, Promise sẽ bị từ chối. [Sử dụng cái này để xuất một shell dự phòng.](/reference/react-dom/server/renderToReadableStream#recovering-from-errors-inside-the-shell) <Note> -### When should I use `prerender`? {/*when-to-use-prerender*/} +### Khi nào tôi nên sử dụng `prerender`? {/*when-to-use-prerender*/} -The static `prerender` API is used for static server-side generation (SSG). Unlike `renderToString`, `prerender` waits for all data to load before resolving. This makes it suitable for generating static HTML for a full page, including data that needs to be fetched using Suspense. To stream content as it loads, use a streaming server-side render (SSR) API like [renderToReadableStream](/reference/react-dom/server/renderToReadableStream). +API `prerender` tĩnh được sử dụng để tạo phía máy chủ tĩnh (SSG). Không giống như `renderToString`, `prerender` đợi tất cả dữ liệu được tải trước khi phân giải. Điều này làm cho nó phù hợp để tạo HTML tĩnh cho toàn bộ trang, bao gồm cả dữ liệu cần được tìm nạp bằng Suspense. Để truyền trực tuyến nội dung khi nó tải, hãy sử dụng API kết xuất phía máy chủ (SSR) phát trực tuyến như [renderToReadableStream](/reference/react-dom/server/renderToReadableStream). </Note> --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering a React tree to a stream of static HTML {/*rendering-a-react-tree-to-a-stream-of-static-html*/} +### Kết xuất một cây React thành một luồng HTML tĩnh {/*rendering-a-react-tree-to-a-stream-of-static-html*/} -Call `prerender` to render your React tree to static HTML into a [Readable Web Stream:](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream): +Gọi `prerender` để kết xuất cây React của bạn thành HTML tĩnh vào một [Readable Web Stream:](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream): ```js [[1, 4, "<App />"], [2, 5, "['/main.js']"]] import { prerender } from 'react-dom/static'; @@ -98,9 +95,9 @@ async function handler(request) { } ``` -Along with the <CodeStep step={1}>root component</CodeStep>, you need to provide a list of <CodeStep step={2}>bootstrap `<script>` paths</CodeStep>. Your root component should return **the entire document including the root `<html>` tag.** +Cùng với <CodeStep step={1}>thành phần gốc</CodeStep>, bạn cần cung cấp một danh sách <CodeStep step={2}>đường dẫn `<script>` bootstrap</CodeStep>. Thành phần gốc của bạn sẽ trả về **toàn bộ tài liệu bao gồm thẻ `<html>` gốc.** -For example, it might look like this: +Ví dụ: nó có thể trông như thế này: ```js [[1, 1, "App"]] export default function App() { @@ -110,7 +107,7 @@ export default function App() { <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="/styles.css"></link> - <title>My app</title> + <title>Ứng dụng của tôi</title> </head> <body> <Router /> @@ -120,17 +117,17 @@ export default function App() { } ``` -React will inject the [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) and your <CodeStep step={2}>bootstrap `<script>` tags</CodeStep> into the resulting HTML stream: +React sẽ chèn [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) và <CodeStep step={2}>thẻ `<script>` bootstrap</CodeStep> của bạn vào luồng HTML kết quả: ```html [[2, 5, "/main.js"]] <!DOCTYPE html> <html> - <!-- ... HTML from your components ... --> + <!-- ... HTML từ các thành phần của bạn ... --> </html> <script src="/main.js" async=""></script> ``` -On the client, your bootstrap script should [hydrate the entire `document` with a call to `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) +Trên máy khách, tập lệnh bootstrap của bạn sẽ [hydrate toàn bộ `document` bằng một lệnh gọi đến `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) ```js [[1, 4, "<App />"]] import { hydrateRoot } from 'react-dom/client'; @@ -139,22 +136,22 @@ import App from './App.js'; hydrateRoot(document, <App />); ``` -This will attach event listeners to the static server-generated HTML and make it interactive. +Điều này sẽ đính kèm trình nghe sự kiện vào HTML tĩnh do máy chủ tạo ra và làm cho nó có tính tương tác. <DeepDive> -#### Reading CSS and JS asset paths from the build output {/*reading-css-and-js-asset-paths-from-the-build-output*/} +#### Đọc đường dẫn tài sản CSS và JS từ đầu ra bản dựng {/*reading-css-and-js-asset-paths-from-the-build-output*/} -The final asset URLs (like JavaScript and CSS files) are often hashed after the build. For example, instead of `styles.css` you might end up with `styles.123456.css`. Hashing static asset filenames guarantees that every distinct build of the same asset will have a different filename. This is useful because it lets you safely enable long-term caching for static assets: a file with a certain name would never change content. +Các URL tài sản cuối cùng (như tệp JavaScript và CSS) thường được băm sau khi xây dựng. Ví dụ: thay vì `styles.css`, bạn có thể kết thúc bằng `styles.123456.css`. Băm tên tệp tài sản tĩnh đảm bảo rằng mọi bản dựng riêng biệt của cùng một tài sản sẽ có một tên tệp khác nhau. Điều này rất hữu ích vì nó cho phép bạn bật bộ nhớ đệm dài hạn một cách an toàn cho các tài sản tĩnh: một tệp có tên nhất định sẽ không bao giờ thay đổi nội dung. -However, if you don't know the asset URLs until after the build, there's no way for you to put them in the source code. For example, hardcoding `"/styles.css"` into JSX like earlier wouldn't work. To keep them out of your source code, your root component can read the real filenames from a map passed as a prop: +Tuy nhiên, nếu bạn không biết URL tài sản cho đến sau khi xây dựng, bạn không có cách nào để đưa chúng vào mã nguồn. Ví dụ: mã hóa cứng `"/styles.css"` vào JSX như trước đây sẽ không hoạt động. Để giữ chúng bên ngoài mã nguồn của bạn, thành phần gốc của bạn có thể đọc tên tệp thực từ một bản đồ được truyền dưới dạng một prop: ```js {1,6} export default function App({ assetMap }) { return ( <html> <head> - <title>My app</title> + <title>Ứng dụng của tôi</title> <link rel="stylesheet" href={assetMap['styles.css']}></link> </head> ... @@ -163,10 +160,10 @@ export default function App({ assetMap }) { } ``` -On the server, render `<App assetMap={assetMap} />` and pass your `assetMap` with the asset URLs: +Trên máy chủ, kết xuất `<App assetMap={assetMap} />` và truyền `assetMap` của bạn với các URL tài sản: ```js {1-5,8,9} -// You'd need to get this JSON from your build tooling, e.g. read it from the build output. +// Bạn sẽ cần lấy JSON này từ công cụ xây dựng của bạn, ví dụ: đọc nó từ đầu ra bản dựng. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -182,10 +179,10 @@ async function handler(request) { } ``` -Since your server is now rendering `<App assetMap={assetMap} />`, you need to render it with `assetMap` on the client too to avoid hydration errors. You can serialize and pass `assetMap` to the client like this: +Vì máy chủ của bạn hiện đang kết xuất `<App assetMap={assetMap} />`, bạn cần kết xuất nó với `assetMap` trên máy khách để tránh lỗi hydration. Bạn có thể tuần tự hóa và truyền `assetMap` cho máy khách như thế này: ```js {9-10} -// You'd need to get this JSON from your build tooling. +// Bạn sẽ cần lấy JSON này từ công cụ xây dựng của bạn. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -193,7 +190,7 @@ const assetMap = { async function handler(request) { const {prelude} = await prerender(<App assetMap={assetMap} />, { - // Careful: It's safe to stringify() this because this data isn't user-generated. + // Cẩn thận: An toàn để stringify() dữ liệu này vì dữ liệu này không phải do người dùng tạo. bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, bootstrapScripts: [assetMap['/main.js']], }); @@ -203,7 +200,7 @@ async function handler(request) { } ``` -In the example above, the `bootstrapScriptContent` option adds an extra inline `<script>` tag that sets the global `window.assetMap` variable on the client. This lets the client code read the same `assetMap`: +Trong ví dụ trên, tùy chọn `bootstrapScriptContent` thêm một thẻ `<script>` nội tuyến bổ sung đặt biến `window.assetMap` toàn cục trên máy khách. Điều này cho phép mã máy khách đọc cùng một `assetMap`: ```js {4} import { hydrateRoot } from 'react-dom/client'; @@ -212,15 +209,15 @@ import App from './App.js'; hydrateRoot(document, <App assetMap={window.assetMap} />); ``` -Both client and server render `App` with the same `assetMap` prop, so there are no hydration errors. +Cả máy khách và máy chủ đều kết xuất `App` với cùng một prop `assetMap`, vì vậy không có lỗi hydration. </DeepDive> --- -### Rendering a React tree to a string of static HTML {/*rendering-a-react-tree-to-a-string-of-static-html*/} +### Kết xuất một cây React thành một chuỗi HTML tĩnh {/*rendering-a-react-tree-to-a-string-of-static-html*/} -Call `prerender` to render your app to a static HTML string: +Gọi `prerender` để kết xuất ứng dụng của bạn thành một chuỗi HTML tĩnh: ```js import { prerender } from 'react-dom/static'; @@ -242,13 +239,13 @@ async function renderToString() { } ``` -This will produce the initial non-interactive HTML output of your React components. On the client, you will need to call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to *hydrate* that server-generated HTML and make it interactive. +Điều này sẽ tạo ra đầu ra HTML không tương tác ban đầu của các thành phần React của bạn. Trên máy khách, bạn sẽ cần gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để *hydrate* HTML do máy chủ tạo ra đó và làm cho nó có tính tương tác. --- -### Waiting for all data to load {/*waiting-for-all-data-to-load*/} +### Chờ tất cả dữ liệu được tải {/*waiting-for-all-data-to-load*/} -`prerender` waits for all data to load before finishing the static HTML generation and resolving. For example, consider a profile page that shows a cover, a sidebar with friends and photos, and a list of posts: +`prerender` đợi tất cả dữ liệu được tải trước khi hoàn thành việc tạo HTML tĩnh và phân giải. Ví dụ: hãy xem xét một trang hồ sơ hiển thị ảnh bìa, thanh bên với bạn bè và ảnh và danh sách các bài đăng: ```js function ProfilePage() { @@ -267,31 +264,30 @@ function ProfilePage() { } ``` -Imagine that `<Posts />` needs to load some data, which takes some time. Ideally, you'd want wait for the posts to finish so it's included in the HTML. To do this, you can use Suspense to suspend on the data, and `prerender` will wait for the suspended content to finish before resolving to the static HTML. +Hãy tưởng tượng rằng `<Posts />` cần tải một số dữ liệu, điều này mất một chút thời gian. Lý tưởng nhất là bạn muốn đợi các bài đăng hoàn thành để nó được đưa vào HTML. Để thực hiện việc này, bạn có thể sử dụng Suspense để tạm dừng dữ liệu và `prerender` sẽ đợi nội dung bị tạm dừng hoàn thành trước khi phân giải thành HTML tĩnh. <Note> -**Only Suspense-enabled data sources will activate the Suspense component.** They include: +**Chỉ các nguồn dữ liệu hỗ trợ Suspense mới kích hoạt thành phần Suspense.** Chúng bao gồm: -- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) -- Lazy-loading component code with [`lazy`](/reference/react/lazy) -- Reading the value of a Promise with [`use`](/reference/react/use) +- Tìm nạp dữ liệu với các framework hỗ trợ Suspense như [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) và [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Tải mã thành phần lười biếng với [`lazy`](/reference/react/lazy) +- Đọc giá trị của một Promise với [`use`](/reference/react/use) -Suspense **does not** detect when data is fetched inside an Effect or event handler. +Suspense **không** phát hiện khi dữ liệu được tìm nạp bên trong một Effect hoặc trình xử lý sự kiện. -The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. +Cách chính xác bạn sẽ tải dữ liệu trong thành phần `Posts` ở trên phụ thuộc vào framework của bạn. Nếu bạn sử dụng một framework hỗ trợ Suspense, bạn sẽ tìm thấy các chi tiết trong tài liệu tìm nạp dữ liệu của nó. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Tìm nạp dữ liệu hỗ trợ Suspense mà không sử dụng framework có ý kiến vẫn chưa được hỗ trợ. Các yêu cầu để triển khai một nguồn dữ liệu hỗ trợ Suspense là không ổn định và không được ghi lại. Một API chính thức để tích hợp các nguồn dữ liệu với Suspense sẽ được phát hành trong một phiên bản React trong tương lai. </Note> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My stream doesn't start until the entire app is rendered {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/} +### Luồng của tôi không bắt đầu cho đến khi toàn bộ ứng dụng được kết xuất {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/} -The `prerender` response waits for the entire app to finish rendering, including waiting for all Suspense boundaries to resolve, before resolving. It is designed for static site generation (SSG) ahead of time and does not support streaming more content as it loads. +Phản hồi `prerender` đợi cho đến khi toàn bộ ứng dụng kết thúc kết xuất, bao gồm cả việc chờ tất cả các ranh giới Suspense được giải quyết, trước khi giải quyết. Nó được thiết kế để tạo trang web tĩnh (SSG) trước thời hạn và không hỗ trợ truyền trực tuyến thêm nội dung khi nó tải. -To stream content as it loads, use a streaming server render API like [renderToReadableStream](/reference/react-dom/server/renderToReadableStream). - \ No newline at end of file +Để truyền trực tuyến nội dung khi nó tải, hãy sử dụng API kết xuất máy chủ phát trực tuyến như [renderToReadableStream](/reference/react-dom/server/renderToReadableStream). diff --git a/src/content/reference/react-dom/static/prerenderToNodeStream.md b/src/content/reference/react-dom/static/prerenderToNodeStream.md index b5bb60eaf..84751417f 100644 --- a/src/content/reference/react-dom/static/prerenderToNodeStream.md +++ b/src/content/reference/react-dom/static/prerenderToNodeStream.md @@ -4,7 +4,7 @@ title: prerenderToNodeStream <Intro> -`prerenderToNodeStream` renders a React tree to a static HTML string using a [Node.js Stream.](https://nodejs.org/api/stream.html). +`prerenderToNodeStream` kết xuất một cây React thành một chuỗi HTML tĩnh bằng cách sử dụng [Node.js Stream.](https://nodejs.org/api/stream.html). ```js const {prelude} = await prerenderToNodeStream(reactNode, options?) @@ -16,22 +16,22 @@ const {prelude} = await prerenderToNodeStream(reactNode, options?) <Note> -This API is specific to Node.js. Environments with [Web Streams,](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) like Deno and modern edge runtimes, should use [`prerender`](/reference/react-dom/static/prerender) instead. +API này dành riêng cho Node.js. Các môi trường có [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), như Deno và các runtime edge hiện đại, nên sử dụng [`prerender`](/reference/react-dom/static/prerender) thay thế. </Note> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `prerenderToNodeStream(reactNode, options?)` {/*prerender*/} -Call `prerenderToNodeStream` to render your app to static HTML. +Gọi `prerenderToNodeStream` để kết xuất ứng dụng của bạn thành HTML tĩnh. ```js import { prerenderToNodeStream } from 'react-dom/static'; -// The route handler syntax depends on your backend framework +// Cú pháp trình xử lý route phụ thuộc vào framework backend của bạn app.use('/', async (request, response) => { const { prelude } = await prerenderToNodeStream(<App />, { bootstrapScripts: ['/main.js'], @@ -42,51 +42,51 @@ app.use('/', async (request, response) => { }); ``` -On the client, call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to make the server-generated HTML interactive. +Trên client, gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để làm cho HTML được tạo từ server trở nên tương tác. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reactNode`: A React node you want to render to HTML. For example, a JSX node like `<App />`. It is expected to represent the entire document, so the App component should render the `<html>` tag. +* `reactNode`: Một node React mà bạn muốn kết xuất thành HTML. Ví dụ: một node JSX như `<App />`. Nó được mong đợi đại diện cho toàn bộ tài liệu, vì vậy component App sẽ kết xuất thẻ `<html>`. -* **optional** `options`: An object with static generation options. - * **optional** `bootstrapScriptContent`: If specified, this string will be placed in an inline `<script>` tag. - * **optional** `bootstrapScripts`: An array of string URLs for the `<script>` tags to emit on the page. Use this to include the `<script>` that calls [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Omit it if you don't want to run React on the client at all. - * **optional** `bootstrapModules`: Like `bootstrapScripts`, but emits [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) instead. - * **optional** `identifierPrefix`: A string prefix React uses for IDs generated by [`useId`.](/reference/react/useId) Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) - * **optional** `namespaceURI`: A string with the root [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) for the stream. Defaults to regular HTML. Pass `'http://www.w3.org/2000/svg'` for SVG or `'http://www.w3.org/1998/Math/MathML'` for MathML. - * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-outside-the-shell) or [not.](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](/reference/react-dom/server/renderToPipeableStream#logging-crashes-on-the-server) make sure that you still call `console.error`. You can also use it to [adjust the status code](/reference/react-dom/server/renderToPipeableStream#setting-the-status-code) before the shell is emitted. - * **optional** `progressiveChunkSize`: The number of bytes in a chunk. [Read more about the default heuristic.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) - * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](/reference/react-dom/server/renderToPipeableStream#aborting-server-rendering) and render the rest on the client. +* **optional** `options`: Một đối tượng với các tùy chọn tạo tĩnh. + * **optional** `bootstrapScriptContent`: Nếu được chỉ định, chuỗi này sẽ được đặt trong một thẻ `<script>` nội tuyến. + * **optional** `bootstrapScripts`: Một mảng các URL chuỗi cho các thẻ `<script>` để phát ra trên trang. Sử dụng cái này để bao gồm `<script>` gọi [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot) Bỏ qua nếu bạn không muốn chạy React trên client. + * **optional** `bootstrapModules`: Giống như `bootstrapScripts`, nhưng phát ra [`<script type="module">`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) thay thế. + * **optional** `identifierPrefix`: Một tiền tố chuỗi mà React sử dụng cho các ID được tạo bởi [`useId`.](/reference/react/useId) Hữu ích để tránh xung đột khi sử dụng nhiều root trên cùng một trang. Phải là cùng một tiền tố như được truyền cho [`hydrateRoot`.](/reference/react-dom/client/hydrateRoot#parameters) + * **optional** `namespaceURI`: Một chuỗi với [namespace URI](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS#important_namespace_uris) gốc cho stream. Mặc định là HTML thông thường. Truyền `'http://www.w3.org/2000/svg'` cho SVG hoặc `'http://www.w3.org/1998/Math/MathML'` cho MathML. + * **optional** `onError`: Một callback kích hoạt bất cứ khi nào có lỗi server, cho dù [có thể khôi phục được](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-outside-the-shell) hay [không.](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-inside-the-shell) Theo mặc định, nó chỉ gọi `console.error`. Nếu bạn ghi đè nó để [ghi lại các báo cáo sự cố,](/reference/react-dom/server/renderToPipeableStream#logging-crashes-on-the-server) hãy đảm bảo rằng bạn vẫn gọi `console.error`. Bạn cũng có thể sử dụng nó để [điều chỉnh mã trạng thái](/reference/react-dom/server/renderToPipeableStream#setting-the-status-code) trước khi shell được phát ra. + * **optional** `progressiveChunkSize`: Số byte trong một chunk. [Đọc thêm về heuristic mặc định.](https://github.com/facebook/react/blob/14c2be8dac2d5482fda8a0906a31d239df8551fc/packages/react-server/src/ReactFizzServer.js#L210-L225) + * **optional** `signal`: Một [tín hiệu abort](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) cho phép bạn [hủy bỏ quá trình kết xuất server](/reference/react-dom/server/renderToPipeableStream#aborting-server-rendering) và kết xuất phần còn lại trên client. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`prerenderToNodeStream` returns a Promise: -- If rendering the is successful, the Promise will resolve to an object containing: - - `prelude`: a [Node.js Stream.](https://nodejs.org/api/stream.html) of HTML. You can use this stream to send a response in chunks, or you can read the entire stream into a string. -- If rendering fails, the Promise will be rejected. [Use this to output a fallback shell.](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-inside-the-shell) +`prerenderToNodeStream` trả về một Promise: +- Nếu kết xuất thành công, Promise sẽ resolve thành một đối tượng chứa: + - `prelude`: một [Node.js Stream](https://nodejs.org/api/stream.html) của HTML. Bạn có thể sử dụng stream này để gửi phản hồi theo từng chunk hoặc bạn có thể đọc toàn bộ stream vào một chuỗi. +- Nếu kết xuất không thành công, Promise sẽ bị reject. [Sử dụng cái này để xuất shell dự phòng.](/reference/react-dom/server/renderToPipeableStream#recovering-from-errors-inside-the-shell) <Note> -### When should I use `prerenderToNodeStream`? {/*when-to-use-prerender*/} +### Khi nào tôi nên sử dụng `prerenderToNodeStream`? {/*when-to-use-prerender*/} -The static `prerenderToNodeStream` API is used for static server-side generation (SSG). Unlike `renderToString`, `prerenderToNodeStream` waits for all data to load before resolving. This makes it suitable for generating static HTML for a full page, including data that needs to be fetched using Suspense. To stream content as it loads, use a streaming server-side render (SSR) API like [renderToReadableStream](/reference/react-dom/server/renderToReadableStream). +API `prerenderToNodeStream` tĩnh được sử dụng để tạo server-side tĩnh (SSG). Không giống như `renderToString`, `prerenderToNodeStream` đợi tất cả dữ liệu được tải trước khi resolve. Điều này làm cho nó phù hợp để tạo HTML tĩnh cho toàn bộ trang, bao gồm cả dữ liệu cần được tìm nạp bằng Suspense. Để truyền trực tuyến nội dung khi nó tải, hãy sử dụng API kết xuất server-side (SSR) phát trực tuyến như [renderToReadableStream](/reference/react-dom/server/renderToReadableStream). </Note> --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Rendering a React tree to a stream of static HTML {/*rendering-a-react-tree-to-a-stream-of-static-html*/} +### Kết xuất một cây React thành một stream HTML tĩnh {/*rendering-a-react-tree-to-a-stream-of-static-html*/} -Call `prerenderToNodeStream` to render your React tree to static HTML into a [Node.js Stream.](https://nodejs.org/api/stream.html): +Gọi `prerenderToNodeStream` để kết xuất cây React của bạn thành HTML tĩnh vào một [Node.js Stream.](https://nodejs.org/api/stream.html): ```js [[1, 5, "<App />"], [2, 6, "['/main.js']"]] import { prerenderToNodeStream } from 'react-dom/static'; -// The route handler syntax depends on your backend framework +// Cú pháp trình xử lý route phụ thuộc vào framework backend của bạn app.use('/', async (request, response) => { const { prelude } = await prerenderToNodeStream(<App />, { bootstrapScripts: ['/main.js'], @@ -97,9 +97,9 @@ app.use('/', async (request, response) => { }); ``` -Along with the <CodeStep step={1}>root component</CodeStep>, you need to provide a list of <CodeStep step={2}>bootstrap `<script>` paths</CodeStep>. Your root component should return **the entire document including the root `<html>` tag.** +Cùng với <CodeStep step={1}>component root</CodeStep>, bạn cần cung cấp một danh sách các <CodeStep step={2}>đường dẫn `<script>` bootstrap</CodeStep>. Component root của bạn sẽ trả về **toàn bộ tài liệu bao gồm thẻ `<html>` gốc.** -For example, it might look like this: +Ví dụ: nó có thể trông như thế này: ```js [[1, 1, "App"]] export default function App() { @@ -109,7 +109,7 @@ export default function App() { <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="/styles.css"></link> - <title>My app</title> + <title>Ứng dụng của tôi</title> </head> <body> <Router /> @@ -119,17 +119,17 @@ export default function App() { } ``` -React will inject the [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) and your <CodeStep step={2}>bootstrap `<script>` tags</CodeStep> into the resulting HTML stream: +React sẽ inject [doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) và <CodeStep step={2}>thẻ `<script>` bootstrap</CodeStep> của bạn vào stream HTML kết quả: ```html [[2, 5, "/main.js"]] <!DOCTYPE html> <html> - <!-- ... HTML from your components ... --> + <!-- ... HTML từ các component của bạn ... --> </html> <script src="/main.js" async=""></script> ``` -On the client, your bootstrap script should [hydrate the entire `document` with a call to `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) +Trên client, script bootstrap của bạn sẽ [hydrate toàn bộ `document` bằng một lệnh gọi đến `hydrateRoot`:](/reference/react-dom/client/hydrateRoot#hydrating-an-entire-document) ```js [[1, 4, "<App />"]] import { hydrateRoot } from 'react-dom/client'; @@ -138,22 +138,22 @@ import App from './App.js'; hydrateRoot(document, <App />); ``` -This will attach event listeners to the static server-generated HTML and make it interactive. +Điều này sẽ đính kèm các trình xử lý sự kiện vào HTML tĩnh được tạo từ server và làm cho nó có tính tương tác. <DeepDive> -#### Reading CSS and JS asset paths from the build output {/*reading-css-and-js-asset-paths-from-the-build-output*/} +#### Đọc đường dẫn CSS và JS asset từ đầu ra bản dựng {/*reading-css-and-js-asset-paths-from-the-build-output*/} -The final asset URLs (like JavaScript and CSS files) are often hashed after the build. For example, instead of `styles.css` you might end up with `styles.123456.css`. Hashing static asset filenames guarantees that every distinct build of the same asset will have a different filename. This is useful because it lets you safely enable long-term caching for static assets: a file with a certain name would never change content. +Các URL asset cuối cùng (như các tệp JavaScript và CSS) thường được băm sau bản dựng. Ví dụ: thay vì `styles.css`, bạn có thể kết thúc với `styles.123456.css`. Băm tên tệp asset tĩnh đảm bảo rằng mọi bản dựng riêng biệt của cùng một asset sẽ có một tên tệp khác nhau. Điều này rất hữu ích vì nó cho phép bạn bật bộ nhớ cache dài hạn một cách an toàn cho các asset tĩnh: một tệp có tên nhất định sẽ không bao giờ thay đổi nội dung. -However, if you don't know the asset URLs until after the build, there's no way for you to put them in the source code. For example, hardcoding `"/styles.css"` into JSX like earlier wouldn't work. To keep them out of your source code, your root component can read the real filenames from a map passed as a prop: +Tuy nhiên, nếu bạn không biết URL asset cho đến sau bản dựng, bạn không có cách nào để đưa chúng vào mã nguồn. Ví dụ: việc mã hóa cứng `"/styles.css"` vào JSX như trước đây sẽ không hoạt động. Để giữ chúng bên ngoài mã nguồn của bạn, component root của bạn có thể đọc tên tệp thực từ một map được truyền dưới dạng một prop: ```js {1,6} export default function App({ assetMap }) { return ( <html> <head> - <title>My app</title> + <title>Ứng dụng của tôi</title> <link rel="stylesheet" href={assetMap['styles.css']}></link> </head> ... @@ -162,10 +162,10 @@ export default function App({ assetMap }) { } ``` -On the server, render `<App assetMap={assetMap} />` and pass your `assetMap` with the asset URLs: +Trên server, kết xuất `<App assetMap={assetMap} />` và truyền `assetMap` của bạn với các URL asset: ```js {1-5,8,9} -// You'd need to get this JSON from your build tooling, e.g. read it from the build output. +// Bạn cần lấy JSON này từ công cụ bản dựng của bạn, ví dụ: đọc nó từ đầu ra bản dựng. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -181,10 +181,10 @@ app.use('/', async (request, response) => { }); ``` -Since your server is now rendering `<App assetMap={assetMap} />`, you need to render it with `assetMap` on the client too to avoid hydration errors. You can serialize and pass `assetMap` to the client like this: +Vì server của bạn hiện đang kết xuất `<App assetMap={assetMap} />`, bạn cần kết xuất nó với `assetMap` trên client để tránh các lỗi hydration. Bạn có thể serialize và truyền `assetMap` cho client như sau: ```js {9-10} -// You'd need to get this JSON from your build tooling. +// Bạn cần lấy JSON này từ công cụ bản dựng của bạn. const assetMap = { 'styles.css': '/styles.123456.css', 'main.js': '/main.123456.js' @@ -192,7 +192,7 @@ const assetMap = { app.use('/', async (request, response) => { const { prelude } = await prerenderToNodeStream(<App />, { - // Careful: It's safe to stringify() this because this data isn't user-generated. + // Cẩn thận: An toàn để stringify() cái này vì dữ liệu này không phải do người dùng tạo. bootstrapScriptContent: `window.assetMap = ${JSON.stringify(assetMap)};`, bootstrapScripts: [assetMap['/main.js']], }); @@ -202,7 +202,7 @@ app.use('/', async (request, response) => { }); ``` -In the example above, the `bootstrapScriptContent` option adds an extra inline `<script>` tag that sets the global `window.assetMap` variable on the client. This lets the client code read the same `assetMap`: +Trong ví dụ trên, tùy chọn `bootstrapScriptContent` thêm một thẻ `<script>` nội tuyến bổ sung đặt biến `window.assetMap` toàn cục trên client. Điều này cho phép mã client đọc cùng một `assetMap`: ```js {4} import { hydrateRoot } from 'react-dom/client'; @@ -211,15 +211,15 @@ import App from './App.js'; hydrateRoot(document, <App assetMap={window.assetMap} />); ``` -Both client and server render `App` with the same `assetMap` prop, so there are no hydration errors. +Cả client và server đều kết xuất `App` với cùng một prop `assetMap`, vì vậy không có lỗi hydration. </DeepDive> --- -### Rendering a React tree to a string of static HTML {/*rendering-a-react-tree-to-a-string-of-static-html*/} +### Kết xuất một cây React thành một chuỗi HTML tĩnh {/*rendering-a-react-tree-to-a-string-of-static-html*/} -Call `prerenderToNodeStream` to render your app to a static HTML string: +Gọi `prerenderToNodeStream` để kết xuất ứng dụng của bạn thành một chuỗi HTML tĩnh: ```js import { prerenderToNodeStream } from 'react-dom/static'; @@ -240,13 +240,13 @@ async function renderToString() { } ``` -This will produce the initial non-interactive HTML output of your React components. On the client, you will need to call [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) to *hydrate* that server-generated HTML and make it interactive. +Điều này sẽ tạo ra đầu ra HTML không tương tác ban đầu của các component React của bạn. Trên client, bạn sẽ cần gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) để *hydrate* HTML được tạo từ server đó và làm cho nó có tính tương tác. --- -### Waiting for all data to load {/*waiting-for-all-data-to-load*/} +### Chờ tất cả dữ liệu được tải {/*waiting-for-all-data-to-load*/} -`prerenderToNodeStream` waits for all data to load before finishing the static HTML generation and resolving. For example, consider a profile page that shows a cover, a sidebar with friends and photos, and a list of posts: +`prerenderToNodeStream` đợi tất cả dữ liệu được tải trước khi hoàn thành việc tạo HTML tĩnh và resolve. Ví dụ: hãy xem xét một trang hồ sơ hiển thị ảnh bìa, một sidebar với bạn bè và ảnh và một danh sách các bài đăng: ```js function ProfilePage() { @@ -265,31 +265,30 @@ function ProfilePage() { } ``` -Imagine that `<Posts />` needs to load some data, which takes some time. Ideally, you'd want wait for the posts to finish so it's included in the HTML. To do this, you can use Suspense to suspend on the data, and `prerenderToNodeStream` will wait for the suspended content to finish before resolving to the static HTML. +Hãy tưởng tượng rằng `<Posts />` cần tải một số dữ liệu, điều này mất một chút thời gian. Lý tưởng nhất là bạn muốn đợi các bài đăng hoàn thành để nó được đưa vào HTML. Để thực hiện việc này, bạn có thể sử dụng Suspense để tạm dừng dữ liệu và `prerenderToNodeStream` sẽ đợi nội dung bị tạm dừng hoàn thành trước khi resolve thành HTML tĩnh. <Note> -**Only Suspense-enabled data sources will activate the Suspense component.** They include: +**Chỉ các nguồn dữ liệu hỗ trợ Suspense mới kích hoạt component Suspense.** Chúng bao gồm: -- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials) -- Lazy-loading component code with [`lazy`](/reference/react/lazy) -- Reading the value of a Promise with [`use`](/reference/react/use) +- Tìm nạp dữ liệu với các framework hỗ trợ Suspense như [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) và [Next.js](https://nextjs.org/docs/getting-started/react-essentials) +- Tải mã component một cách lazy với [`lazy`](/reference/react/lazy) +- Đọc giá trị của một Promise với [`use`](/reference/react/use) -Suspense **does not** detect when data is fetched inside an Effect or event handler. +Suspense **không** phát hiện khi dữ liệu được tìm nạp bên trong một Effect hoặc trình xử lý sự kiện. -The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. +Cách chính xác bạn sẽ tải dữ liệu trong component `Posts` ở trên phụ thuộc vào framework của bạn. Nếu bạn sử dụng một framework hỗ trợ Suspense, bạn sẽ tìm thấy các chi tiết trong tài liệu tìm nạp dữ liệu của nó. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Việc tìm nạp dữ liệu hỗ trợ Suspense mà không sử dụng framework có ý kiến vẫn chưa được hỗ trợ. Các yêu cầu để triển khai một nguồn dữ liệu hỗ trợ Suspense là không ổn định và không được ghi lại. Một API chính thức để tích hợp các nguồn dữ liệu với Suspense sẽ được phát hành trong một phiên bản React trong tương lai. </Note> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My stream doesn't start until the entire app is rendered {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/} +### Stream của tôi không bắt đầu cho đến khi toàn bộ ứng dụng được kết xuất {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/} -The `prerenderToNodeStream` response waits for the entire app to finish rendering, including waiting for all Suspense boundaries to resolve, before resolving. It is designed for static site generation (SSG) ahead of time and does not support streaming more content as it loads. +Phản hồi `prerenderToNodeStream` đợi cho đến khi toàn bộ ứng dụng kết thúc quá trình kết xuất, bao gồm cả việc chờ tất cả các ranh giới Suspense được resolve, trước khi resolve. Nó được thiết kế để tạo trang web tĩnh (SSG) trước thời hạn và không hỗ trợ truyền trực tuyến thêm nội dung khi nó tải. -To stream content as it loads, use a streaming server render API like [renderToPipeableStream](/reference/react-dom/server/renderToPipeableStream). - +Để truyền trực tuyến nội dung khi nó tải, hãy sử dụng API kết xuất server phát trực tuyến như [renderToPipeableStream](/reference/react-dom/server/renderToPipeableStream). diff --git a/src/content/reference/react/Children.md b/src/content/reference/react/Children.md index 81a28c5b3..58b62d3cf 100644 --- a/src/content/reference/react/Children.md +++ b/src/content/reference/react/Children.md @@ -4,13 +4,13 @@ title: Children <Pitfall> -Using `Children` is uncommon and can lead to fragile code. [See common alternatives.](#alternatives) +Việc sử dụng `Children` là không phổ biến và có thể dẫn đến code dễ bị lỗi. [Xem các giải pháp thay thế phổ biến.](#alternatives) </Pitfall> <Intro> -`Children` lets you manipulate and transform the JSX you received as the [`children` prop.](/learn/passing-props-to-a-component#passing-jsx-as-children) +`Children` cho phép bạn thao tác và chuyển đổi JSX mà bạn nhận được dưới dạng [`children` prop.](/learn/passing-props-to-a-component#passing-jsx-as-children) ```js const mappedChildren = Children.map(children, child => @@ -18,7 +18,6 @@ const mappedChildren = Children.map(children, child => {child} </div> ); - ``` </Intro> @@ -27,11 +26,11 @@ const mappedChildren = Children.map(children, child => --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `Children.count(children)` {/*children-count*/} -Call `Children.count(children)` to count the number of children in the `children` data structure. +Gọi `Children.count(children)` để đếm số lượng phần tử con trong cấu trúc dữ liệu `children`. ```js src/RowList.js active import { Children } from 'react'; @@ -39,32 +38,32 @@ import { Children } from 'react'; function RowList({ children }) { return ( <> - <h1>Total rows: {Children.count(children)}</h1> + <h1>Tổng số hàng: {Children.count(children)}</h1> ... </> ); } ``` -[See more examples below.](#counting-children) +[Xem thêm các ví dụ bên dưới.](#counting-children) -#### Parameters {/*children-count-parameters*/} +#### Tham số {/*children-count-parameters*/} -* `children`: The value of the [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) received by your component. +* `children`: Giá trị của [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) mà component của bạn nhận được. -#### Returns {/*children-count-returns*/} +#### Giá trị trả về {/*children-count-returns*/} -The number of nodes inside these `children`. +Số lượng node bên trong `children`. -#### Caveats {/*children-count-caveats*/} +#### Lưu ý {/*children-count-caveats*/} -- Empty nodes (`null`, `undefined`, and Booleans), strings, numbers, and [React elements](/reference/react/createElement) count as individual nodes. Arrays don't count as individual nodes, but their children do. **The traversal does not go deeper than React elements:** they don't get rendered, and their children aren't traversed. [Fragments](/reference/react/Fragment) don't get traversed. +- Các node rỗng (`null`, `undefined` và Booleans), strings, numbers và [React elements](/reference/react/createElement) được tính là các node riêng lẻ. Mảng không được tính là các node riêng lẻ, nhưng các phần tử con của chúng thì có. **Việc duyệt không đi sâu hơn các React elements:** chúng không được render và các phần tử con của chúng không được duyệt. [Fragments](/reference/react/Fragment) không được duyệt. --- ### `Children.forEach(children, fn, thisArg?)` {/*children-foreach*/} -Call `Children.forEach(children, fn, thisArg?)` to run some code for each child in the `children` data structure. +Gọi `Children.forEach(children, fn, thisArg?)` để chạy một đoạn code cho mỗi phần tử con trong cấu trúc dữ liệu `children`. ```js src/RowList.js active import { Children } from 'react'; @@ -78,27 +77,27 @@ function SeparatorList({ children }) { // ... ``` -[See more examples below.](#running-some-code-for-each-child) +[Xem thêm các ví dụ bên dưới.](#running-some-code-for-each-child) -#### Parameters {/*children-foreach-parameters*/} +#### Tham số {/*children-foreach-parameters*/} -* `children`: The value of the [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) received by your component. -* `fn`: The function you want to run for each child, similar to the [array `forEach` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) callback. It will be called with the child as the first argument and its index as the second argument. The index starts at `0` and increments on each call. -* **optional** `thisArg`: The [`this` value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) with which the `fn` function should be called. If omitted, it's `undefined`. +* `children`: Giá trị của [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) mà component của bạn nhận được. +* `fn`: Hàm bạn muốn chạy cho mỗi phần tử con, tương tự như callback của [array `forEach` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). Nó sẽ được gọi với phần tử con là đối số đầu tiên và chỉ mục của nó là đối số thứ hai. Chỉ mục bắt đầu từ `0` và tăng lên trên mỗi lần gọi. +* **optional** `thisArg`: Giá trị [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) mà hàm `fn` sẽ được gọi. Nếu bỏ qua, nó là `undefined`. -#### Returns {/*children-foreach-returns*/} +#### Giá trị trả về {/*children-foreach-returns*/} -`Children.forEach` returns `undefined`. +`Children.forEach` trả về `undefined`. -#### Caveats {/*children-foreach-caveats*/} +#### Lưu ý {/*children-foreach-caveats*/} -- Empty nodes (`null`, `undefined`, and Booleans), strings, numbers, and [React elements](/reference/react/createElement) count as individual nodes. Arrays don't count as individual nodes, but their children do. **The traversal does not go deeper than React elements:** they don't get rendered, and their children aren't traversed. [Fragments](/reference/react/Fragment) don't get traversed. +- Các node rỗng (`null`, `undefined` và Booleans), strings, numbers và [React elements](/reference/react/createElement) được tính là các node riêng lẻ. Mảng không được tính là các node riêng lẻ, nhưng các phần tử con của chúng thì có. **Việc duyệt không đi sâu hơn các React elements:** chúng không được render và các phần tử con của chúng không được duyệt. [Fragments](/reference/react/Fragment) không được duyệt. --- ### `Children.map(children, fn, thisArg?)` {/*children-map*/} -Call `Children.map(children, fn, thisArg?)` to map or transform each child in the `children` data structure. +Gọi `Children.map(children, fn, thisArg?)` để ánh xạ hoặc chuyển đổi mỗi phần tử con trong cấu trúc dữ liệu `children`. ```js src/RowList.js active import { Children } from 'react'; @@ -116,32 +115,31 @@ function RowList({ children }) { } ``` -[See more examples below.](#transforming-children) +[Xem thêm các ví dụ bên dưới.](#transforming-children) -#### Parameters {/*children-map-parameters*/} +#### Tham số {/*children-map-parameters*/} -* `children`: The value of the [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) received by your component. -* `fn`: The mapping function, similar to the [array `map` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) callback. It will be called with the child as the first argument and its index as the second argument. The index starts at `0` and increments on each call. You need to return a React node from this function. This may be an empty node (`null`, `undefined`, or a Boolean), a string, a number, a React element, or an array of other React nodes. -* **optional** `thisArg`: The [`this` value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) with which the `fn` function should be called. If omitted, it's `undefined`. +* `children`: Giá trị của [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) mà component của bạn nhận được. +* `fn`: Hàm ánh xạ, tương tự như callback của [array `map` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). Nó sẽ được gọi với phần tử con là đối số đầu tiên và chỉ mục của nó là đối số thứ hai. Chỉ mục bắt đầu từ `0` và tăng lên trên mỗi lần gọi. Bạn cần trả về một React node từ hàm này. Đây có thể là một node rỗng (`null`, `undefined` hoặc Boolean), một string, một number, một React element hoặc một mảng các React node khác. +* **optional** `thisArg`: Giá trị [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) mà hàm `fn` sẽ được gọi. Nếu bỏ qua, nó là `undefined`. -#### Returns {/*children-map-returns*/} +#### Giá trị trả về {/*children-map-returns*/} -If `children` is `null` or `undefined`, returns the same value. +Nếu `children` là `null` hoặc `undefined`, trả về giá trị tương tự. -Otherwise, returns a flat array consisting of the nodes you've returned from the `fn` function. The returned array will contain all nodes you returned except for `null` and `undefined`. +Nếu không, trả về một mảng phẳng bao gồm các node bạn đã trả về từ hàm `fn`. Mảng trả về sẽ chứa tất cả các node bạn đã trả về ngoại trừ `null` và `undefined`. -#### Caveats {/*children-map-caveats*/} +#### Lưu ý {/*children-map-caveats*/} -- Empty nodes (`null`, `undefined`, and Booleans), strings, numbers, and [React elements](/reference/react/createElement) count as individual nodes. Arrays don't count as individual nodes, but their children do. **The traversal does not go deeper than React elements:** they don't get rendered, and their children aren't traversed. [Fragments](/reference/react/Fragment) don't get traversed. +- Các node rỗng (`null`, `undefined` và Booleans), strings, numbers và [React elements](/reference/react/createElement) được tính là các node riêng lẻ. Mảng không được tính là các node riêng lẻ, nhưng các phần tử con của chúng thì có. **Việc duyệt không đi sâu hơn các React elements:** chúng không được render và các phần tử con của chúng không được duyệt. [Fragments](/reference/react/Fragment) không được duyệt. -- If you return an element or an array of elements with keys from `fn`, **the returned elements' keys will be automatically combined with the key of the corresponding original item from `children`.** When you return multiple elements from `fn` in an array, their keys only need to be unique locally amongst each other. +- Nếu bạn trả về một element hoặc một mảng các element có keys từ `fn`, **các keys của các element được trả về sẽ tự động được kết hợp với key của mục gốc tương ứng từ `children`.** Khi bạn trả về nhiều element từ `fn` trong một mảng, các keys của chúng chỉ cần là duy nhất cục bộ giữa chúng với nhau. --- ### `Children.only(children)` {/*children-only*/} - -Call `Children.only(children)` to assert that `children` represent a single React element. +Gọi `Children.only(children)` để xác nhận rằng `children` đại diện cho một React element duy nhất. ```js function Box({ children }) { @@ -149,25 +147,25 @@ function Box({ children }) { // ... ``` -#### Parameters {/*children-only-parameters*/} +#### Tham số {/*children-only-parameters*/} -* `children`: The value of the [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) received by your component. +* `children`: Giá trị của [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) mà component của bạn nhận được. -#### Returns {/*children-only-returns*/} +#### Giá trị trả về {/*children-only-returns*/} -If `children` [is a valid element,](/reference/react/isValidElement) returns that element. +Nếu `children` [là một element hợp lệ,](/reference/react/isValidElement) trả về element đó. -Otherwise, throws an error. +Nếu không, đưa ra một lỗi. -#### Caveats {/*children-only-caveats*/} +#### Lưu ý {/*children-only-caveats*/} -- This method always **throws if you pass an array (such as the return value of `Children.map`) as `children`.** In other words, it enforces that `children` is a single React element, not that it's an array with a single element. +- Phương thức này luôn **ném ra lỗi nếu bạn truyền một mảng (chẳng hạn như giá trị trả về của `Children.map`) làm `children`.** Nói cách khác, nó thực thi rằng `children` là một React element duy nhất, không phải là một mảng với một element duy nhất. --- ### `Children.toArray(children)` {/*children-toarray*/} -Call `Children.toArray(children)` to create an array out of the `children` data structure. +Gọi `Children.toArray(children)` để tạo một mảng từ cấu trúc dữ liệu `children`. ```js src/ReversedList.js active import { Children } from 'react'; @@ -178,25 +176,25 @@ export default function ReversedList({ children }) { // ... ``` -#### Parameters {/*children-toarray-parameters*/} +#### Tham số {/*children-toarray-parameters*/} -* `children`: The value of the [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) received by your component. +* `children`: Giá trị của [`children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) mà component của bạn nhận được. -#### Returns {/*children-toarray-returns*/} +#### Giá trị trả về {/*children-toarray-returns*/} -Returns a flat array of elements in `children`. +Trả về một mảng phẳng các element trong `children`. -#### Caveats {/*children-toarray-caveats*/} +#### Lưu ý {/*children-toarray-caveats*/} -- Empty nodes (`null`, `undefined`, and Booleans) will be omitted in the returned array. **The returned elements' keys will be calculated from the original elements' keys and their level of nesting and position.** This ensures that flattening the array does not introduce changes in behavior. +- Các node rỗng (`null`, `undefined` và Booleans) sẽ bị bỏ qua trong mảng trả về. **Các keys của các element được trả về sẽ được tính toán từ các keys của các element gốc và mức độ lồng nhau và vị trí của chúng.** Điều này đảm bảo rằng việc làm phẳng mảng không gây ra thay đổi trong hành vi. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Transforming children {/*transforming-children*/} +### Chuyển đổi các phần tử con {/*transforming-children*/} -To transform the children JSX that your component [receives as the `children` prop,](/learn/passing-props-to-a-component#passing-jsx-as-children) call `Children.map`: +Để chuyển đổi JSX children mà component của bạn [nhận được dưới dạng `children` prop,](/learn/passing-props-to-a-component#passing-jsx-as-children) hãy gọi `Children.map`: ```js {6,10} import { Children } from 'react'; @@ -214,33 +212,33 @@ function RowList({ children }) { } ``` -In the example above, the `RowList` wraps every child it receives into a `<div className="Row">` container. For example, let's say the parent component passes three `<p>` tags as the `children` prop to `RowList`: +Trong ví dụ trên, `RowList` bao bọc mọi phần tử con mà nó nhận được vào một container `<div className="Row">`. Ví dụ: giả sử component cha truyền ba thẻ `<p>` làm `children` prop cho `RowList`: ```js <RowList> - <p>This is the first item.</p> - <p>This is the second item.</p> - <p>This is the third item.</p> + <p>Đây là mục đầu tiên.</p> + <p>Đây là mục thứ hai.</p> + <p>Đây là mục thứ ba.</p> </RowList> ``` -Then, with the `RowList` implementation above, the final rendered result will look like this: +Sau đó, với việc triển khai `RowList` ở trên, kết quả render cuối cùng sẽ trông như thế này: ```js <div className="RowList"> <div className="Row"> - <p>This is the first item.</p> + <p>Đây là mục đầu tiên.</p> </div> <div className="Row"> - <p>This is the second item.</p> + <p>Đây là mục thứ hai.</p> </div> <div className="Row"> - <p>This is the third item.</p> + <p>Đây là mục thứ ba.</p> </div> </div> ``` -`Children.map` is similar to [to transforming arrays with `map()`.](/learn/rendering-lists) The difference is that the `children` data structure is considered *opaque.* This means that even if it's sometimes an array, you should not assume it's an array or any other particular data type. This is why you should use `Children.map` if you need to transform it. +`Children.map` tương tự như [chuyển đổi mảng với `map()`.](/learn/rendering-lists) Sự khác biệt là cấu trúc dữ liệu `children` được coi là *opaque*. Điều này có nghĩa là ngay cả khi đôi khi nó là một mảng, bạn không nên cho rằng nó là một mảng hoặc bất kỳ kiểu dữ liệu cụ thể nào khác. Đây là lý do tại sao bạn nên sử dụng `Children.map` nếu bạn cần chuyển đổi nó. <Sandpack> @@ -250,9 +248,9 @@ import RowList from './RowList.js'; export default function App() { return ( <RowList> - <p>This is the first item.</p> - <p>This is the second item.</p> - <p>This is the third item.</p> + <p>Đây là mục đầu tiên.</p> + <p>Đây là mục thứ hai.</p> + <p>Đây là mục thứ ba.</p> </RowList> ); } @@ -293,24 +291,24 @@ export default function RowList({ children }) { <DeepDive> -#### Why is the children prop not always an array? {/*why-is-the-children-prop-not-always-an-array*/} +#### Tại sao children prop không phải lúc nào cũng là một mảng? {/*why-is-the-children-prop-not-always-an-array*/} -In React, the `children` prop is considered an *opaque* data structure. This means that you shouldn't rely on how it is structured. To transform, filter, or count children, you should use the `Children` methods. +Trong React, `children` prop được coi là một cấu trúc dữ liệu *opaque*. Điều này có nghĩa là bạn không nên dựa vào cách nó được cấu trúc. Để chuyển đổi, lọc hoặc đếm các phần tử con, bạn nên sử dụng các phương thức `Children`. -In practice, the `children` data structure is often represented as an array internally. However, if there is only a single child, then React won't create an extra array since this would lead to unnecessary memory overhead. As long as you use the `Children` methods instead of directly introspecting the `children` prop, your code will not break even if React changes how the data structure is actually implemented. +Trong thực tế, cấu trúc dữ liệu `children` thường được biểu diễn dưới dạng một mảng bên trong. Tuy nhiên, nếu chỉ có một phần tử con duy nhất, thì React sẽ không tạo một mảng bổ sung vì điều này sẽ dẫn đến chi phí bộ nhớ không cần thiết. Miễn là bạn sử dụng các phương thức `Children` thay vì trực tiếp xem xét `children` prop, code của bạn sẽ không bị hỏng ngay cả khi React thay đổi cách cấu trúc dữ liệu được thực hiện trên thực tế. -Even when `children` is an array, `Children.map` has useful special behavior. For example, `Children.map` combines the [keys](/learn/rendering-lists#keeping-list-items-in-order-with-key) on the returned elements with the keys on the `children` you've passed to it. This ensures the original JSX children don't "lose" keys even if they get wrapped like in the example above. +Ngay cả khi `children` là một mảng, `Children.map` có hành vi đặc biệt hữu ích. Ví dụ: `Children.map` kết hợp các [keys](/learn/rendering-lists#keeping-list-items-in-order-with-key) trên các element được trả về với các keys trên `children` mà bạn đã truyền cho nó. Điều này đảm bảo rằng các JSX children ban đầu không "mất" keys ngay cả khi chúng được bao bọc như trong ví dụ trên. </DeepDive> <Pitfall> -The `children` data structure **does not include rendered output** of the components you pass as JSX. In the example below, the `children` received by the `RowList` only contains two items rather than three: +Cấu trúc dữ liệu `children` **không bao gồm đầu ra được render** của các component bạn truyền dưới dạng JSX. Trong ví dụ bên dưới, `children` mà `RowList` nhận được chỉ chứa hai mục thay vì ba: -1. `<p>This is the first item.</p>` +1. `<p>Đây là mục đầu tiên.</p>` 2. `<MoreRows />` -This is why only two row wrappers are generated in this example: +Đây là lý do tại sao chỉ có hai trình bao bọc hàng được tạo trong ví dụ này: <Sandpack> @@ -320,7 +318,7 @@ import RowList from './RowList.js'; export default function App() { return ( <RowList> - <p>This is the first item.</p> + <p>Đây là mục đầu tiên.</p> <MoreRows /> </RowList> ); @@ -329,8 +327,8 @@ export default function App() { function MoreRows() { return ( <> - <p>This is the second item.</p> - <p>This is the third item.</p> + <p>Đây là mục thứ hai.</p> + <p>Đây là mục thứ ba.</p> </> ); } @@ -369,15 +367,15 @@ export default function RowList({ children }) { </Sandpack> -**There is no way to get the rendered output of an inner component** like `<MoreRows />` when manipulating `children`. This is why [it's usually better to use one of the alternative solutions.](#alternatives) +**Không có cách nào để lấy đầu ra được render của một component bên trong** như `<MoreRows />` khi thao tác `children`. Đây là lý do tại sao [thường tốt hơn là sử dụng một trong các giải pháp thay thế.](#alternatives) </Pitfall> --- -### Running some code for each child {/*running-some-code-for-each-child*/} +### Chạy một đoạn code cho mỗi phần tử con {/*running-some-code-for-each-child*/} -Call `Children.forEach` to iterate over each child in the `children` data structure. It does not return any value and is similar to the [array `forEach` method.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) You can use it to run custom logic like constructing your own array. +Gọi `Children.forEach` để lặp lại trên mỗi phần tử con trong cấu trúc dữ liệu `children`. Nó không trả về bất kỳ giá trị nào và tương tự như [array `forEach` method.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) Bạn có thể sử dụng nó để chạy logic tùy chỉnh như xây dựng mảng của riêng bạn. <Sandpack> @@ -387,9 +385,9 @@ import SeparatorList from './SeparatorList.js'; export default function App() { return ( <SeparatorList> - <p>This is the first item.</p> - <p>This is the second item.</p> - <p>This is the third item.</p> + <p>Đây là mục đầu tiên.</p> + <p>Đây là mục thứ hai.</p> + <p>Đây là mục thứ ba.</p> </SeparatorList> ); } @@ -413,15 +411,15 @@ export default function SeparatorList({ children }) { <Pitfall> -As mentioned earlier, there is no way to get the rendered output of an inner component when manipulating `children`. This is why [it's usually better to use one of the alternative solutions.](#alternatives) +Như đã đề cập trước đó, không có cách nào để lấy đầu ra được render của một component bên trong khi thao tác `children`. Đây là lý do tại sao [thường tốt hơn là sử dụng một trong các giải pháp thay thế.](#alternatives) </Pitfall> --- -### Counting children {/*counting-children*/} +### Đếm các phần tử con {/*counting-children*/} -Call `Children.count(children)` to calculate the number of children. +Gọi `Children.count(children)` để tính số lượng phần tử con. <Sandpack> @@ -431,9 +429,9 @@ import RowList from './RowList.js'; export default function App() { return ( <RowList> - <p>This is the first item.</p> - <p>This is the second item.</p> - <p>This is the third item.</p> + <p>Đây là mục đầu tiên.</p> + <p>Đây là mục thứ hai.</p> + <p>Đây là mục thứ ba.</p> </RowList> ); } @@ -446,7 +444,7 @@ export default function RowList({ children }) { return ( <div className="RowList"> <h1 className="RowListHeader"> - Total rows: {Children.count(children)} + Tổng số hàng: {Children.count(children)} </h1> {Children.map(children, child => <div className="Row"> @@ -484,15 +482,15 @@ export default function RowList({ children }) { <Pitfall> -As mentioned earlier, there is no way to get the rendered output of an inner component when manipulating `children`. This is why [it's usually better to use one of the alternative solutions.](#alternatives) +Như đã đề cập trước đó, không có cách nào để lấy đầu ra được render của một component bên trong khi thao tác `children`. Đây là lý do tại sao [thường tốt hơn là sử dụng một trong các giải pháp thay thế.](#alternatives) </Pitfall> --- -### Converting children to an array {/*converting-children-to-an-array*/} +### Chuyển đổi các phần tử con thành một mảng {/*converting-children-to-an-array*/} -Call `Children.toArray(children)` to turn the `children` data structure into a regular JavaScript array. This lets you manipulate the array with built-in array methods like [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), [`sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort), or [`reverse`.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) +Gọi `Children.toArray(children)` để biến cấu trúc dữ liệu `children` thành một mảng JavaScript thông thường. Điều này cho phép bạn thao tác mảng với các phương thức mảng tích hợp như [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), [`sort`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) hoặc [`reverse`.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse) <Sandpack> @@ -502,9 +500,9 @@ import ReversedList from './ReversedList.js'; export default function App() { return ( <ReversedList> - <p>This is the first item.</p> - <p>This is the second item.</p> - <p>This is the third item.</p> + <p>Đây là mục đầu tiên.</p> + <p>Đây là mục thứ hai.</p> + <p>Đây là mục thứ ba.</p> </ReversedList> ); } @@ -524,31 +522,32 @@ export default function ReversedList({ children }) { <Pitfall> -As mentioned earlier, there is no way to get the rendered output of an inner component when manipulating `children`. This is why [it's usually better to use one of the alternative solutions.](#alternatives) +Như đã đề cập trước đó, không có cách nào để lấy đầu ra được render của một component bên trong khi thao tác `children`. Đây là lý do tại sao [thường tốt hơn là sử dụng một trong các giải pháp thay thế.](#alternatives) </Pitfall> --- -## Alternatives {/*alternatives*/} +## Các giải pháp thay thế {/*alternatives*/} <Note> -This section describes alternatives to the `Children` API (with capital `C`) that's imported like this: +Phần này mô tả các giải pháp thay thế cho `Children` API (viết hoa `C`) được import như thế này: ```js import { Children } from 'react'; ``` -Don't confuse it with [using the `children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) (lowercase `c`), which is good and encouraged. +Đừng nhầm lẫn nó với [việc sử dụng `children` prop](/learn/passing-props-to-a-component#passing-jsx-as-children) (viết thường `c`), điều này là tốt và được khuyến khích. </Note> -### Exposing multiple components {/*exposing-multiple-components*/} -Manipulating children with the `Children` methods often leads to fragile code. When you pass children to a component in JSX, you don't usually expect the component to manipulate or transform the individual children. +### Trình bày nhiều component {/*exposing-multiple-components*/} + +Việc thao tác các phần tử con bằng các phương thức `Children` thường dẫn đến code dễ bị lỗi. Khi bạn truyền các phần tử con vào một component trong JSX, bạn thường không mong đợi component đó thao tác hoặc chuyển đổi các phần tử con riêng lẻ. -When you can, try to avoid using the `Children` methods. For example, if you want every child of `RowList` to be wrapped in `<div className="Row">`, export a `Row` component, and manually wrap every row into it like this: +Khi có thể, hãy cố gắng tránh sử dụng các phương thức `Children`. Ví dụ: nếu bạn muốn mọi phần tử con của `RowList` được bao bọc trong `<div className="Row">`, hãy xuất một component `Row` và tự bao bọc từng hàng vào nó như sau: <Sandpack> @@ -559,13 +558,13 @@ export default function App() { return ( <RowList> <Row> - <p>This is the first item.</p> + <p>Đây là mục đầu tiên.</p> </Row> <Row> - <p>This is the second item.</p> + <p>Đây là mục thứ hai.</p> </Row> <Row> - <p>This is the third item.</p> + <p>Đây là mục thứ ba.</p> </Row> </RowList> ); @@ -607,7 +606,7 @@ export function Row({ children }) { </Sandpack> -Unlike using `Children.map`, this approach does not wrap every child automatically. **However, this approach has a significant benefit compared to the [earlier example with `Children.map`](#transforming-children) because it works even if you keep extracting more components.** For example, it still works if you extract your own `MoreRows` component: +Không giống như sử dụng `Children.map`, phương pháp này không tự động bao bọc mọi phần tử con. **Tuy nhiên, phương pháp này có một lợi ích đáng kể so với [ví dụ trước với `Children.map`](#transforming-children) vì nó hoạt động ngay cả khi bạn tiếp tục trích xuất thêm các component.** Ví dụ: nó vẫn hoạt động nếu bạn trích xuất component `MoreRows` của riêng bạn: <Sandpack> @@ -618,7 +617,7 @@ export default function App() { return ( <RowList> <Row> - <p>This is the first item.</p> + <p>Đây là mục đầu tiên.</p> </Row> <MoreRows /> </RowList> @@ -629,10 +628,10 @@ function MoreRows() { return ( <> <Row> - <p>This is the second item.</p> + <p>Đây là mục thứ hai.</p> </Row> <Row> - <p>This is the third item.</p> + <p>Đây là mục thứ ba.</p> </Row> </> ); @@ -674,13 +673,13 @@ export function Row({ children }) { </Sandpack> -This wouldn't work with `Children.map` because it would "see" `<MoreRows />` as a single child (and a single row). +Điều này sẽ không hoạt động với `Children.map` vì nó sẽ "nhìn thấy" `<MoreRows />` như một phần tử con duy nhất (và một hàng duy nhất). --- -### Accepting an array of objects as a prop {/*accepting-an-array-of-objects-as-a-prop*/} +### Chấp nhận một mảng các đối tượng làm prop {/*accepting-an-array-of-objects-as-a-prop*/} -You can also explicitly pass an array as a prop. For example, this `RowList` accepts a `rows` array as a prop: +Bạn cũng có thể truyền một mảng một cách rõ ràng làm prop. Ví dụ: `RowList` này chấp nhận một mảng `rows` làm prop: <Sandpack> @@ -690,9 +689,9 @@ import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rows={[ - { id: 'first', content: <p>This is the first item.</p> }, - { id: 'second', content: <p>This is the second item.</p> }, - { id: 'third', content: <p>This is the third item.</p> } + { id: 'first', content: <p>Đây là mục đầu tiên.</p> }, + { id: 'second', content: <p>Đây là mục thứ hai.</p> }, + { id: 'third', content: <p>Đây là mục thứ ba.</p> } ]} /> ); } @@ -729,9 +728,9 @@ export function RowList({ rows }) { </Sandpack> -Since `rows` is a regular JavaScript array, the `RowList` component can use built-in array methods like [`map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) on it. +Vì `rows` là một mảng JavaScript thông thường, component `RowList` có thể sử dụng các phương thức mảng tích hợp như [`map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) trên nó. -This pattern is especially useful when you want to be able to pass more information as structured data together with children. In the below example, the `TabSwitcher` component receives an array of objects as the `tabs` prop: +Mô hình này đặc biệt hữu ích khi bạn muốn có thể truyền thêm thông tin dưới dạng dữ liệu có cấu trúc cùng với các phần tử con. Trong ví dụ dưới đây, component `TabSwitcher` nhận một mảng các đối tượng làm prop `tabs`: <Sandpack> @@ -744,17 +743,17 @@ export default function App() { { id: 'first', header: 'First', - content: <p>This is the first item.</p> + content: <p>Đây là mục đầu tiên.</p> }, { id: 'second', header: 'Second', - content: <p>This is the second item.</p> + content: <p>Đây là mục thứ hai.</p> }, { id: 'third', header: 'Third', - content: <p>This is the third item.</p> + content: <p>Đây là mục thứ ba.</p> } ]} /> ); @@ -789,13 +788,13 @@ export default function TabSwitcher({ tabs }) { </Sandpack> -Unlike passing the children as JSX, this approach lets you associate some extra data like `header` with each item. Because you are working with the `tabs` directly, and it is an array, you do not need the `Children` methods. +Không giống như truyền các phần tử con dưới dạng JSX, phương pháp này cho phép bạn liên kết một số dữ liệu bổ sung như `header` với mỗi mục. Vì bạn đang làm việc trực tiếp với `tabs` và nó là một mảng, bạn không cần các phương thức `Children`. --- -### Calling a render prop to customize rendering {/*calling-a-render-prop-to-customize-rendering*/} +### Gọi một render prop để tùy chỉnh rendering {/*calling-a-render-prop-to-customize-rendering*/} -Instead of producing JSX for every single item, you can also pass a function that returns JSX, and call that function when necessary. In this example, the `App` component passes a `renderContent` function to the `TabSwitcher` component. The `TabSwitcher` component calls `renderContent` only for the selected tab: +Thay vì tạo JSX cho mọi mục duy nhất, bạn cũng có thể truyền một hàm trả về JSX và gọi hàm đó khi cần thiết. Trong ví dụ này, component `App` truyền một hàm `renderContent` cho component `TabSwitcher`. Component `TabSwitcher` chỉ gọi `renderContent` cho tab đã chọn: <Sandpack> @@ -810,7 +809,7 @@ export default function App() { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { - return <p>This is the {tabId} item.</p>; + return <p>Đây là mục {tabId}.</p>; }} /> ); @@ -844,9 +843,9 @@ export default function TabSwitcher({ tabIds, getHeader, renderContent }) { </Sandpack> -A prop like `renderContent` is called a *render prop* because it is a prop that specifies how to render a piece of the user interface. However, there is nothing special about it: it is a regular prop which happens to be a function. +Một prop như `renderContent` được gọi là *render prop* vì nó là một prop chỉ định cách render một phần của giao diện người dùng. Tuy nhiên, không có gì đặc biệt về nó: nó là một prop thông thường, tình cờ là một hàm. -Render props are functions, so you can pass information to them. For example, this `RowList` component passes the `id` and the `index` of each row to the `renderRow` render prop, which uses `index` to highlight even rows: +Render props là các hàm, vì vậy bạn có thể truyền thông tin cho chúng. Ví dụ: component `RowList` này truyền `id` và `index` của mỗi hàng cho render prop `renderRow`, sử dụng `index` để làm nổi bật các hàng chẵn: <Sandpack> @@ -860,7 +859,7 @@ export default function App() { renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> - <p>This is the {id} item.</p> + <p>Đây là mục {id}.</p> </Row> ); }} @@ -876,7 +875,7 @@ export function RowList({ rowIds, renderRow }) { return ( <div className="RowList"> <h1 className="RowListHeader"> - Total rows: {rowIds.length} + Tổng số hàng: {rowIds.length} </h1> {rowIds.map((rowId, index) => <Fragment key={rowId}> @@ -927,23 +926,23 @@ export function Row({ children, isHighlighted }) { </Sandpack> -This is another example of how parent and child components can cooperate without manipulating the children. +Đây là một ví dụ khác về cách các component cha và con có thể hợp tác mà không cần thao tác các phần tử con. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I pass a custom component, but the `Children` methods don't show its render result {/*i-pass-a-custom-component-but-the-children-methods-dont-show-its-render-result*/} +### Tôi truyền một component tùy chỉnh, nhưng các phương thức `Children` không hiển thị kết quả render của nó {/*i-pass-a-custom-component-but-the-children-methods-dont-show-its-render-result*/} -Suppose you pass two children to `RowList` like this: +Giả sử bạn truyền hai phần tử con cho `RowList` như sau: ```js <RowList> - <p>First item</p> + <p>Mục đầu tiên</p> <MoreRows /> </RowList> ``` -If you do `Children.count(children)` inside `RowList`, you will get `2`. Even if `MoreRows` renders 10 different items, or if it returns `null`, `Children.count(children)` will still be `2`. From the `RowList`'s perspective, it only "sees" the JSX it has received. It does not "see" the internals of the `MoreRows` component. +Nếu bạn thực hiện `Children.count(children)` bên trong `RowList`, bạn sẽ nhận được `2`. Ngay cả khi `MoreRows` render 10 mục khác nhau hoặc nếu nó trả về `null`, `Children.count(children)` vẫn sẽ là `2`. Từ góc độ của `RowList`, nó chỉ "nhìn thấy" JSX mà nó đã nhận được. Nó không "nhìn thấy" các phần bên trong của component `MoreRows`. -The limitation makes it hard to extract a component. This is why [alternatives](#alternatives) are preferred to using `Children`. +Hạn chế này gây khó khăn cho việc trích xuất một component. Đây là lý do tại sao [các giải pháp thay thế](#alternatives) được ưu tiên hơn là sử dụng `Children`. diff --git a/src/content/reference/react/Profiler.md b/src/content/reference/react/Profiler.md index 188b2d1b2..9d933f219 100644 --- a/src/content/reference/react/Profiler.md +++ b/src/content/reference/react/Profiler.md @@ -4,7 +4,7 @@ title: <Profiler> <Intro> -`<Profiler>` lets you measure rendering performance of a React tree programmatically. +`<Profiler>` cho phép bạn đo lường hiệu suất hiển thị của một cây React một cách программно. ```js <Profiler id="App" onRender={onRender}> @@ -18,11 +18,11 @@ title: <Profiler> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<Profiler>` {/*profiler*/} -Wrap a component tree in a `<Profiler>` to measure its rendering performance. +Bọc một cây thành phần trong một `<Profiler>` để đo lường hiệu suất hiển thị của nó. ```js <Profiler id="App" onRender={onRender}> @@ -32,18 +32,18 @@ Wrap a component tree in a `<Profiler>` to measure its rendering performance. #### Props {/*props*/} -* `id`: A string identifying the part of the UI you are measuring. -* `onRender`: An [`onRender` callback](#onrender-callback) that React calls every time components within the profiled tree update. It receives information about what was rendered and how much time it took. +* `id`: Một chuỗi xác định phần của UI bạn đang đo lường. +* `onRender`: Một [`onRender` callback](#onrender-callback) mà React gọi mỗi khi các thành phần trong cây được профилирование cập nhật. Nó nhận thông tin về những gì đã được hiển thị và mất bao nhiêu thời gian. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Profiling adds some additional overhead, so **it is disabled in the production build by default.** To opt into production profiling, you need to enable a [special production build with profiling enabled.](https://fb.me/react-profiling) +* Hồ sơ thêm một số chi phí bổ sung, vì vậy **nó bị tắt theo mặc định trong bản dựng sản xuất.** Để chọn tham gia lập hồ sơ sản xuất, bạn cần bật [bản dựng sản xuất đặc biệt với hồ sơ được bật.](https://fb.me/react-profiling) --- ### `onRender` callback {/*onrender-callback*/} -React will call your `onRender` callback with information about what was rendered. +React sẽ gọi `onRender` callback của bạn với thông tin về những gì đã được hiển thị. ```js function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime) { @@ -51,22 +51,22 @@ function onRender(id, phase, actualDuration, baseDuration, startTime, commitTime } ``` -#### Parameters {/*onrender-parameters*/} +#### Tham số {/*onrender-parameters*/} -* `id`: The string `id` prop of the `<Profiler>` tree that has just committed. This lets you identify which part of the tree was committed if you are using multiple profilers. -* `phase`: `"mount"`, `"update"` or `"nested-update"`. This lets you know whether the tree has just been mounted for the first time or re-rendered due to a change in props, state, or Hooks. -* `actualDuration`: The number of milliseconds spent rendering the `<Profiler>` and its descendants for the current update. This indicates how well the subtree makes use of memoization (e.g. [`memo`](/reference/react/memo) and [`useMemo`](/reference/react/useMemo)). Ideally this value should decrease significantly after the initial mount as many of the descendants will only need to re-render if their specific props change. -* `baseDuration`: The number of milliseconds estimating how much time it would take to re-render the entire `<Profiler>` subtree without any optimizations. It is calculated by summing up the most recent render durations of each component in the tree. This value estimates a worst-case cost of rendering (e.g. the initial mount or a tree with no memoization). Compare `actualDuration` against it to see if memoization is working. -* `startTime`: A numeric timestamp for when React began rendering the current update. -* `commitTime`: A numeric timestamp for when React committed the current update. This value is shared between all profilers in a commit, enabling them to be grouped if desirable. +* `id`: Chuỗi `id` prop của cây `<Profiler>` vừa cam kết. Điều này cho phép bạn xác định phần nào của cây đã được cam kết nếu bạn đang sử dụng nhiều profiler. +* `phase`: `"mount"`, `"update"` hoặc `"nested-update"`. Điều này cho bạn biết liệu cây vừa được gắn kết lần đầu tiên hay được hiển thị lại do thay đổi trong props, state hoặc Hooks. +* `actualDuration`: Số mili giây đã dành để hiển thị `<Profiler>` và các hậu duệ của nó cho bản cập nhật hiện tại. Điều này cho biết mức độ sử dụng memoization của cây con (ví dụ: [`memo`](/reference/react/memo) và [`useMemo`](/reference/react/useMemo)). Lý tưởng nhất là giá trị này sẽ giảm đáng kể sau lần gắn kết ban đầu vì nhiều hậu duệ sẽ chỉ cần hiển thị lại nếu các props cụ thể của chúng thay đổi. +* `baseDuration`: Số mili giây ước tính thời gian cần thiết để hiển thị lại toàn bộ cây con `<Profiler>` mà không cần bất kỳ tối ưu hóa nào. Nó được tính bằng cách cộng tổng thời gian hiển thị gần đây nhất của mỗi thành phần trong cây. Giá trị này ước tính chi phí trường hợp xấu nhất của việc hiển thị (ví dụ: gắn kết ban đầu hoặc một cây không có memoization). So sánh `actualDuration` với nó để xem memoization có hoạt động không. +* `startTime`: Một dấu thời gian số cho thời điểm React bắt đầu hiển thị bản cập nhật hiện tại. +* `commitTime`: Một dấu thời gian số cho thời điểm React cam kết bản cập nhật hiện tại. Giá trị này được chia sẻ giữa tất cả các profiler trong một cam kết, cho phép chúng được nhóm lại nếu muốn. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Measuring rendering performance programmatically {/*measuring-rendering-performance-programmatically*/} +### Đo lường hiệu suất hiển thị theo программно {/*measuring-rendering-performance-programmatically*/} -Wrap the `<Profiler>` component around a React tree to measure its rendering performance. +Bọc thành phần `<Profiler>` xung quanh một cây React để đo lường hiệu suất hiển thị của nó. ```js {2,4} <App> @@ -77,25 +77,25 @@ Wrap the `<Profiler>` component around a React tree to measure its rendering per </App> ``` -It requires two props: an `id` (string) and an `onRender` callback (function) which React calls any time a component within the tree "commits" an update. +Nó yêu cầu hai props: một `id` (chuỗi) và một `onRender` callback (hàm) mà React gọi bất cứ khi nào một thành phần trong cây "cam kết" một bản cập nhật. <Pitfall> -Profiling adds some additional overhead, so **it is disabled in the production build by default.** To opt into production profiling, you need to enable a [special production build with profiling enabled.](https://fb.me/react-profiling) +Hồ sơ thêm một số chi phí bổ sung, vì vậy **nó bị tắt theo mặc định trong bản dựng sản xuất.** Để chọn tham gia lập hồ sơ sản xuất, bạn cần bật [bản dựng sản xuất đặc biệt với hồ sơ được bật.](https://fb.me/react-profiling) </Pitfall> <Note> -`<Profiler>` lets you gather measurements programmatically. If you're looking for an interactive profiler, try the Profiler tab in [React Developer Tools](/learn/react-developer-tools). It exposes similar functionality as a browser extension. +`<Profiler>` cho phép bạn thu thập các phép đo một cách программно. Nếu bạn đang tìm kiếm một profiler tương tác, hãy thử tab Profiler trong [React Developer Tools](/learn/react-developer-tools). Nó hiển thị chức năng tương tự như một tiện ích mở rộng trình duyệt. </Note> --- -### Measuring different parts of the application {/*measuring-different-parts-of-the-application*/} +### Đo lường các phần khác nhau của ứng dụng {/*measuring-different-parts-of-the-application*/} -You can use multiple `<Profiler>` components to measure different parts of your application: +Bạn có thể sử dụng nhiều thành phần `<Profiler>` để đo lường các phần khác nhau của ứng dụng của bạn: ```js {5,7} <App> @@ -108,7 +108,7 @@ You can use multiple `<Profiler>` components to measure different parts of your </App> ``` -You can also nest `<Profiler>` components: +Bạn cũng có thể lồng các thành phần `<Profiler>`: ```js {5,7,9,12} <App> @@ -126,7 +126,6 @@ You can also nest `<Profiler>` components: </App> ``` -Although `<Profiler>` is a lightweight component, it should be used only when necessary. Each use adds some CPU and memory overhead to an application. +Mặc dù `<Profiler>` là một thành phần nhẹ, nhưng nó chỉ nên được sử dụng khi cần thiết. Mỗi lần sử dụng sẽ thêm một số chi phí CPU và bộ nhớ cho ứng dụng. --- - diff --git a/src/content/reference/react/PureComponent.md b/src/content/reference/react/PureComponent.md index 3e2d074e1..0400ca213 100644 --- a/src/content/reference/react/PureComponent.md +++ b/src/content/reference/react/PureComponent.md @@ -4,18 +4,18 @@ title: PureComponent <Pitfall> -We recommend defining components as functions instead of classes. [See how to migrate.](#alternatives) +Chúng tôi khuyên bạn nên định nghĩa các component dưới dạng function thay vì class. [Xem cách migrate.](#alternatives) </Pitfall> <Intro> -`PureComponent` is similar to [`Component`](/reference/react/Component) but it skips re-renders for same props and state. Class components are still supported by React, but we don't recommend using them in new code. +`PureComponent` tương tự như [`Component`](/reference/react/Component) nhưng nó bỏ qua việc re-render khi props và state giống nhau. Các component class vẫn được React hỗ trợ, nhưng chúng tôi khuyên bạn không nên sử dụng chúng trong code mới. ```js class Greeting extends PureComponent { render() { - return <h1>Hello, {this.props.name}!</h1>; + return <h1>Xin chào, {this.props.name}!</h1>; } } ``` @@ -26,46 +26,45 @@ class Greeting extends PureComponent { --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `PureComponent` {/*purecomponent*/} -To skip re-rendering a class component for same props and state, extend `PureComponent` instead of [`Component`:](/reference/react/Component) +Để bỏ qua việc re-render một class component khi props và state giống nhau, hãy kế thừa `PureComponent` thay vì [`Component`:](/reference/react/Component) ```js import { PureComponent } from 'react'; class Greeting extends PureComponent { render() { - return <h1>Hello, {this.props.name}!</h1>; + return <h1>Xin chào, {this.props.name}!</h1>; } } ``` -`PureComponent` is a subclass of `Component` and supports [all the `Component` APIs.](/reference/react/Component#reference) Extending `PureComponent` is equivalent to defining a custom [`shouldComponentUpdate`](/reference/react/Component#shouldcomponentupdate) method that shallowly compares props and state. +`PureComponent` là một lớp con của `Component` và hỗ trợ [tất cả các API của `Component`.](/reference/react/Component#reference) Kế thừa `PureComponent` tương đương với việc định nghĩa một phương thức [`shouldComponentUpdate`](/reference/react/Component#shouldcomponentupdate) tùy chỉnh để so sánh nông props và state. - -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Skipping unnecessary re-renders for class components {/*skipping-unnecessary-re-renders-for-class-components*/} +### Bỏ qua các re-render không cần thiết cho class component {/*skipping-unnecessary-re-renders-for-class-components*/} -React normally re-renders a component whenever its parent re-renders. As an optimization, you can create a component that React will not re-render when its parent re-renders so long as its new props and state are the same as the old props and state. [Class components](/reference/react/Component) can opt into this behavior by extending `PureComponent`: +React thường re-render một component bất cứ khi nào parent của nó re-render. Để tối ưu hóa, bạn có thể tạo một component mà React sẽ không re-render khi parent của nó re-render, miễn là các props và state mới của nó giống với các props và state cũ. [Class component](/reference/react/Component) có thể chọn tham gia hành vi này bằng cách kế thừa `PureComponent`: ```js {1} class Greeting extends PureComponent { render() { - return <h1>Hello, {this.props.name}!</h1>; + return <h1>Xin chào, {this.props.name}!</h1>; } } ``` -A React component should always have [pure rendering logic.](/learn/keeping-components-pure) This means that it must return the same output if its props, state, and context haven't changed. By using `PureComponent`, you are telling React that your component complies with this requirement, so React doesn't need to re-render as long as its props and state haven't changed. However, your component will still re-render if a context that it's using changes. +Một React component phải luôn có [logic render thuần túy.](/learn/keeping-components-pure) Điều này có nghĩa là nó phải trả về cùng một đầu ra nếu props, state và context của nó không thay đổi. Bằng cách sử dụng `PureComponent`, bạn đang nói với React rằng component của bạn tuân thủ yêu cầu này, vì vậy React không cần phải re-render miễn là props và state của nó không thay đổi. Tuy nhiên, component của bạn vẫn sẽ re-render nếu một context mà nó đang sử dụng thay đổi. -In this example, notice that the `Greeting` component re-renders whenever `name` is changed (because that's one of its props), but not when `address` is changed (because it's not passed to `Greeting` as a prop): +Trong ví dụ này, hãy để ý rằng component `Greeting` re-render bất cứ khi nào `name` thay đổi (vì đó là một trong các props của nó), nhưng không re-render khi `address` thay đổi (vì nó không được truyền cho `Greeting` dưới dạng một prop): <Sandpack> @@ -75,7 +74,7 @@ import { PureComponent, useState } from 'react'; class Greeting extends PureComponent { render() { console.log("Greeting was rendered at", new Date().toLocaleTimeString()); - return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>; + return <h3>Xin chào{this.props.name && ', '}{this.props.name}!</h3>; } } @@ -85,11 +84,11 @@ export default function MyApp() { return ( <> <label> - Name{': '} + Tên{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> - Address{': '} + Địa chỉ{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> @@ -109,17 +108,17 @@ label { <Pitfall> -We recommend defining components as functions instead of classes. [See how to migrate.](#alternatives) +Chúng tôi khuyên bạn nên định nghĩa các component dưới dạng function thay vì class. [Xem cách migrate.](#alternatives) </Pitfall> --- -## Alternatives {/*alternatives*/} +## Các lựa chọn thay thế {/*alternatives*/} -### Migrating from a `PureComponent` class component to a function {/*migrating-from-a-purecomponent-class-component-to-a-function*/} +### Migrate từ một class component `PureComponent` sang một function {/*migrating-from-a-purecomponent-class-component-to-a-function*/} -We recommend using function components instead of [class components](/reference/react/Component) in new code. If you have some existing class components using `PureComponent`, here is how you can convert them. This is the original code: +Chúng tôi khuyên bạn nên sử dụng function component thay vì [class component](/reference/react/Component) trong code mới. Nếu bạn có một số class component hiện có đang sử dụng `PureComponent`, đây là cách bạn có thể chuyển đổi chúng. Đây là code gốc: <Sandpack> @@ -129,7 +128,7 @@ import { PureComponent, useState } from 'react'; class Greeting extends PureComponent { render() { console.log("Greeting was rendered at", new Date().toLocaleTimeString()); - return <h3>Hello{this.props.name && ', '}{this.props.name}!</h3>; + return <h3>Xin chào{this.props.name && ', '}{this.props.name}!</h3>; } } @@ -139,11 +138,11 @@ export default function MyApp() { return ( <> <label> - Name{': '} + Tên{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> - Address{': '} + Địa chỉ{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> @@ -161,7 +160,7 @@ label { </Sandpack> -When you [convert this component from a class to a function,](/reference/react/Component#alternatives) wrap it in [`memo`:](/reference/react/memo) +Khi bạn [chuyển đổi component này từ một class sang một function,](/reference/react/Component#alternatives) hãy bọc nó trong [`memo`:](/reference/react/memo) <Sandpack> @@ -170,7 +169,7 @@ import { memo, useState } from 'react'; const Greeting = memo(function Greeting({ name }) { console.log("Greeting was rendered at", new Date().toLocaleTimeString()); - return <h3>Hello{name && ', '}{name}!</h3>; + return <h3>Xin chào{name && ', '}{name}!</h3>; }); export default function MyApp() { @@ -179,11 +178,11 @@ export default function MyApp() { return ( <> <label> - Name{': '} + Tên{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> - Address{': '} + Địa chỉ{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> @@ -203,6 +202,6 @@ label { <Note> -Unlike `PureComponent`, [`memo`](/reference/react/memo) does not compare the new and the old state. In function components, calling the [`set` function](/reference/react/useState#setstate) with the same state [already prevents re-renders by default,](/reference/react/memo#updating-a-memoized-component-using-state) even without `memo`. +Không giống như `PureComponent`, [`memo`](/reference/react/memo) không so sánh state mới và state cũ. Trong function component, việc gọi function [`set`](/reference/react/useState#setstate) với cùng một state [đã ngăn chặn việc re-render theo mặc định,](/reference/react/memo#updating-a-memoized-component-using-state) ngay cả khi không có `memo`. </Note> diff --git a/src/content/reference/react/StrictMode.md b/src/content/reference/react/StrictMode.md index 1af02ccd1..95be7b5de 100644 --- a/src/content/reference/react/StrictMode.md +++ b/src/content/reference/react/StrictMode.md @@ -2,11 +2,9 @@ title: <StrictMode> --- - <Intro> -`<StrictMode>` lets you find common bugs in your components early during development. - +`<StrictMode>` cho phép bạn tìm các lỗi phổ biến trong các component của mình sớm trong quá trình phát triển. ```js <StrictMode> @@ -20,11 +18,11 @@ title: <StrictMode> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<StrictMode>` {/*strictmode*/} -Use `StrictMode` to enable additional development behaviors and warnings for the component tree inside: +Sử dụng `StrictMode` để bật các hành vi và cảnh báo phát triển bổ sung cho cây component bên trong: ```js import { StrictMode } from 'react'; @@ -38,33 +36,34 @@ root.render( ); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -Strict Mode enables the following development-only behaviors: +Strict Mode kích hoạt các hành vi chỉ dành cho quá trình phát triển sau: -- Your components will [re-render an extra time](#fixing-bugs-found-by-double-rendering-in-development) to find bugs caused by impure rendering. -- Your components will [re-run Effects an extra time](#fixing-bugs-found-by-re-running-effects-in-development) to find bugs caused by missing Effect cleanup. -- Your components will [re-run refs callbacks an extra time](#fixing-bugs-found-by-re-running-ref-callbacks-in-development) to find bugs caused by missing ref cleanup. -- Your components will [be checked for usage of deprecated APIs.](#fixing-deprecation-warnings-enabled-by-strict-mode) +- Các component của bạn sẽ [render lại thêm một lần](#fixing-bugs-found-by-double-rendering-in-development) để tìm các lỗi do quá trình render không thuần túy gây ra. +- Các component của bạn sẽ [chạy lại Effects thêm một lần](#fixing-bugs-found-by-re-running-effects-in-development) để tìm các lỗi do thiếu dọn dẹp Effect. +- Các component của bạn sẽ [chạy lại các callback ref thêm một lần](#fixing-bugs-found-by-re-running-ref-callbacks-in-development) để tìm các lỗi do thiếu dọn dẹp ref. +- Các component của bạn sẽ [được kiểm tra việc sử dụng các API không dùng nữa.](#fixing-deprecation-warnings-enabled-by-strict-mode) #### Props {/*props*/} -`StrictMode` accepts no props. +`StrictMode` không chấp nhận props nào. -#### Caveats {/*caveats*/} +#### Lưu ý {/*lưu-ý*/} -* There is no way to opt out of Strict Mode inside a tree wrapped in `<StrictMode>`. This gives you confidence that all components inside `<StrictMode>` are checked. If two teams working on a product disagree whether they find the checks valuable, they need to either reach consensus or move `<StrictMode>` down in the tree. +#### Lưu ý {/*caveats*/} ---- +* Không có cách nào để tắt Strict Mode bên trong một cây được bọc trong `<StrictMode>`. Điều này giúp bạn tin tưởng rằng tất cả các component bên trong `<StrictMode>` đều được kiểm tra. Nếu hai nhóm làm việc trên một sản phẩm không đồng ý về việc họ thấy các kiểm tra có giá trị hay không, họ cần đạt được sự đồng thuận hoặc di chuyển `<StrictMode>` xuống trong cây. -## Usage {/*usage*/} +--- -### Enabling Strict Mode for entire app {/*enabling-strict-mode-for-entire-app*/} +## Cách sử dụng {/*usage*/} -Strict Mode enables extra development-only checks for the entire component tree inside the `<StrictMode>` component. These checks help you find common bugs in your components early in the development process. +### Bật Strict Mode cho toàn bộ ứng dụng {/*enabling-strict-mode-for-entire-app*/} +Strict Mode cho phép các kiểm tra chỉ dành cho quá trình phát triển đối với toàn bộ cây component bên trong component `<StrictMode>`. Các kiểm tra này giúp bạn tìm thấy các lỗi phổ biến trong các component của bạn sớm trong quá trình phát triển. -To enable Strict Mode for your entire app, wrap your root component with `<StrictMode>` when you render it: +Để bật Strict Mode cho toàn bộ ứng dụng của bạn, hãy bọc component gốc của bạn bằng `<StrictMode>` khi bạn render nó: ```js {6,8} import { StrictMode } from 'react'; @@ -78,28 +77,27 @@ root.render( ); ``` -We recommend wrapping your entire app in Strict Mode, especially for newly created apps. If you use a framework that calls [`createRoot`](/reference/react-dom/client/createRoot) for you, check its documentation for how to enable Strict Mode. +Chúng tôi khuyên bạn nên bọc toàn bộ ứng dụng của mình trong Strict Mode, đặc biệt đối với các ứng dụng mới tạo. Nếu bạn sử dụng một framework gọi [`createRoot`](/reference/react-dom/client/createRoot) cho bạn, hãy kiểm tra tài liệu của nó để biết cách bật Strict Mode. -Although the Strict Mode checks **only run in development,** they help you find bugs that already exist in your code but can be tricky to reliably reproduce in production. Strict Mode lets you fix bugs before your users report them. +Mặc dù các kiểm tra của Strict Mode **chỉ chạy trong quá trình phát triển,** nhưng chúng giúp bạn tìm thấy các lỗi đã tồn tại trong code của bạn nhưng có thể khó tái tạo một cách đáng tin cậy trong production. Strict Mode cho phép bạn sửa lỗi trước khi người dùng của bạn báo cáo chúng. <Note> -Strict Mode enables the following checks in development: +Strict Mode cho phép các kiểm tra sau trong quá trình phát triển: -- Your components will [re-render an extra time](#fixing-bugs-found-by-double-rendering-in-development) to find bugs caused by impure rendering. -- Your components will [re-run Effects an extra time](#fixing-bugs-found-by-re-running-effects-in-development) to find bugs caused by missing Effect cleanup. -- Your components will [re-run ref callbacks an extra time](#fixing-bugs-found-by-re-running-ref-callbacks-in-development) to find bugs caused by missing ref cleanup. -- Your components will [be checked for usage of deprecated APIs.](#fixing-deprecation-warnings-enabled-by-strict-mode) +- Các component của bạn sẽ [render lại thêm một lần](#fixing-bugs-found-by-double-rendering-in-development) để tìm các lỗi do quá trình render không thuần túy gây ra. +- Các component của bạn sẽ [chạy lại Effects thêm một lần](#fixing-bugs-found-by-re-running-effects-in-development) để tìm các lỗi do thiếu dọn dẹp Effect. +- Các component của bạn sẽ [chạy lại các callback ref thêm một lần](#fixing-bugs-found-by-re-running-ref-callbacks-in-development) để tìm các lỗi do thiếu dọn dẹp ref. +- Các component của bạn sẽ [được kiểm tra việc sử dụng các API không dùng nữa.](#fixing-deprecation-warnings-enabled-by-strict-mode) -**All of these checks are development-only and do not impact the production build.** +**Tất cả các kiểm tra này chỉ dành cho quá trình phát triển và không ảnh hưởng đến bản dựng production.** </Note> --- +### Bật Strict Mode cho một phần của ứng dụng {/*enabling-strict-mode-for-a-part-of-the-app*/} -### Enabling Strict Mode for a part of the app {/*enabling-strict-mode-for-a-part-of-the-app*/} - -You can also enable Strict Mode for any part of your application: +Bạn cũng có thể bật Strict Mode cho bất kỳ phần nào của ứng dụng của bạn: ```js {7,12} import { StrictMode } from 'react'; @@ -120,31 +118,30 @@ function App() { } ``` -In this example, Strict Mode checks will not run against the `Header` and `Footer` components. However, they will run on `Sidebar` and `Content`, as well as all of the components inside them, no matter how deep. +Trong ví dụ này, các kiểm tra của Strict Mode sẽ không chạy trên các component `Header` và `Footer`. Tuy nhiên, chúng sẽ chạy trên `Sidebar` và `Content`, cũng như tất cả các component bên trong chúng, bất kể độ sâu. <Note> -When `StrictMode` is enabled for a part of the app, React will only enable behaviors that are possible in production. For example, if `<StrictMode>` is not enabled at the root of the app, it will not [re-run Effects an extra time](#fixing-bugs-found-by-re-running-effects-in-development) on initial mount, since this would cause child effects to double fire without the parent effects, which cannot happen in production. +Khi `StrictMode` được bật cho một phần của ứng dụng, React sẽ chỉ bật các hành vi có thể xảy ra trong production. Ví dụ: nếu `<StrictMode>` không được bật ở gốc của ứng dụng, nó sẽ không [chạy lại Effects thêm một lần](#fixing-bugs-found-by-re-running-effects-in-development) khi mount ban đầu, vì điều này sẽ khiến các effect con bị kích hoạt gấp đôi mà không có các effect cha, điều này không thể xảy ra trong production. </Note> --- +### Khắc phục các lỗi được tìm thấy bằng cách render hai lần trong quá trình phát triển {/*fixing-bugs-found-by-double-rendering-in-development*/} -### Fixing bugs found by double rendering in development {/*fixing-bugs-found-by-double-rendering-in-development*/} - -[React assumes that every component you write is a pure function.](/learn/keeping-components-pure) This means that React components you write must always return the same JSX given the same inputs (props, state, and context). +[React giả định rằng mọi component bạn viết là một hàm thuần túy.](/learn/keeping-components-pure) Điều này có nghĩa là các component React bạn viết phải luôn trả về cùng một JSX với cùng một đầu vào (props, state và context). -Components breaking this rule behave unpredictably and cause bugs. To help you find accidentally impure code, Strict Mode calls some of your functions (only the ones that should be pure) **twice in development.** This includes: +Các component vi phạm quy tắc này hoạt động không thể đoán trước và gây ra lỗi. Để giúp bạn tìm thấy code không thuần túy một cách vô tình, Strict Mode gọi một số hàm của bạn (chỉ những hàm được cho là thuần túy) **hai lần trong quá trình phát triển.** Điều này bao gồm: -- Your component function body (only top-level logic, so this doesn't include code inside event handlers) -- Functions that you pass to [`useState`](/reference/react/useState), [`set` functions](/reference/react/useState#setstate), [`useMemo`](/reference/react/useMemo), or [`useReducer`](/reference/react/useReducer) -- Some class component methods like [`constructor`](/reference/react/Component#constructor), [`render`](/reference/react/Component#render), [`shouldComponentUpdate`](/reference/react/Component#shouldcomponentupdate) ([see the whole list](https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects)) +- Phần thân hàm component của bạn (chỉ logic cấp cao nhất, vì vậy điều này không bao gồm code bên trong trình xử lý sự kiện) +- Các hàm mà bạn chuyển cho [`useState`](/reference/react/useState), các hàm [`set`](/reference/react/useState#setstate), [`useMemo`](/reference/react/useMemo) hoặc [`useReducer`](/reference/react/useReducer) +- Một số phương thức component class như [`constructor`](/reference/react/Component#constructor), [`render`](/reference/react/Component#render), [`shouldComponentUpdate`](/reference/react/Component#shouldcomponentupdate) ([xem toàn bộ danh sách](https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects)) -If a function is pure, running it twice does not change its behavior because a pure function produces the same result every time. However, if a function is impure (for example, it mutates the data it receives), running it twice tends to be noticeable (that's what makes it impure!) This helps you spot and fix the bug early. +Nếu một hàm là thuần túy, việc chạy nó hai lần không thay đổi hành vi của nó vì một hàm thuần túy tạo ra cùng một kết quả mỗi lần. Tuy nhiên, nếu một hàm là không thuần túy (ví dụ: nó thay đổi dữ liệu mà nó nhận được), việc chạy nó hai lần có xu hướng dễ nhận thấy (đó là điều khiến nó không thuần túy!) Điều này giúp bạn phát hiện và sửa lỗi sớm. -**Here is an example to illustrate how double rendering in Strict Mode helps you find bugs early.** +**Đây là một ví dụ để minh họa cách render hai lần trong Strict Mode giúp bạn tìm lỗi sớm.** -This `StoryTray` component takes an array of `stories` and adds one last "Create Story" item at the end: +Component `StoryTray` này lấy một mảng `stories` và thêm một mục "Create Story" cuối cùng vào cuối: <Sandpack> @@ -311,9 +308,9 @@ li { </Sandpack> -Notice how every time you hover over the `StoryTray` component, "Create Story" gets added to the list again. The intention of the code was to add it once at the end. But `StoryTray` directly modifies the `stories` array from the props. Every time `StoryTray` renders, it adds "Create Story" again at the end of the same array. In other words, `StoryTray` is not a pure function--running it multiple times produces different results. +Lưu ý rằng mỗi khi bạn di chuột qua component `StoryTray`, "Create Story" sẽ được thêm lại vào danh sách. Mục đích của code là thêm nó một lần ở cuối. Nhưng `StoryTray` sửa đổi trực tiếp mảng `stories` từ props. Mỗi khi `StoryTray` render, nó lại thêm "Create Story" vào cuối cùng của cùng một mảng. Nói cách khác, `StoryTray` không phải là một hàm thuần túy - việc chạy nó nhiều lần sẽ tạo ra các kết quả khác nhau. -To fix this problem, you can make a copy of the array, and modify that copy instead of the original one: +Để khắc phục sự cố này, bạn có thể tạo một bản sao của mảng và sửa đổi bản sao đó thay vì bản gốc: ```js {2} export default function StoryTray({ stories }) { @@ -322,9 +319,9 @@ export default function StoryTray({ stories }) { items.push({ id: 'create', label: 'Create Story' }); ``` -This would [make the `StoryTray` function pure.](/learn/keeping-components-pure) Each time it is called, it would only modify a new copy of the array, and would not affect any external objects or variables. This solves the bug, but you had to make the component re-render more often before it became obvious that something is wrong with its behavior. +Điều này sẽ [làm cho hàm `StoryTray` trở nên thuần khiết.](/learn/keeping-components-pure) Mỗi khi nó được gọi, nó sẽ chỉ sửa đổi một bản sao mới của mảng và sẽ không ảnh hưởng đến bất kỳ đối tượng hoặc biến bên ngoài nào. Điều này giải quyết lỗi, nhưng bạn phải làm cho component render lại thường xuyên hơn trước khi nó trở nên rõ ràng rằng có điều gì đó không ổn với hành vi của nó. -**In the original example, the bug wasn't obvious. Now let's wrap the original (buggy) code in `<StrictMode>`:** +**Trong ví dụ ban đầu, lỗi không rõ ràng. Bây giờ, hãy bọc mã gốc (có lỗi) trong `<StrictMode>`:** <Sandpack> @@ -407,7 +404,7 @@ li { </Sandpack> -**Strict Mode *always* calls your rendering function twice, so you can see the mistake right away** ("Create Story" appears twice). This lets you notice such mistakes early in the process. When you fix your component to render in Strict Mode, you *also* fix many possible future production bugs like the hover functionality from before: +**Strict Mode *luôn* gọi hàm render của bạn hai lần, vì vậy bạn có thể thấy lỗi ngay lập tức** ("Create Story" xuất hiện hai lần). Điều này cho phép bạn nhận thấy những sai sót như vậy sớm trong quá trình này. Khi bạn sửa component của mình để render trong Strict Mode, bạn *cũng* sửa nhiều lỗi sản xuất có thể xảy ra trong tương lai như chức năng di chuột từ trước: <Sandpack> @@ -499,29 +496,29 @@ li { </Sandpack> -Without Strict Mode, it was easy to miss the bug until you added more re-renders. Strict Mode made the same bug appear right away. Strict Mode helps you find bugs before you push them to your team and to your users. +Nếu không có Strict Mode, bạn có thể dễ dàng bỏ lỡ lỗi cho đến khi bạn thêm nhiều lần render lại. Strict Mode làm cho lỗi tương tự xuất hiện ngay lập tức. Strict Mode giúp bạn tìm lỗi trước khi bạn đẩy chúng cho nhóm của mình và cho người dùng của bạn. -[Read more about keeping components pure.](/learn/keeping-components-pure) +[Đọc thêm về cách giữ cho các component thuần khiết.](/learn/keeping-components-pure) <Note> -If you have [React DevTools](/learn/react-developer-tools) installed, any `console.log` calls during the second render call will appear slightly dimmed. React DevTools also offers a setting (off by default) to suppress them completely. - +Nếu bạn đã cài đặt [React DevTools](/learn/react-developer-tools), bất kỳ lệnh gọi `console.log` nào trong lần gọi render thứ hai sẽ xuất hiện hơi mờ. React DevTools cũng cung cấp một cài đặt (tắt theo mặc định) để tắt chúng hoàn toàn. </Note> ---- -### Fixing bugs found by re-running Effects in development {/*fixing-bugs-found-by-re-running-effects-in-development*/} -Strict Mode can also help find bugs in [Effects.](/learn/synchronizing-with-effects) +--- +### Khắc phục các lỗi được tìm thấy bằng cách chạy lại Effects trong quá trình phát triển {/*fixing-bugs-found-by-re-running-effects-in-development*/} + +Strict Mode cũng có thể giúp tìm các lỗi trong [Effects.](/learn/synchronizing-with-effects) -Every Effect has some setup code and may have some cleanup code. Normally, React calls setup when the component *mounts* (is added to the screen) and calls cleanup when the component *unmounts* (is removed from the screen). React then calls cleanup and setup again if its dependencies changed since the last render. +Mỗi Effect có một số mã thiết lập và có thể có một số mã dọn dẹp. Thông thường, React gọi thiết lập khi component *mounts* (được thêm vào màn hình) và gọi dọn dẹp khi component *unmounts* (bị xóa khỏi màn hình). Sau đó, React gọi dọn dẹp và thiết lập lại nếu các dependencies của nó thay đổi kể từ lần render cuối cùng. -When Strict Mode is on, React will also run **one extra setup+cleanup cycle in development for every Effect.** This may feel surprising, but it helps reveal subtle bugs that are hard to catch manually. +Khi Strict Mode được bật, React cũng sẽ chạy **một chu kỳ thiết lập + dọn dẹp bổ sung trong quá trình phát triển cho mỗi Effect.** Điều này có vẻ đáng ngạc nhiên, nhưng nó giúp tiết lộ những lỗi nhỏ khó bắt gặp theo cách thủ công. -**Here is an example to illustrate how re-running Effects in Strict Mode helps you find bugs early.** +**Đây là một ví dụ để minh họa cách chạy lại Effects trong Strict Mode giúp bạn tìm lỗi sớm.** -Consider this example that connects a component to a chat: +Hãy xem xét ví dụ này kết nối một component với một cuộc trò chuyện: <Sandpack> @@ -578,9 +575,9 @@ button { margin-left: 10px; } </Sandpack> -There is an issue with this code, but it might not be immediately clear. +Có một vấn đề với mã này, nhưng nó có thể không rõ ràng ngay lập tức. -To make the issue more obvious, let's implement a feature. In the example below, `roomId` is not hardcoded. Instead, the user can select the `roomId` that they want to connect to from a dropdown. Click "Open chat" and then select different chat rooms one by one. Keep track of the number of active connections in the console: +Để làm cho vấn đề trở nên rõ ràng hơn, hãy triển khai một tính năng. Trong ví dụ dưới đây, `roomId` không được mã hóa cứng. Thay vào đó, người dùng có thể chọn `roomId` mà họ muốn kết nối từ một danh sách thả xuống. Nhấp vào "Open chat" và sau đó chọn các phòng chat khác nhau từng cái một. Theo dõi số lượng kết nối đang hoạt động trong bảng điều khiển: <Sandpack> @@ -662,7 +659,7 @@ button { margin-left: 10px; } </Sandpack> -You'll notice that the number of open connections always keeps growing. In a real app, this would cause performance and network problems. The issue is that [your Effect is missing a cleanup function:](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) +Bạn sẽ nhận thấy rằng số lượng kết nối đang mở luôn tăng lên. Trong một ứng dụng thực tế, điều này sẽ gây ra các vấn đề về hiệu suất và mạng. Vấn đề là [Effect của bạn đang thiếu một hàm dọn dẹp:](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) ```js {4} useEffect(() => { @@ -672,9 +669,9 @@ You'll notice that the number of open connections always keeps growing. In a rea }, [roomId]); ``` -Now that your Effect "cleans up" after itself and destroys the outdated connections, the leak is solved. However, notice that the problem did not become visible until you've added more features (the select box). +Giờ đây, Effect của bạn "dọn dẹp" sau chính nó và phá hủy các kết nối lỗi thời, rò rỉ đã được giải quyết. Tuy nhiên, hãy lưu ý rằng sự cố không trở nên rõ ràng cho đến khi bạn thêm nhiều tính năng hơn (hộp chọn). -**In the original example, the bug wasn't obvious. Now let's wrap the original (buggy) code in `<StrictMode>`:** +**Trong ví dụ ban đầu, lỗi không rõ ràng. Bây giờ, hãy bọc mã gốc (có lỗi) trong `<StrictMode>`:** <Sandpack> @@ -736,9 +733,9 @@ button { margin-left: 10px; } </Sandpack> -**With Strict Mode, you immediately see that there is a problem** (the number of active connections jumps to 2). Strict Mode runs an extra setup+cleanup cycle for every Effect. This Effect has no cleanup logic, so it creates an extra connection but doesn't destroy it. This is a hint that you're missing a cleanup function. +**Với Strict Mode, bạn sẽ thấy ngay rằng có một vấn đề** (số lượng kết nối đang hoạt động tăng lên 2). Strict Mode chạy thêm một chu kỳ thiết lập + dọn dẹp cho mỗi Effect. Effect này không có logic dọn dẹp, vì vậy nó tạo thêm một kết nối nhưng không hủy nó. Đây là một gợi ý rằng bạn đang thiếu một hàm dọn dẹp. -Strict Mode lets you notice such mistakes early in the process. When you fix your Effect by adding a cleanup function in Strict Mode, you *also* fix many possible future production bugs like the select box from before: +Strict Mode cho phép bạn nhận thấy những sai sót như vậy sớm trong quá trình này. Khi bạn sửa Effect của mình bằng cách thêm một hàm dọn dẹp trong Strict Mode, bạn *cũng* sửa nhiều lỗi sản xuất có thể xảy ra trong tương lai như hộp chọn từ trước: <Sandpack> @@ -826,22 +823,23 @@ button { margin-left: 10px; } </Sandpack> -Notice how the active connection count in the console doesn't keep growing anymore. +Bạn sẽ thấy rằng số lượng kết nối đang hoạt động trong bảng điều khiển không còn tăng lên nữa. -Without Strict Mode, it was easy to miss that your Effect needed cleanup. By running *setup → cleanup → setup* instead of *setup* for your Effect in development, Strict Mode made the missing cleanup logic more noticeable. +Nếu không có Strict Mode, bạn có thể dễ dàng bỏ lỡ việc Effect của bạn cần dọn dẹp. Bằng cách chạy *thiết lập → dọn dẹp → thiết lập* thay vì *thiết lập* cho Effect của bạn trong quá trình phát triển, Strict Mode giúp bạn dễ nhận thấy logic dọn dẹp bị thiếu hơn. -[Read more about implementing Effect cleanup.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +[Đọc thêm về cách triển khai dọn dẹp Effect.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) --- -### Fixing bugs found by re-running ref callbacks in development {/*fixing-bugs-found-by-re-running-ref-callbacks-in-development*/} -Strict Mode can also help find bugs in [callbacks refs.](/learn/manipulating-the-dom-with-refs) +### Khắc phục các lỗi được tìm thấy bằng cách chạy lại các callback ref trong quá trình phát triển {/*fixing-bugs-found-by-re-running-ref-callbacks-in-development*/} + +Strict Mode cũng có thể giúp tìm các lỗi trong [callback refs.](/learn/manipulating-the-dom-with-refs) -Every callback `ref` has some setup code and may have some cleanup code. Normally, React calls setup when the element is *created* (is added to the DOM) and calls cleanup when the element is *removed* (is removed from the DOM). +Mỗi callback `ref` có một số mã thiết lập và có thể có một số mã dọn dẹp. Thông thường, React gọi thiết lập khi phần tử được *tạo* (được thêm vào DOM) và gọi dọn dẹp khi phần tử bị *xóa* (bị xóa khỏi DOM). -When Strict Mode is on, React will also run **one extra setup+cleanup cycle in development for every callback `ref`.** This may feel surprising, but it helps reveal subtle bugs that are hard to catch manually. +Khi Strict Mode được bật, React cũng sẽ chạy **một chu kỳ thiết lập + dọn dẹp bổ sung trong quá trình phát triển cho mỗi callback `ref`.** Điều này có vẻ đáng ngạc nhiên, nhưng nó giúp tiết lộ những lỗi nhỏ khó bắt gặp theo cách thủ công. -Consider this example, which allows you to select an animal and then scroll to one of them. Notice when you switch from "Cats" to "Dogs", the console logs show that the number of animals in the list keeps growing, and the "Scroll to" buttons stop working: +Hãy xem xét ví dụ này, cho phép bạn chọn một animal và sau đó cuộn đến một trong số chúng. Lưu ý khi bạn chuyển từ "Cats" sang "Dogs", nhật ký bảng điều khiển cho thấy số lượng animal trong danh sách tiếp tục tăng lên và các nút "Scroll to" ngừng hoạt động: <Sandpack> @@ -961,9 +959,9 @@ li { </Sandpack> -**This is a production bug!** Since the ref callback doesn't remove animals from the list in the cleanup, the list of animals keeps growing. This is a memory leak that can cause performance problems in a real app, and breaks the behavior of the app. +**Đây là một lỗi sản xuất!** Vì callback ref không xóa các animal khỏi danh sách trong quá trình dọn dẹp, danh sách các animal tiếp tục tăng lên. Đây là một rò rỉ bộ nhớ có thể gây ra các vấn đề về hiệu suất trong một ứng dụng thực tế và phá vỡ hành vi của ứng dụng. -The issue is the ref callback doesn't cleanup after itself: +Vấn đề là callback ref không tự dọn dẹp: ```js {6-8} <li @@ -1102,9 +1100,9 @@ li { </Sandpack> -**With Strict Mode, you immediately see that there is a problem**. Strict Mode runs an extra setup+cleanup cycle for every callback ref. This callback ref has no cleanup logic, so it adds refs but doesn't remove them. This is a hint that you're missing a cleanup function. +**Với Strict Mode, bạn sẽ thấy ngay rằng có một vấn đề**. Strict Mode chạy thêm một chu kỳ thiết lập + dọn dẹp cho mỗi callback ref. Callback ref này không có logic dọn dẹp, vì vậy nó thêm ref nhưng không xóa chúng. Đây là một gợi ý rằng bạn đang thiếu một hàm dọn dẹp. -Strict Mode lets you eagerly find mistakes in callback refs. When you fix your callback by adding a cleanup function in Strict Mode, you *also* fix many possible future production bugs like the "Scroll to" bug from before: +Strict Mode cho phép bạn tìm thấy những sai sót trong callback ref một cách nhanh chóng. Khi bạn sửa callback của mình bằng cách thêm một hàm dọn dẹp trong Strict Mode, bạn *cũng* sửa nhiều lỗi sản xuất có thể xảy ra trong tương lai như lỗi "Scroll to" từ trước: <Sandpack> @@ -1228,8 +1226,7 @@ li { ``` </Sandpack> - -Now on inital mount in StrictMode, the ref callbacks are all setup, cleaned up, and setup again: +Giờ đây, khi gắn kết ban đầu trong StrictMode, tất cả các callback ref đều được thiết lập, dọn dẹp và thiết lập lại: ``` ... @@ -1240,15 +1237,16 @@ Now on inital mount in StrictMode, the ref callbacks are all setup, cleaned up, ✅ Adding animal to the map. Total animals: 10 ``` -**This is expected.** Strict Mode confirms that the ref callbacks are cleaned up correctly, so the size never grows above the expected amount. After the fix, there are no memory leaks, and all the features work as expected. +**Điều này là mong đợi.** Strict Mode xác nhận rằng các callback ref được dọn dẹp chính xác, vì vậy kích thước không bao giờ tăng trên mức dự kiến. Sau khi sửa lỗi, không có rò rỉ bộ nhớ và tất cả các tính năng hoạt động như mong đợi. + +Nếu không có Strict Mode, bạn có thể dễ dàng bỏ lỡ lỗi cho đến khi bạn nhấp vào ứng dụng để nhận thấy các tính năng bị hỏng. Strict Mode làm cho các lỗi xuất hiện ngay lập tức, trước khi bạn đẩy chúng vào sản xuất. -Without Strict Mode, it was easy to miss the bug until you clicked around to app to notice broken features. Strict Mode made the bugs appear right away, before you push them to production. +--- ---- -### Fixing deprecation warnings enabled by Strict Mode {/*fixing-deprecation-warnings-enabled-by-strict-mode*/} +### Khắc phục các cảnh báo không dùng nữa được bật bởi Strict Mode {/*fixing-deprecation-warnings-enabled-by-strict-mode*/} -React warns if some component anywhere inside a `<StrictMode>` tree uses one of these deprecated APIs: +React cảnh báo nếu một số thành phần ở bất kỳ đâu bên trong cây `<StrictMode>` sử dụng một trong các API không dùng nữa này: -* `UNSAFE_` class lifecycle methods like [`UNSAFE_componentWillMount`](/reference/react/Component#unsafe_componentwillmount). [See alternatives.](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#migrating-from-legacy-lifecycles) +* Các phương thức vòng đời lớp `UNSAFE_` như [`UNSAFE_componentWillMount`](/reference/react/Component#unsafe_componentwillmount). [Xem các lựa chọn thay thế.](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#migrating-from-legacy-lifecycles) -These APIs are primarily used in older [class components](/reference/react/Component) so they rarely appear in modern apps. +Các API này chủ yếu được sử dụng trong các [thành phần lớp](/reference/react/Component) cũ hơn, vì vậy chúng hiếm khi xuất hiện trong các ứng dụng hiện đại. diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md index 4fce69d69..559583fd3 100644 --- a/src/content/reference/react/Suspense.md +++ b/src/content/reference/react/Suspense.md @@ -4,7 +4,7 @@ title: <Suspense> <Intro> -`<Suspense>` lets you display a fallback until its children have finished loading. +`<Suspense>` cho phép bạn hiển thị một fallback cho đến khi các thành phần con của nó đã tải xong. ```js @@ -19,28 +19,28 @@ title: <Suspense> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `<Suspense>` {/*suspense*/} #### Props {/*props*/} -* `children`: The actual UI you intend to render. If `children` suspends while rendering, the Suspense boundary will switch to rendering `fallback`. -* `fallback`: An alternate UI to render in place of the actual UI if it has not finished loading. Any valid React node is accepted, though in practice, a fallback is a lightweight placeholder view, such as a loading spinner or skeleton. Suspense will automatically switch to `fallback` when `children` suspends, and back to `children` when the data is ready. If `fallback` suspends while rendering, it will activate the closest parent Suspense boundary. +* `children`: Giao diện người dùng thực tế mà bạn định hiển thị. Nếu `children` tạm ngưng trong khi hiển thị, ranh giới Suspense sẽ chuyển sang hiển thị `fallback`. +* `fallback`: Một giao diện người dùng thay thế để hiển thị thay cho giao diện người dùng thực tế nếu nó chưa tải xong. Bất kỳ nút React hợp lệ nào đều được chấp nhận, mặc dù trên thực tế, fallback là một chế độ xem giữ chỗ nhẹ, chẳng hạn như trình quay tải hoặc bộ xương. Suspense sẽ tự động chuyển sang `fallback` khi `children` tạm ngưng và quay lại `children` khi dữ liệu đã sẵn sàng. Nếu `fallback` tạm ngưng trong khi hiển thị, nó sẽ kích hoạt ranh giới Suspense cha gần nhất. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -- React does not preserve any state for renders that got suspended before they were able to mount for the first time. When the component has loaded, React will retry rendering the suspended tree from scratch. -- If Suspense was displaying content for the tree, but then it suspended again, the `fallback` will be shown again unless the update causing it was caused by [`startTransition`](/reference/react/startTransition) or [`useDeferredValue`](/reference/react/useDeferredValue). -- If React needs to hide the already visible content because it suspended again, it will clean up [layout Effects](/reference/react/useLayoutEffect) in the content tree. When the content is ready to be shown again, React will fire the layout Effects again. This ensures that Effects measuring the DOM layout don't try to do this while the content is hidden. -- React includes under-the-hood optimizations like *Streaming Server Rendering* and *Selective Hydration* that are integrated with Suspense. Read [an architectural overview](https://github.com/reactwg/react-18/discussions/37) and watch [a technical talk](https://www.youtube.com/watch?v=pj5N-Khihgc) to learn more. +- React không giữ lại bất kỳ trạng thái nào cho các lần hiển thị bị tạm ngưng trước khi chúng có thể gắn kết lần đầu tiên. Khi thành phần đã tải, React sẽ thử lại hiển thị cây bị tạm ngưng từ đầu. +- Nếu Suspense đang hiển thị nội dung cho cây, nhưng sau đó nó lại bị tạm ngưng, thì `fallback` sẽ được hiển thị lại trừ khi bản cập nhật gây ra nó được gây ra bởi [`startTransition`](/reference/react/startTransition) hoặc [`useDeferredValue`](/reference/react/useDeferredValue). +- Nếu React cần ẩn nội dung đã hiển thị vì nó bị tạm ngưng lại, nó sẽ dọn dẹp [layout Effects](/reference/react/useLayoutEffect) trong cây nội dung. Khi nội dung đã sẵn sàng để hiển thị lại, React sẽ kích hoạt lại layout Effects. Điều này đảm bảo rằng Effects đo bố cục DOM không cố gắng thực hiện điều này trong khi nội dung bị ẩn. +- React bao gồm các tối ưu hóa ẩn bên dưới như *Kết xuất máy chủ phát trực tuyến* và *Hydrat hóa có chọn lọc* được tích hợp với Suspense. Đọc [tổng quan về kiến trúc](https://github.com/reactwg/react-18/discussions/37) và xem [bài nói chuyện kỹ thuật](https://www.youtube.com/watch?v=pj5N-Khihgc) để tìm hiểu thêm. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Displaying a fallback while content is loading {/*displaying-a-fallback-while-content-is-loading*/} +### Hiển thị fallback trong khi nội dung đang tải {/*displaying-a-fallback-while-content-is-loading*/} -You can wrap any part of your application with a Suspense boundary: +Bạn có thể bọc bất kỳ phần nào của ứng dụng của bạn bằng một ranh giới Suspense: ```js [[1, 1, "<Loading />"], [2, 2, "<Albums />"]] <Suspense fallback={<Loading />}> @@ -48,9 +48,9 @@ You can wrap any part of your application with a Suspense boundary: </Suspense> ``` -React will display your <CodeStep step={1}>loading fallback</CodeStep> until all the code and data needed by <CodeStep step={2}>the children</CodeStep> has been loaded. +React sẽ hiển thị <CodeStep step={1}>fallback tải</CodeStep> của bạn cho đến khi tất cả code và dữ liệu cần thiết bởi <CodeStep step={2}>các thành phần con</CodeStep> đã được tải xong. -In the example below, the `Albums` component *suspends* while fetching the list of albums. Until it's ready to render, React switches the closest Suspense boundary above to show the fallback--your `Loading` component. Then, when the data loads, React hides the `Loading` fallback and renders the `Albums` component with data. +Trong ví dụ dưới đây, thành phần `Albums` *tạm ngưng* trong khi tìm nạp danh sách album. Cho đến khi nó sẵn sàng hiển thị, React chuyển ranh giới Suspense gần nhất bên trên để hiển thị fallback -- thành phần `Loading` của bạn. Sau đó, khi dữ liệu được tải, React ẩn fallback `Loading` và hiển thị thành phần `Albums` với dữ liệu. <Sandpack> @@ -205,25 +205,26 @@ async function getAlbums() { <Note> -**Only Suspense-enabled data sources will activate the Suspense component.** They include: +**Chỉ các nguồn dữ liệu hỗ trợ Suspense mới kích hoạt thành phần Suspense.** Chúng bao gồm: -- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) -- Lazy-loading component code with [`lazy`](/reference/react/lazy) -- Reading the value of a cached Promise with [`use`](/reference/react/use) +- Tìm nạp dữ liệu với các framework hỗ trợ Suspense như [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) và [Next.js](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) +- Tải code component một cách lazy với [`lazy`](/reference/react/lazy) +- Đọc giá trị của một Promise đã được cache với [`use`](/reference/react/use) -Suspense **does not** detect when data is fetched inside an Effect or event handler. +Suspense **không** phát hiện khi dữ liệu được tìm nạp bên trong một Effect hoặc trình xử lý sự kiện. -The exact way you would load data in the `Albums` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation. +Cách chính xác để bạn tải dữ liệu trong component `Albums` ở trên phụ thuộc vào framework của bạn. Nếu bạn sử dụng một framework hỗ trợ Suspense, bạn sẽ tìm thấy chi tiết trong tài liệu tìm nạp dữ liệu của nó. -Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React. +Việc tìm nạp dữ liệu hỗ trợ Suspense mà không sử dụng framework theo ý kiến riêng vẫn chưa được hỗ trợ. Các yêu cầu để triển khai một nguồn dữ liệu hỗ trợ Suspense là không ổn định và không được ghi lại. Một API chính thức để tích hợp các nguồn dữ liệu với Suspense sẽ được phát hành trong một phiên bản React trong tương lai. </Note> --- -### Revealing content together at once {/*revealing-content-together-at-once*/} +### Tiết lộ nội dung cùng nhau đồng thời {/*revealing-content-together-at-once*/} + +Theo mặc định, toàn bộ cây bên trong Suspense được coi là một đơn vị duy nhất. Ví dụ: ngay cả khi *chỉ một* trong số các component này tạm ngưng để chờ một số dữ liệu, thì *tất cả* chúng cùng nhau sẽ được thay thế bằng chỉ báo tải: -By default, the whole tree inside Suspense is treated as a single unit. For example, even if *only one* of these components suspends waiting for some data, *all* of them together will be replaced by the loading indicator: ```js {2-5} <Suspense fallback={<Loading />}> @@ -233,10 +234,9 @@ By default, the whole tree inside Suspense is treated as a single unit. For exam </Panel> </Suspense> ``` +Sau đó, sau khi tất cả chúng đã sẵn sàng để hiển thị, chúng sẽ xuất hiện cùng nhau ngay lập tức. -Then, after all of them are ready to be displayed, they will all appear together at once. - -In the example below, both `Biography` and `Albums` fetch some data. However, because they are grouped under a single Suspense boundary, these components always "pop in" together at the same time. +Trong ví dụ dưới đây, cả `Biography` và `Albums` đều tìm nạp một số dữ liệu. Tuy nhiên, vì chúng được nhóm dưới một ranh giới Suspense duy nhất, các thành phần này luôn "hiện ra" cùng nhau đồng thời. <Sandpack> @@ -443,7 +443,24 @@ async function getAlbums() { </Sandpack> -Components that load data don't have to be direct children of the Suspense boundary. For example, you can move `Biography` and `Albums` into a new `Details` component. This doesn't change the behavior. `Biography` and `Albums` share the same closest parent Suspense boundary, so their reveal is coordinated together. +Các component tải dữ liệu không cần phải là con trực tiếp của ranh giới Suspense. Ví dụ: bạn có thể di chuyển `Biography` và `Albums` vào một component `Details` mới. Điều này không thay đổi hành vi. `Biography` và `Albums` dùng chung ranh giới Suspense cha gần nhất, vì vậy việc hiển thị của chúng được phối hợp cùng nhau. + +```js {2,8-11} +<Suspense fallback={<Loading />}> + <Details artistId={artist.id} /> +</Suspense> + +function Details({ artistId }) { + return ( + <> + <Biography artistId={artistId} /> + <Panel> + <Albums artistId={artistId} /> + </Panel> + </> + ); +} +``` ```js {2,8-11} <Suspense fallback={<Loading />}> @@ -463,10 +480,267 @@ function Details({ artistId }) { ``` --- +### Tiết lộ nội dung lồng nhau khi nó tải {/*revealing-nested-content-as-it-loads*/} + +Khi một thành phần tạm ngưng, thành phần Suspense cha gần nhất sẽ hiển thị fallback. Điều này cho phép bạn lồng nhiều thành phần Suspense để tạo ra một chuỗi tải. Fallback của mỗi ranh giới Suspense sẽ được điền vào khi cấp nội dung tiếp theo có sẵn. Ví dụ: bạn có thể cung cấp cho danh sách album fallback riêng: + +```js {3,7} +<Suspense fallback={<BigSpinner />}> + <Biography /> + <Suspense fallback={<AlbumsGlimmer />}> + <Panel> + <Albums /> + </Panel> + </Suspense> +</Suspense> +``` + +Với thay đổi này, việc hiển thị `Biography` không cần phải "chờ" `Albums` tải. + +Trình tự sẽ là: + +1. Nếu `Biography` chưa tải xong, `BigSpinner` sẽ được hiển thị thay cho toàn bộ vùng nội dung. +2. Khi `Biography` tải xong, `BigSpinner` được thay thế bằng nội dung. +3. Nếu `Albums` chưa tải xong, `AlbumsGlimmer` sẽ được hiển thị thay cho `Albums` và `Panel` cha của nó. +4. Cuối cùng, khi `Albums` tải xong, nó sẽ thay thế `AlbumsGlimmer`. + +<Sandpack> + +```js src/App.js hidden +import { useState } from 'react'; +import ArtistPage from './ArtistPage.js'; + +export default function App() { + const [show, setShow] = useState(false); + if (show) { + return ( + <ArtistPage + artist={{ + id: 'the-beatles', + name: 'The Beatles', + }} + /> + ); + } else { + return ( + <button onClick={() => setShow(true)}> + Open The Beatles artist page + </button> + ); + } +} +``` + +```js src/ArtistPage.js active +import { Suspense } from 'react'; +import Albums from './Albums.js'; +import Biography from './Biography.js'; +import Panel from './Panel.js'; + +export default function ArtistPage({ artist }) { + return ( + <> + <h1>{artist.name}</h1> + <Suspense fallback={<BigSpinner />}> + <Biography artistId={artist.id} /> + <Suspense fallback={<AlbumsGlimmer />}> + <Panel> + <Albums artistId={artist.id} /> + </Panel> + </Suspense> + </Suspense> + </> + ); +} + +function BigSpinner() { + return <h2>🌀 Loading...</h2>; +} + +function AlbumsGlimmer() { + return ( + <div className="glimmer-panel"> + <div className="glimmer-line" /> + <div className="glimmer-line" /> + <div className="glimmer-line" /> + </div> + ); +} +``` -### Revealing nested content as it loads {/*revealing-nested-content-as-it-loads*/} +```js src/Panel.js +export default function Panel({ children }) { + return ( + <section className="panel"> + {children} + </section> + ); +} +``` -When a component suspends, the closest parent Suspense component shows the fallback. This lets you nest multiple Suspense components to create a loading sequence. Each Suspense boundary's fallback will be filled in as the next level of content becomes available. For example, you can give the album list its own fallback: +```js src/Biography.js +import {use} from 'react'; +import { fetchData } from './data.js'; + +export default function Biography({ artistId }) { + const bio = use(fetchData(`/${artistId}/bio`)); + return ( + <section> + <p className="bio">{bio}</p> + </section> + ); +} +``` + +```js src/Albums.js +import {use} from 'react'; +import { fetchData } from './data.js'; + +export default function Albums({ artistId }) { + const albums = use(fetchData(`/${artistId}/albums`)); + return ( + <ul> + {albums.map(album => ( + <li key={album.id}> + {album.title} ({album.year}) + </li> + ))} + </ul> + ); +} +``` + +```js src/data.js hidden +// Note: the way you would do data fetching depends on +// the framework that you use together with Suspense. +// Normally, the caching logic would be inside a framework. + +let cache = new Map(); + +export function fetchData(url) { + if (!cache.has(url)) { + cache.set(url, getData(url)); + } + return cache.get(url); +} + +async function getData(url) { + if (url === '/the-beatles/albums') { + return await getAlbums(); + } else if (url === '/the-beatles/bio') { + return await getBio(); + } else { + throw Error('Not implemented'); + } +} + +async function getBio() { + // Add a fake delay to make waiting noticeable. + await new Promise(resolve => { + setTimeout(resolve, 500); + }); + + return `The Beatles were an English rock band, + formed in Liverpool in 1960, that comprised + John Lennon, Paul McCartney, George Harrison + and Ringo Starr.`; +} + +async function getAlbums() { + // Add a fake delay to make waiting noticeable. + await new Promise(resolve => { + setTimeout(resolve, 3000); + }); + + return [{ + id: 13, + title: 'Let It Be', + year: 1970 + }, { + id: 12, + title: 'Abbey Road', + year: 1969 + }, { + id: 11, + title: 'Yellow Submarine', + year: 1969 + }, { + id: 10, + title: 'The Beatles', + year: 1968 + }, { + id: 9, + title: 'Magical Mystery Tour', + year: 1967 + }, { + id: 8, + title: 'Sgt. Pepper\'s Lonely Hearts Club Band', + year: 1967 + }, { + id: 7, + title: 'Revolver', + year: 1966 + }, { + id: 6, + title: 'Rubber Soul', + year: 1965 + }, { + id: 5, + title: 'Help!', + year: 1965 + }, { + id: 4, + title: 'Beatles For Sale', + year: 1964 + }, { + id: 3, + title: 'A Hard Day\'s Night', + year: 1964 + }, { + id: 2, + title: 'With The Beatles', + year: 1963 + }, { + id: 1, + title: 'Please Please Me', + year: 1963 + }]; +} +``` + +```css +.bio { font-style: italic; } + +.panel { + border: 1px solid #aaa; + border-radius: 6px; + margin-top: 20px; + padding: 10px; +} + +.glimmer-panel { + border: 1px dashed #aaa; + background: linear-gradient(90deg, rgba(221,221,221,1) 0%, rgba(255,255,255,1) 100%); + border-radius: 6px; + margin-top: 20px; + padding: 10px; +} + +.glimmer-line { + display: block; + width: 60%; + height: 20px; + margin: 10px; + border-radius: 4px; + background: #f0f0f0; +} +``` + +</Sandpack> + +Các ranh giới Suspense cho phép bạn điều phối những phần nào của giao diện người dùng của bạn sẽ luôn "hiển thị" cùng nhau đồng thời và những phần nào sẽ dần dần tiết lộ thêm nội dung theo một chuỗi các trạng thái tải. Bạn có thể thêm, di chuyển hoặc xóa các ranh giới Suspense ở bất kỳ đâu trong cây mà không ảnh hưởng đến hành vi của phần còn lại của ứng dụng của bạn. + +Đừng đặt một ranh giới Suspense xung quanh mọi thành phần. Các ranh giới Suspense không nên chi tiết hơn trình tự tải mà bạn muốn người dùng trải nghiệm. Nếu bạn làm việc với một nhà thiết kế, hãy hỏi họ nơi đặt các trạng thái tải--có khả năng là họ đã đưa chúng vào wireframe thiết kế của họ. ```js {3,7} <Suspense fallback={<BigSpinner />}> @@ -479,14 +753,14 @@ When a component suspends, the closest parent Suspense component shows the fallb </Suspense> ``` -With this change, displaying the `Biography` doesn't need to "wait" for the `Albums` to load. +Với thay đổi này, việc hiển thị `Biography` không cần phải "chờ" `Albums` tải. -The sequence will be: +Trình tự sẽ là: -1. If `Biography` hasn't loaded yet, `BigSpinner` is shown in place of the entire content area. -2. Once `Biography` finishes loading, `BigSpinner` is replaced by the content. -3. If `Albums` hasn't loaded yet, `AlbumsGlimmer` is shown in place of `Albums` and its parent `Panel`. -4. Finally, once `Albums` finishes loading, it replaces `AlbumsGlimmer`. +1. Nếu `Biography` chưa tải xong, `BigSpinner` sẽ được hiển thị thay cho toàn bộ vùng nội dung. +2. Khi `Biography` tải xong, `BigSpinner` được thay thế bằng nội dung. +3. Nếu `Albums` chưa tải xong, `AlbumsGlimmer` sẽ được hiển thị thay cho `Albums` và `Panel` cha của nó. +4. Cuối cùng, khi `Albums` tải xong, nó sẽ thay thế `AlbumsGlimmer`. <Sandpack> @@ -722,15 +996,15 @@ async function getAlbums() { </Sandpack> -Suspense boundaries let you coordinate which parts of your UI should always "pop in" together at the same time, and which parts should progressively reveal more content in a sequence of loading states. You can add, move, or delete Suspense boundaries in any place in the tree without affecting the rest of your app's behavior. +Các ranh giới Suspense cho phép bạn điều phối những phần nào của giao diện người dùng của bạn sẽ luôn "hiển thị" cùng nhau đồng thời và những phần nào sẽ dần dần tiết lộ thêm nội dung theo một chuỗi các trạng thái tải. Bạn có thể thêm, di chuyển hoặc xóa các ranh giới Suspense ở bất kỳ đâu trong cây mà không ảnh hưởng đến hành vi của phần còn lại của ứng dụng của bạn. -Don't put a Suspense boundary around every component. Suspense boundaries should not be more granular than the loading sequence that you want the user to experience. If you work with a designer, ask them where the loading states should be placed--it's likely that they've already included them in their design wireframes. +Đừng đặt một ranh giới Suspense xung quanh mọi thành phần. Các ranh giới Suspense không nên chi tiết hơn trình tự tải mà bạn muốn người dùng trải nghiệm. Nếu bạn làm việc với một nhà thiết kế, hãy hỏi họ nơi đặt các trạng thái tải--có khả năng là họ đã đưa chúng vào wireframe thiết kế của họ. --- -### Showing stale content while fresh content is loading {/*showing-stale-content-while-fresh-content-is-loading*/} +### Hiển thị nội dung cũ trong khi nội dung mới đang tải {/*showing-stale-content-while-fresh-content-is-loading*/} -In this example, the `SearchResults` component suspends while fetching the search results. Type `"a"`, wait for the results, and then edit it to `"ab"`. The results for `"a"` will get replaced by the loading fallback. +Trong ví dụ này, thành phần `SearchResults` tạm ngưng trong khi tìm nạp kết quả tìm kiếm. Nhập `"a"`, đợi kết quả và sau đó chỉnh sửa thành `"ab"`. Các kết quả cho `"a"` sẽ được thay thế bằng fallback tải. <Sandpack> @@ -877,7 +1151,7 @@ input { margin: 10px; } </Sandpack> -A common alternative UI pattern is to *defer* updating the list and to keep showing the previous results until the new results are ready. The [`useDeferredValue`](/reference/react/useDeferredValue) Hook lets you pass a deferred version of the query down: +Một mẫu giao diện người dùng thay thế phổ biến là *hoãn lại* việc cập nhật danh sách và tiếp tục hiển thị các kết quả trước đó cho đến khi các kết quả mới sẵn sàng. Hook [`useDeferredValue`](/reference/react/useDeferredValue) cho phép bạn chuyển một phiên bản hoãn lại của truy vấn xuống: ```js {3,11} export default function App() { @@ -896,10 +1170,19 @@ export default function App() { ); } ``` +`query` sẽ cập nhật ngay lập tức, vì vậy đầu vào sẽ hiển thị giá trị mới. Tuy nhiên, `deferredQuery` sẽ giữ giá trị trước đó cho đến khi dữ liệu được tải, vì vậy `SearchResults` sẽ hiển thị kết quả cũ trong một khoảng thời gian. + +Để làm cho nó rõ ràng hơn với người dùng, bạn có thể thêm một chỉ báo trực quan khi danh sách kết quả cũ được hiển thị: -The `query` will update immediately, so the input will display the new value. However, the `deferredQuery` will keep its previous value until the data has loaded, so `SearchResults` will show the stale results for a bit. +```js {2} +<div style={{ + opacity: query !== deferredQuery ? 0.5 : 1 +}}> + <SearchResults query={deferredQuery} /> +</div> +``` -To make it more obvious to the user, you can add a visual indication when the stale result list is displayed: +Nhập `"a"` trong ví dụ bên dưới, đợi kết quả tải và sau đó chỉnh sửa đầu vào thành `"ab"`. Lưu ý rằng thay vì fallback của Suspense, bây giờ bạn thấy danh sách kết quả cũ bị làm mờ cho đến khi các kết quả mới được tải: ```js {2} <div style={{ @@ -909,7 +1192,7 @@ To make it more obvious to the user, you can add a visual indication when the st </div> ``` -Enter `"a"` in the example below, wait for the results to load, and then edit the input to `"ab"`. Notice how instead of the Suspense fallback, you now see the dimmed stale result list until the new results have loaded: +Nhập `"a"` trong ví dụ bên dưới, đợi kết quả tải và sau đó chỉnh sửa đầu vào thành `"ab"`. Lưu ý rằng thay vì fallback của Suspense, bây giờ bạn thấy danh sách kết quả cũ bị làm mờ cho đến khi các kết quả mới được tải: <Sandpack> @@ -963,9 +1246,9 @@ export default function SearchResults({ query }) { ``` ```js src/data.js hidden -// Note: the way you would do data fetching depends on -// the framework that you use together with Suspense. -// Normally, the caching logic would be inside a framework. +// Lưu ý: cách bạn thực hiện tìm nạp dữ liệu phụ thuộc vào +// framework mà bạn sử dụng cùng với Suspense. +// Thông thường, logic bộ nhớ đệm sẽ nằm bên trong một framework. let cache = new Map(); @@ -1063,15 +1346,14 @@ input { margin: 10px; } <Note> -Both deferred values and [Transitions](#preventing-already-revealed-content-from-hiding) let you avoid showing Suspense fallback in favor of inline indicators. Transitions mark the whole update as non-urgent so they are typically used by frameworks and router libraries for navigation. Deferred values, on the other hand, are mostly useful in application code where you want to mark a part of UI as non-urgent and let it "lag behind" the rest of the UI. +Cả giá trị được hoãn lại và [Transitions](#preventing-already-revealed-content-from-hiding) cho phép bạn tránh hiển thị fallback của Suspense để ủng hộ các chỉ báo nội tuyến. Transitions đánh dấu toàn bộ bản cập nhật là không khẩn cấp, vì vậy chúng thường được sử dụng bởi các framework và thư viện bộ định tuyến để điều hướng. Mặt khác, các giá trị được hoãn lại chủ yếu hữu ích trong mã ứng dụng, nơi bạn muốn đánh dấu một phần của giao diện người dùng là không khẩn cấp và để nó "tụt lại phía sau" so với phần còn lại của giao diện người dùng. </Note> --- +### Ngăn chặn việc ẩn nội dung đã hiển thị {/*preventing-already-revealed-content-from-hiding*/} -### Preventing already revealed content from hiding {/*preventing-already-revealed-content-from-hiding*/} - -When a component suspends, the closest parent Suspense boundary switches to showing the fallback. This can lead to a jarring user experience if it was already displaying some content. Try pressing this button: +Khi một thành phần tạm ngưng, ranh giới Suspense gần nhất sẽ chuyển sang hiển thị fallback. Điều này có thể dẫn đến trải nghiệm người dùng khó chịu nếu nó đã hiển thị một số nội dung. Hãy thử nhấn nút này: <Sandpack> @@ -1364,10 +1646,9 @@ main { ``` </Sandpack> +Khi bạn nhấn nút, thành phần `Router` đã kết xuất `ArtistPage` thay vì `IndexPage`. Một thành phần bên trong `ArtistPage` tạm ngưng, vì vậy ranh giới Suspense gần nhất bắt đầu hiển thị fallback. Ranh giới Suspense gần nhất ở gần gốc, vì vậy toàn bộ bố cục trang web đã được thay thế bằng `BigSpinner`. -When you pressed the button, the `Router` component rendered `ArtistPage` instead of `IndexPage`. A component inside `ArtistPage` suspended, so the closest Suspense boundary started showing the fallback. The closest Suspense boundary was near the root, so the whole site layout got replaced by `BigSpinner`. - -To prevent this, you can mark the navigation state update as a *Transition* with [`startTransition`:](/reference/react/startTransition) +Để ngăn điều này, bạn có thể đánh dấu bản cập nhật trạng thái điều hướng là một *Transition* với [`startTransition`:](/reference/react/startTransition) ```js {5,7} function Router() { @@ -1381,7 +1662,7 @@ function Router() { // ... ``` -This tells React that the state transition is not urgent, and it's better to keep showing the previous page instead of hiding any already revealed content. Now clicking the button "waits" for the `Biography` to load: +Điều này cho React biết rằng quá trình chuyển đổi trạng thái không khẩn cấp và tốt hơn là tiếp tục hiển thị trang trước thay vì ẩn bất kỳ nội dung nào đã hiển thị. Bây giờ, việc nhấp vào nút sẽ "chờ" `Biography` tải: <Sandpack> @@ -1677,19 +1958,19 @@ main { </Sandpack> -A Transition doesn't wait for *all* content to load. It only waits long enough to avoid hiding already revealed content. For example, the website `Layout` was already revealed, so it would be bad to hide it behind a loading spinner. However, the nested `Suspense` boundary around `Albums` is new, so the Transition doesn't wait for it. +Một Transition không đợi *tất cả* nội dung tải. Nó chỉ đợi đủ lâu để tránh ẩn nội dung đã hiển thị. Ví dụ: `Layout` của trang web đã được hiển thị, vì vậy sẽ rất tệ nếu ẩn nó sau một spinner tải. Tuy nhiên, ranh giới `Suspense` lồng nhau xung quanh `Albums` là mới, vì vậy Transition không đợi nó. <Note> -Suspense-enabled routers are expected to wrap the navigation updates into Transitions by default. +Các bộ định tuyến hỗ trợ Suspense được mong đợi sẽ tự động gói các bản cập nhật điều hướng vào Transitions theo mặc định. </Note> --- +### Cho biết rằng một Transition đang diễn ra {/*indicating-that-a-transition-is-happening*/} -### Indicating that a Transition is happening {/*indicating-that-a-transition-is-happening*/} +Trong ví dụ trên, khi bạn nhấp vào nút, không có dấu hiệu trực quan nào cho thấy một điều hướng đang được tiến hành. Để thêm một chỉ báo, bạn có thể thay thế [`startTransition`](/reference/react/startTransition) bằng [`useTransition`](/reference/react/useTransition), cái mà cung cấp cho bạn một giá trị boolean `isPending`. Trong ví dụ dưới đây, nó được sử dụng để thay đổi kiểu tiêu đề trang web trong khi một Transition đang diễn ra: -In the above example, once you click the button, there is no visual indication that a navigation is in progress. To add an indicator, you can replace [`startTransition`](/reference/react/startTransition) with [`useTransition`](/reference/react/useTransition) which gives you a boolean `isPending` value. In the example below, it's used to change the website header styling while a Transition is happening: <Sandpack> @@ -1990,27 +2271,27 @@ main { --- -### Resetting Suspense boundaries on navigation {/*resetting-suspense-boundaries-on-navigation*/} +### Đặt lại ranh giới Suspense khi điều hướng {/*resetting-suspense-boundaries-on-navigation*/} -During a Transition, React will avoid hiding already revealed content. However, if you navigate to a route with different parameters, you might want to tell React it is *different* content. You can express this with a `key`: +Trong quá trình Transition, React sẽ tránh ẩn nội dung đã hiển thị. Tuy nhiên, nếu bạn điều hướng đến một tuyến đường có các tham số khác nhau, bạn có thể muốn cho React biết đó là nội dung *khác*. Bạn có thể thể hiện điều này bằng một `key`: ```js <ProfilePage key={queryParams.id} /> ``` -Imagine you're navigating within a user's profile page, and something suspends. If that update is wrapped in a Transition, it will not trigger the fallback for already visible content. That's the expected behavior. +Hãy tưởng tượng bạn đang điều hướng trong trang hồ sơ của người dùng và một cái gì đó tạm ngưng. Nếu bản cập nhật đó được gói trong một Transition, nó sẽ không kích hoạt fallback cho nội dung đã hiển thị. Đó là hành vi mong đợi. -However, now imagine you're navigating between two different user profiles. In that case, it makes sense to show the fallback. For example, one user's timeline is *different content* from another user's timeline. By specifying a `key`, you ensure that React treats different users' profiles as different components, and resets the Suspense boundaries during navigation. Suspense-integrated routers should do this automatically. +Tuy nhiên, bây giờ hãy tưởng tượng bạn đang điều hướng giữa hai hồ sơ người dùng khác nhau. Trong trường hợp đó, việc hiển thị fallback sẽ có ý nghĩa. Ví dụ: dòng thời gian của một người dùng *là nội dung khác* so với dòng thời gian của một người dùng khác. Bằng cách chỉ định một `key`, bạn đảm bảo rằng React coi hồ sơ của những người dùng khác nhau là các thành phần khác nhau và đặt lại các ranh giới Suspense trong quá trình điều hướng. Các bộ định tuyến tích hợp Suspense sẽ tự động thực hiện điều này. --- -### Providing a fallback for server errors and client-only content {/*providing-a-fallback-for-server-errors-and-client-only-content*/} +### Cung cấp fallback cho các lỗi máy chủ và nội dung chỉ dành cho máy khách {/*providing-a-fallback-for-server-errors-and-client-only-content*/} -If you use one of the [streaming server rendering APIs](/reference/react-dom/server) (or a framework that relies on them), React will also use your `<Suspense>` boundaries to handle errors on the server. If a component throws an error on the server, React will not abort the server render. Instead, it will find the closest `<Suspense>` component above it and include its fallback (such as a spinner) into the generated server HTML. The user will see a spinner at first. +Nếu bạn sử dụng một trong các [API kết xuất máy chủ phát trực tuyến](/reference/react-dom/server) (hoặc một framework dựa trên chúng), React cũng sẽ sử dụng các ranh giới `<Suspense>` của bạn để xử lý các lỗi trên máy chủ. Nếu một thành phần đưa ra lỗi trên máy chủ, React sẽ không hủy bỏ quá trình kết xuất máy chủ. Thay vào đó, nó sẽ tìm thành phần `<Suspense>` gần nhất ở trên nó và bao gồm fallback của nó (chẳng hạn như một spinner) vào HTML máy chủ đã tạo. Người dùng sẽ thấy một spinner lúc đầu. -On the client, React will attempt to render the same component again. If it errors on the client too, React will throw the error and display the closest [error boundary.](/reference/react/Component#static-getderivedstatefromerror) However, if it does not error on the client, React will not display the error to the user since the content was eventually displayed successfully. +Trên máy khách, React sẽ cố gắng kết xuất lại thành phần tương tự. Nếu nó cũng gây ra lỗi trên máy khách, React sẽ đưa ra lỗi và hiển thị [ranh giới lỗi](/reference/react/Component#static-getderivedstatefromerror) gần nhất. Tuy nhiên, nếu nó không gây ra lỗi trên máy khách, React sẽ không hiển thị lỗi cho người dùng vì nội dung cuối cùng đã được hiển thị thành công. -You can use this to opt out some components from rendering on the server. To do this, throw an error in the server environment and then wrap them in a `<Suspense>` boundary to replace their HTML with fallbacks: +Bạn có thể sử dụng điều này để chọn không kết xuất một số thành phần trên máy chủ. Để thực hiện việc này, hãy đưa ra một lỗi trong môi trường máy chủ và sau đó gói chúng trong một ranh giới `<Suspense>` để thay thế HTML của chúng bằng các fallback: ```js <Suspense fallback={<Loading />}> @@ -2025,17 +2306,31 @@ function Chat() { } ``` -The server HTML will include the loading indicator. It will be replaced by the `Chat` component on the client. +HTML máy chủ sẽ bao gồm chỉ báo tải. Nó sẽ được thay thế bằng thành phần `Chat` trên máy khách. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### How do I prevent the UI from being replaced by a fallback during an update? {/*preventing-unwanted-fallbacks*/} +### Làm cách nào để ngăn giao diện người dùng bị thay thế bằng fallback trong quá trình cập nhật? {/*preventing-unwanted-fallbacks*/} + +Việc thay thế giao diện người dùng hiển thị bằng fallback tạo ra trải nghiệm người dùng khó chịu. Điều này có thể xảy ra khi một bản cập nhật khiến một thành phần tạm ngưng và ranh giới Suspense gần nhất đã hiển thị nội dung cho người dùng. + +Để ngăn điều này xảy ra, [hãy đánh dấu bản cập nhật là không khẩn cấp bằng cách sử dụng `startTransition`](#preventing-already-revealed-content-from-hiding). Trong quá trình Transition, React sẽ đợi cho đến khi đủ dữ liệu được tải để ngăn chặn fallback không mong muốn xuất hiện: + +```js {2-3,5} +function handleNextPageClick() { + // Nếu bản cập nhật này tạm ngưng, đừng ẩn nội dung đã hiển thị + startTransition(() => { + setCurrentPage(currentPage + 1); + }); +} +``` +Điều này sẽ tránh việc ẩn nội dung hiện có. Tuy nhiên, bất kỳ ranh giới `Suspense` mới được hiển thị nào vẫn sẽ hiển thị ngay lập tức các fallback để tránh chặn giao diện người dùng và cho phép người dùng xem nội dung khi nó có sẵn. -Replacing visible UI with a fallback creates a jarring user experience. This can happen when an update causes a component to suspend, and the nearest Suspense boundary is already showing content to the user. +**React sẽ chỉ ngăn chặn các fallback không mong muốn trong quá trình cập nhật không khẩn cấp**. Nó sẽ không trì hoãn việc hiển thị nếu đó là kết quả của một bản cập nhật khẩn cấp. Bạn phải chọn tham gia bằng một API như [`startTransition`](/reference/react/startTransition) hoặc [`useDeferredValue`](/reference/react/useDeferredValue). -To prevent this from happening, [mark the update as non-urgent using `startTransition`](#preventing-already-revealed-content-from-hiding). During a Transition, React will wait until enough data has loaded to prevent an unwanted fallback from appearing: +Nếu bộ định tuyến của bạn được tích hợp với Suspense, nó sẽ tự động gói các bản cập nhật của nó vào [`startTransition`](/reference/react/startTransition). ```js {2-3,5} function handleNextPageClick() { @@ -2045,9 +2340,37 @@ function handleNextPageClick() { }); } ``` +Điều này sẽ tránh việc ẩn nội dung hiện có. Tuy nhiên, bất kỳ ranh giới `Suspense` mới được hiển thị nào vẫn sẽ hiển thị ngay lập tức các fallback để tránh chặn giao diện người dùng và cho phép người dùng xem nội dung khi nó có sẵn. -This will avoid hiding existing content. However, any newly rendered `Suspense` boundaries will still immediately display fallbacks to avoid blocking the UI and let the user see the content as it becomes available. +**React sẽ chỉ ngăn chặn các fallback không mong muốn trong quá trình cập nhật không khẩn cấp**. Nó sẽ không trì hoãn việc hiển thị nếu đó là kết quả của một bản cập nhật khẩn cấp. Bạn phải chọn tham gia bằng một API như [`startTransition`](/reference/react/startTransition) hoặc [`useDeferredValue`](/reference/react/useDeferredValue). + +Nếu bộ định tuyến của bạn được tích hợp với Suspense, nó sẽ tự động gói các bản cập nhật của nó vào [`startTransition`](/reference/react/startTransition). + +```js +<Suspense fallback={<Loading />}> + <Chat /> +</Suspense> + +function Chat() { + if (typeof window === 'undefined') { + throw Error('Chat should only render on the client.'); + } + // ... +} +``` +HTML máy chủ sẽ bao gồm chỉ báo tải. Nó sẽ được thay thế bằng thành phần `Chat` trên máy khách. +--- + +```js {2-3,5} +function handleNextPageClick() { + // If this update suspends, don't hide the already displayed content + startTransition(() => { + setCurrentPage(currentPage + 1); + }); +} +``` +Điều này sẽ tránh việc ẩn nội dung hiện có. Tuy nhiên, bất kỳ ranh giới `Suspense` mới được hiển thị nào vẫn sẽ hiển thị ngay lập tức các fallback để tránh chặn giao diện người dùng và cho phép người dùng xem nội dung khi nó có sẵn. -**React will only prevent unwanted fallbacks during non-urgent updates**. It will not delay a render if it's the result of an urgent update. You must opt in with an API like [`startTransition`](/reference/react/startTransition) or [`useDeferredValue`](/reference/react/useDeferredValue). +**React sẽ chỉ ngăn chặn các fallback không mong muốn trong quá trình cập nhật không khẩn cấp**. Nó sẽ không trì hoãn việc hiển thị nếu đó là kết quả của một bản cập nhật khẩn cấp. Bạn phải chọn tham gia bằng một API như [`startTransition`](/reference/react/startTransition) hoặc [`useDeferredValue`](/reference/react/useDeferredValue). -If your router is integrated with Suspense, it should wrap its updates into [`startTransition`](/reference/react/startTransition) automatically. +Nếu bộ định tuyến của bạn được tích hợp với Suspense, nó sẽ tự động gói các bản cập nhật của nó vào [`startTransition`](/reference/react/startTransition). diff --git a/src/content/reference/react/act.md b/src/content/reference/react/act.md index eff3f891f..06f99d279 100644 --- a/src/content/reference/react/act.md +++ b/src/content/reference/react/act.md @@ -4,7 +4,7 @@ title: act <Intro> -`act` is a test helper to apply pending React updates before making assertions. +`act` là một helper kiểm thử để áp dụng các cập nhật React đang chờ xử lý trước khi đưa ra các khẳng định. ```js await act(async actFn) @@ -12,24 +12,23 @@ await act(async actFn) </Intro> -To prepare a component for assertions, wrap the code rendering it and performing updates inside an `await act()` call. This makes your test run closer to how React works in the browser. +Để chuẩn bị một component cho các khẳng định, hãy bọc code hiển thị nó và thực hiện các cập nhật bên trong một lệnh gọi `await act()`. Điều này làm cho quá trình kiểm thử của bạn chạy gần hơn với cách React hoạt động trong trình duyệt. <Note> -You might find using `act()` directly a bit too verbose. To avoid some of the boilerplate, you could use a library like [React Testing Library](https://testing-library.com/docs/react-testing-library/intro), whose helpers are wrapped with `act()`. +Bạn có thể thấy việc sử dụng trực tiếp `act()` hơi dài dòng. Để tránh một số boilerplate, bạn có thể sử dụng một thư viện như [React Testing Library](https://testing-library.com/docs/react-testing-library/intro), có các helper được bọc bằng `act()`. </Note> - <InlineToc /> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `await act(async actFn)` {/*await-act-async-actfn*/} -When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface. React provides a helper called `act()` that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions. +Khi viết các bài kiểm tra UI, các tác vụ như hiển thị, các sự kiện người dùng hoặc tìm nạp dữ liệu có thể được coi là "các đơn vị" tương tác với giao diện người dùng. React cung cấp một helper gọi là `act()` để đảm bảo rằng tất cả các cập nhật liên quan đến các "đơn vị" này đã được xử lý và áp dụng cho DOM trước khi bạn đưa ra bất kỳ khẳng định nào. -The name `act` comes from the [Arrange-Act-Assert](https://wiki.c2.com/?ArrangeActAssert) pattern. +Tên `act` xuất phát từ mẫu [Arrange-Act-Assert](https://wiki.c2.com/?ArrangeActAssert). ```js {2,4} it ('renders with button disabled', async () => { @@ -42,25 +41,25 @@ it ('renders with button disabled', async () => { <Note> -We recommend using `act` with `await` and an `async` function. Although the sync version works in many cases, it doesn't work in all cases and due to the way React schedules updates internally, it's difficult to predict when you can use the sync version. +Chúng tôi khuyên bạn nên sử dụng `act` với `await` và một hàm `async`. Mặc dù phiên bản đồng bộ hoạt động trong nhiều trường hợp, nhưng nó không hoạt động trong tất cả các trường hợp và do cách React lên lịch các cập nhật bên trong, rất khó để dự đoán khi nào bạn có thể sử dụng phiên bản đồng bộ. -We will deprecate and remove the sync version in the future. +Chúng tôi sẽ ngừng sử dụng và xóa phiên bản đồng bộ trong tương lai. </Note> -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `async actFn`: An async function wrapping renders or interactions for components being tested. Any updates triggered within the `actFn`, are added to an internal act queue, which are then flushed together to process and apply any changes to the DOM. Since it is async, React will also run any code that crosses an async boundary, and flush any updates scheduled. +* `async actFn`: Một hàm async bao bọc các lần hiển thị hoặc tương tác cho các component đang được kiểm tra. Bất kỳ cập nhật nào được kích hoạt trong `actFn`, sẽ được thêm vào hàng đợi act nội bộ, sau đó được làm mới cùng nhau để xử lý và áp dụng bất kỳ thay đổi nào đối với DOM. Vì nó là async, React cũng sẽ chạy bất kỳ code nào vượt qua ranh giới async và làm mới mọi cập nhật đã lên lịch. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`act` does not return anything. +`act` không trả về bất cứ thứ gì. -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -When testing a component, you can use `act` to make assertions about its output. +Khi kiểm tra một component, bạn có thể sử dụng `act` để đưa ra các khẳng định về đầu ra của nó. -For example, let’s say we have this `Counter` component, the usage examples below show how to test it: +Ví dụ: giả sử chúng ta có component `Counter` này, các ví dụ sử dụng bên dưới cho thấy cách kiểm tra nó: ```js function Counter() { @@ -84,9 +83,9 @@ function Counter() { } ``` -### Rendering components in tests {/*rendering-components-in-tests*/} +### Hiển thị component trong kiểm thử {/*rendering-components-in-tests*/} -To test the render output of a component, wrap the render inside `act()`: +Để kiểm tra đầu ra hiển thị của một component, hãy bọc quá trình hiển thị bên trong `act()`: ```js {10,12} import {act} from 'react'; @@ -97,7 +96,7 @@ it('can render and update a counter', async () => { container = document.createElement('div'); document.body.appendChild(container); - // ✅ Render the component inside act(). + // ✅ Render component bên trong act(). await act(() => { ReactDOMClient.createRoot(container).render(<Counter />); }); @@ -109,13 +108,13 @@ it('can render and update a counter', async () => { }); ``` -Here, we create a container, append it to the document, and render the `Counter` component inside `act()`. This ensures that the component is rendered and its effects are applied before making assertions. +Ở đây, chúng ta tạo một container, thêm nó vào document và hiển thị component `Counter` bên trong `act()`. Điều này đảm bảo rằng component được hiển thị và các effect của nó được áp dụng trước khi đưa ra các khẳng định. -Using `act` ensures that all updates have been applied before we make assertions. +Sử dụng `act` đảm bảo rằng tất cả các cập nhật đã được áp dụng trước khi chúng ta đưa ra các khẳng định. -### Dispatching events in tests {/*dispatching-events-in-tests*/} +### Dispatch các event trong kiểm thử {/*dispatching-events-in-tests*/} -To test events, wrap the event dispatch inside `act()`: +Để kiểm tra các event, hãy bọc quá trình dispatch event bên trong `act()`: ```js {14,16} import {act} from 'react'; @@ -130,7 +129,7 @@ it.only('can render and update a counter', async () => { ReactDOMClient.createRoot(container).render(<Counter />); }); - // ✅ Dispatch the event inside act(). + // ✅ Dispatch event bên trong act(). await act(async () => { button.dispatchEvent(new MouseEvent('click', { bubbles: true })); }); @@ -142,21 +141,21 @@ it.only('can render and update a counter', async () => { }); ``` -Here, we render the component with `act`, and then dispatch the event inside another `act()`. This ensures that all updates from the event are applied before making assertions. +Ở đây, chúng ta hiển thị component với `act`, và sau đó dispatch event bên trong một `act()` khác. Điều này đảm bảo rằng tất cả các cập nhật từ event được áp dụng trước khi đưa ra các khẳng định. <Pitfall> -Don’t forget that dispatching DOM events only works when the DOM container is added to the document. You can use a library like [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) to reduce the boilerplate code. +Đừng quên rằng việc dispatch các event DOM chỉ hoạt động khi DOM container được thêm vào document. Bạn có thể sử dụng một thư viện như [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) để giảm code boilerplate. </Pitfall> -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I'm getting an error: "The current testing environment is not configured to support act"(...)" {/*error-the-current-testing-environment-is-not-configured-to-support-act*/} +### Tôi gặp lỗi: "The current testing environment is not configured to support act"(...)" {/*error-the-current-testing-environment-is-not-configured-to-support-act*/} -Using `act` requires setting `global.IS_REACT_ACT_ENVIRONMENT=true` in your test environment. This is to ensure that `act` is only used in the correct environment. +Sử dụng `act` yêu cầu thiết lập `global.IS_REACT_ACT_ENVIRONMENT=true` trong môi trường kiểm thử của bạn. Điều này là để đảm bảo rằng `act` chỉ được sử dụng trong môi trường chính xác. -If you don't set the global, you will see an error like this: +Nếu bạn không đặt global, bạn sẽ thấy một lỗi như sau: <ConsoleBlock level="error"> @@ -164,7 +163,7 @@ Warning: The current testing environment is not configured to support act(...) </ConsoleBlock> -To fix, add this to your global setup file for React tests: +Để sửa lỗi, hãy thêm dòng này vào file thiết lập global cho các bài kiểm tra React: ```js global.IS_REACT_ACT_ENVIRONMENT=true @@ -172,6 +171,6 @@ global.IS_REACT_ACT_ENVIRONMENT=true <Note> -In testing frameworks like [React Testing Library](https://testing-library.com/docs/react-testing-library/intro), `IS_REACT_ACT_ENVIRONMENT` is already set for you. +Trong các framework kiểm thử như [React Testing Library](https://testing-library.com/docs/react-testing-library/intro), `IS_REACT_ACT_ENVIRONMENT` đã được thiết lập cho bạn. </Note> diff --git a/src/content/reference/react/apis.md b/src/content/reference/react/apis.md index cbab4a0e6..4ca603fc5 100644 --- a/src/content/reference/react/apis.md +++ b/src/content/reference/react/apis.md @@ -1,31 +1,31 @@ --- -title: "Built-in React APIs" +title: "Các API React Tích Hợp" --- <Intro> -In addition to [Hooks](/reference/react) and [Components](/reference/react/components), the `react` package exports a few other APIs that are useful for defining components. This page lists all the remaining modern React APIs. +Ngoài [Hooks](/reference/react) và [Components](/reference/react/components), gói `react` xuất ra một vài API khác hữu ích cho việc định nghĩa các component. Trang này liệt kê tất cả các API React hiện đại còn lại. </Intro> --- -* [`createContext`](/reference/react/createContext) lets you define and provide context to the child components. Used with [`useContext`.](/reference/react/useContext) -* [`forwardRef`](/reference/react/forwardRef) lets your component expose a DOM node as a ref to the parent. Used with [`useRef`.](/reference/react/useRef) -* [`lazy`](/reference/react/lazy) lets you defer loading a component's code until it's rendered for the first time. -* [`memo`](/reference/react/memo) lets your component skip re-renders with same props. Used with [`useMemo`](/reference/react/useMemo) and [`useCallback`.](/reference/react/useCallback) -* [`startTransition`](/reference/react/startTransition) lets you mark a state update as non-urgent. Similar to [`useTransition`.](/reference/react/useTransition) -* [`act`](/reference/react/act) lets you wrap renders and interactions in tests to ensure updates have processed before making assertions. +* [`createContext`](/reference/react/createContext) cho phép bạn định nghĩa và cung cấp context cho các component con. Được sử dụng với [`useContext`.](/reference/react/useContext) +* [`forwardRef`](/reference/react/forwardRef) cho phép component của bạn hiển thị một DOM node như một ref cho component cha. Được sử dụng với [`useRef`.](/reference/react/useRef) +* [`lazy`](/reference/react/lazy) cho phép bạn trì hoãn việc tải code của một component cho đến khi nó được render lần đầu tiên. +* [`memo`](/reference/react/memo) cho phép component của bạn bỏ qua việc re-render khi props không thay đổi. Được sử dụng với [`useMemo`](/reference/react/useMemo) và [`useCallback`.](/reference/react/useCallback) +* [`startTransition`](/reference/react/startTransition) cho phép bạn đánh dấu một cập nhật trạng thái là không khẩn cấp. Tương tự như [`useTransition`.](/reference/react/useTransition) +* [`act`](/reference/react/act) cho phép bạn bao bọc các lần render và tương tác trong các bài kiểm tra để đảm bảo các cập nhật đã được xử lý trước khi đưa ra các khẳng định. --- ## Resource APIs {/*resource-apis*/} -*Resources* can be accessed by a component without having them as part of their state. For example, a component can read a message from a Promise or read styling information from a context. +*Resources* có thể được truy cập bởi một component mà không cần chúng là một phần của state của component đó. Ví dụ: một component có thể đọc một tin nhắn từ một Promise hoặc đọc thông tin kiểu dáng từ một context. -To read a value from a resource, use this API: +Để đọc một giá trị từ một resource, hãy sử dụng API này: -* [`use`](/reference/react/use) lets you read the value of a resource like a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or [context](/learn/passing-data-deeply-with-context). +* [`use`](/reference/react/use) cho phép bạn đọc giá trị của một resource như một [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) hoặc [context](/learn/passing-data-deeply-with-context). ```js function MessageComponent({ messagePromise }) { const message = use(messagePromise); diff --git a/src/content/reference/react/cache.md b/src/content/reference/react/cache.md index 22a9b23bd..2546737f4 100644 --- a/src/content/reference/react/cache.md +++ b/src/content/reference/react/cache.md @@ -5,13 +5,13 @@ canary: true <RSC> -`cache` is only for use with [React Server Components](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components). +`cache` chỉ được sử dụng với [React Server Components](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components). </RSC> <Intro> -`cache` lets you cache the result of a data fetch or computation. +`cache` cho phép bạn lưu trữ kết quả của một lần tìm nạp dữ liệu hoặc tính toán. ```js const cachedFn = cache(fn); @@ -23,11 +23,11 @@ const cachedFn = cache(fn); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `cache(fn)` {/*cache*/} -Call `cache` outside of any components to create a version of the function with caching. +Gọi `cache` bên ngoài bất kỳ component nào để tạo ra một phiên bản của hàm có bộ nhớ đệm. ```js {4,7} import {cache} from 'react'; @@ -41,42 +41,42 @@ function Chart({data}) { } ``` -When `getMetrics` is first called with `data`, `getMetrics` will call `calculateMetrics(data)` and store the result in cache. If `getMetrics` is called again with the same `data`, it will return the cached result instead of calling `calculateMetrics(data)` again. +Khi `getMetrics` được gọi lần đầu tiên với `data`, `getMetrics` sẽ gọi `calculateMetrics(data)` và lưu trữ kết quả vào bộ nhớ đệm. Nếu `getMetrics` được gọi lại với cùng một `data`, nó sẽ trả về kết quả đã lưu trong bộ nhớ đệm thay vì gọi lại `calculateMetrics(data)`. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -- `fn`: The function you want to cache results for. `fn` can take any arguments and return any value. +- `fn`: Hàm bạn muốn lưu kết quả vào bộ nhớ đệm. `fn` có thể nhận bất kỳ đối số nào và trả về bất kỳ giá trị nào. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`cache` returns a cached version of `fn` with the same type signature. It does not call `fn` in the process. +`cache` trả về một phiên bản đã được lưu trong bộ nhớ đệm của `fn` với cùng một kiểu chữ ký. Nó không gọi `fn` trong quá trình này. -When calling `cachedFn` with given arguments, it first checks if a cached result exists in the cache. If a cached result exists, it returns the result. If not, it calls `fn` with the arguments, stores the result in the cache, and returns the result. The only time `fn` is called is when there is a cache miss. +Khi gọi `cachedFn` với các đối số đã cho, nó sẽ kiểm tra trước xem có kết quả đã lưu trong bộ nhớ đệm hay không. Nếu có kết quả đã lưu trong bộ nhớ đệm, nó sẽ trả về kết quả đó. Nếu không, nó sẽ gọi `fn` với các đối số, lưu trữ kết quả vào bộ nhớ đệm và trả về kết quả. `fn` chỉ được gọi khi có một cache miss. <Note> -The optimization of caching return values based on inputs is known as [_memoization_](https://en.wikipedia.org/wiki/Memoization). We refer to the function returned from `cache` as a memoized function. +Việc tối ưu hóa các giá trị trả về dựa trên đầu vào được gọi là [_memoization_](https://en.wikipedia.org/wiki/Memoization). Chúng ta gọi hàm được trả về từ `cache` là một hàm đã được ghi nhớ. </Note> -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} [//]: # 'TODO: add links to Server/Client Component reference once https://github.com/reactjs/react.dev/pull/6177 is merged' -- React will invalidate the cache for all memoized functions for each server request. -- Each call to `cache` creates a new function. This means that calling `cache` with the same function multiple times will return different memoized functions that do not share the same cache. -- `cachedFn` will also cache errors. If `fn` throws an error for certain arguments, it will be cached, and the same error is re-thrown when `cachedFn` is called with those same arguments. -- `cache` is for use in [Server Components](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components) only. +- React sẽ làm mất hiệu lực bộ nhớ đệm cho tất cả các hàm đã được ghi nhớ cho mỗi yêu cầu máy chủ. +- Mỗi lần gọi `cache` sẽ tạo ra một hàm mới. Điều này có nghĩa là việc gọi `cache` với cùng một hàm nhiều lần sẽ trả về các hàm đã được ghi nhớ khác nhau, không dùng chung cùng một bộ nhớ đệm. +- `cachedFn` cũng sẽ lưu trữ các lỗi. Nếu `fn` đưa ra một lỗi cho các đối số nhất định, nó sẽ được lưu vào bộ nhớ đệm và cùng một lỗi sẽ được đưa ra lại khi `cachedFn` được gọi với các đối số tương tự. +- `cache` chỉ được sử dụng trong [Server Components](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-server-components). --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Cache an expensive computation {/*cache-expensive-computation*/} +### Lưu vào bộ nhớ đệm một phép tính tốn kém {/*cache-expensive-computation*/} -Use `cache` to skip duplicate work. +Sử dụng `cache` để bỏ qua công việc trùng lặp. ```js [[1, 7, "getUserMetrics(user)"],[2, 13, "getUserMetrics(user)"]] import {cache} from 'react'; @@ -98,17 +98,17 @@ function TeamReport({users}) { } ``` -If the same `user` object is rendered in both `Profile` and `TeamReport`, the two components can share work and only call `calculateUserMetrics` once for that `user`. +Nếu cùng một đối tượng `user` được hiển thị trong cả `Profile` và `TeamReport`, hai component có thể chia sẻ công việc và chỉ gọi `calculateUserMetrics` một lần cho `user` đó. -Assume `Profile` is rendered first. It will call <CodeStep step={1}>`getUserMetrics`</CodeStep>, and check if there is a cached result. Since it is the first time `getUserMetrics` is called with that `user`, there will be a cache miss. `getUserMetrics` will then call `calculateUserMetrics` with that `user` and write the result to cache. +Giả sử `Profile` được hiển thị trước. Nó sẽ gọi <CodeStep step={1}>`getUserMetrics`</CodeStep>, và kiểm tra xem có kết quả đã lưu trong bộ nhớ đệm hay không. Vì đây là lần đầu tiên `getUserMetrics` được gọi với `user` đó, sẽ có một cache miss. `getUserMetrics` sau đó sẽ gọi `calculateUserMetrics` với `user` đó và ghi kết quả vào bộ nhớ đệm. -When `TeamReport` renders its list of `users` and reaches the same `user` object, it will call <CodeStep step={2}>`getUserMetrics`</CodeStep> and read the result from cache. +Khi `TeamReport` hiển thị danh sách `users` của nó và đạt đến cùng một đối tượng `user`, nó sẽ gọi <CodeStep step={2}>`getUserMetrics`</CodeStep> và đọc kết quả từ bộ nhớ đệm. <Pitfall> -##### Calling different memoized functions will read from different caches. {/*pitfall-different-memoized-functions*/} +##### Gọi các hàm đã ghi nhớ khác nhau sẽ đọc từ các bộ nhớ đệm khác nhau. {/*pitfall-different-memoized-functions*/} -To access the same cache, components must call the same memoized function. +Để truy cập cùng một bộ nhớ đệm, các component phải gọi cùng một hàm đã ghi nhớ. ```js [[1, 7, "getWeekReport"], [1, 7, "cache(calculateWeekReport)"], [1, 8, "getWeekReport"]] // Temperature.js @@ -116,7 +116,7 @@ import {cache} from 'react'; import {calculateWeekReport} from './report'; export function Temperature({cityData}) { - // 🚩 Wrong: Calling `cache` in component creates new `getWeekReport` for each render + // 🚩 Sai: Gọi `cache` trong component tạo ra `getWeekReport` mới cho mỗi lần hiển thị const getWeekReport = cache(calculateWeekReport); const report = getWeekReport(cityData); // ... @@ -128,7 +128,7 @@ export function Temperature({cityData}) { import {cache} from 'react'; import {calculateWeekReport} from './report'; -// 🚩 Wrong: `getWeekReport` is only accessible for `Precipitation` component. +// 🚩 Sai: `getWeekReport` chỉ có thể truy cập được cho component `Precipitation`. const getWeekReport = cache(calculateWeekReport); export function Precipitation({cityData}) { @@ -137,11 +137,11 @@ export function Precipitation({cityData}) { } ``` -In the above example, <CodeStep step={2}>`Precipitation`</CodeStep> and <CodeStep step={1}>`Temperature`</CodeStep> each call `cache` to create a new memoized function with their own cache look-up. If both components render for the same `cityData`, they will do duplicate work to call `calculateWeekReport`. +Trong ví dụ trên, <CodeStep step={2}>`Precipitation`</CodeStep> và <CodeStep step={1}>`Temperature`</CodeStep> mỗi component gọi `cache` để tạo một hàm đã ghi nhớ mới với bộ nhớ đệm riêng của chúng. Nếu cả hai component hiển thị cho cùng một `cityData`, chúng sẽ thực hiện công việc trùng lặp để gọi `calculateWeekReport`. -In addition, `Temperature` creates a <CodeStep step={1}>new memoized function</CodeStep> each time the component is rendered which doesn't allow for any cache sharing. +Ngoài ra, `Temperature` tạo ra một <CodeStep step={1}>hàm đã ghi nhớ mới</CodeStep> mỗi khi component được hiển thị, điều này không cho phép chia sẻ bộ nhớ đệm. -To maximize cache hits and reduce work, the two components should call the same memoized function to access the same cache. Instead, define the memoized function in a dedicated module that can be [`import`-ed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) across components. +Để tối đa hóa số lần truy cập bộ nhớ đệm và giảm công việc, hai component nên gọi cùng một hàm đã ghi nhớ để truy cập cùng một bộ nhớ đệm. Thay vào đó, hãy xác định hàm đã ghi nhớ trong một module chuyên dụng có thể được [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) trên các component. ```js [[3, 5, "export default cache(calculateWeekReport)"]] // getWeekReport.js @@ -156,7 +156,7 @@ export default cache(calculateWeekReport); import getWeekReport from './getWeekReport'; export default function Temperature({cityData}) { - const report = getWeekReport(cityData); + const report = getWeekReport(cityData); // ... } ``` @@ -170,48 +170,48 @@ export default function Precipitation({cityData}) { // ... } ``` -Here, both components call the <CodeStep step={3}>same memoized function</CodeStep> exported from `./getWeekReport.js` to read and write to the same cache. +Ở đây, cả hai component gọi <CodeStep step={3}>cùng một hàm đã ghi nhớ</CodeStep> được xuất từ `./getWeekReport.js` để đọc và ghi vào cùng một bộ nhớ đệm. </Pitfall> -### Share a snapshot of data {/*take-and-share-snapshot-of-data*/} +### Chia sẻ ảnh chụp nhanh dữ liệu {/*take-and-share-snapshot-of-data*/} -To share a snapshot of data between components, call `cache` with a data-fetching function like `fetch`. When multiple components make the same data fetch, only one request is made and the data returned is cached and shared across components. All components refer to the same snapshot of data across the server render. +Để chia sẻ ảnh chụp nhanh dữ liệu giữa các component, hãy gọi `cache` với một hàm tìm nạp dữ liệu như `fetch`. Khi nhiều component thực hiện cùng một lần tìm nạp dữ liệu, chỉ một yêu cầu được thực hiện và dữ liệu trả về được lưu vào bộ nhớ đệm và chia sẻ giữa các component. Tất cả các component tham chiếu đến cùng một ảnh chụp nhanh dữ liệu trên toàn bộ quá trình hiển thị máy chủ. ```js [[1, 4, "city"], [1, 5, "fetchTemperature(city)"], [2, 4, "getTemperature"], [2, 9, "getTemperature"], [1, 9, "city"], [2, 14, "getTemperature"], [1, 14, "city"]] import {cache} from 'react'; import {fetchTemperature} from './api.js'; const getTemperature = cache(async (city) => { - return await fetchTemperature(city); + return await fetchTemperature(city); }); async function AnimatedWeatherCard({city}) { - const temperature = await getTemperature(city); - // ... + const temperature = await getTemperature(city); + // ... } async function MinimalWeatherCard({city}) { - const temperature = await getTemperature(city); - // ... + const temperature = await getTemperature(city); + // ... } ``` -If `AnimatedWeatherCard` and `MinimalWeatherCard` both render for the same <CodeStep step={1}>city</CodeStep>, they will receive the same snapshot of data from the <CodeStep step={2}>memoized function</CodeStep>. +Nếu `AnimatedWeatherCard` và `MinimalWeatherCard` cả hai đều hiển thị cho cùng một <CodeStep step={1}>city</CodeStep>, chúng sẽ nhận được cùng một ảnh chụp nhanh dữ liệu từ <CodeStep step={2}>hàm đã ghi nhớ</CodeStep>. -If `AnimatedWeatherCard` and `MinimalWeatherCard` supply different <CodeStep step={1}>city</CodeStep> arguments to <CodeStep step={2}>`getTemperature`</CodeStep>, then `fetchTemperature` will be called twice and each call site will receive different data. +Nếu `AnimatedWeatherCard` và `MinimalWeatherCard` cung cấp các đối số <CodeStep step={1}>city</CodeStep> khác nhau cho <CodeStep step={2}>`getTemperature`</CodeStep>, thì `fetchTemperature` sẽ được gọi hai lần và mỗi vị trí gọi sẽ nhận được dữ liệu khác nhau. -The <CodeStep step={1}>city</CodeStep> acts as a cache key. +<CodeStep step={1}>city</CodeStep> đóng vai trò là một khóa bộ nhớ đệm. <Note> [//]: # 'TODO: add links to Server Components when merged.' -<CodeStep step={3}>Asynchronous rendering</CodeStep> is only supported for Server Components. +<CodeStep step={3}>Hiển thị không đồng bộ</CodeStep> chỉ được hỗ trợ cho Server Components. ```js [[3, 1, "async"], [3, 2, "await"]] async function AnimatedWeatherCard({city}) { - const temperature = await getTemperature(city); - // ... + const temperature = await getTemperature(city); + // ... } ``` [//]: # 'TODO: add link and mention to use documentation when merged' @@ -219,9 +219,9 @@ async function AnimatedWeatherCard({city}) { </Note> -### Preload data {/*preload-data*/} +### Tải trước dữ liệu {/*preload-data*/} -By caching a long-running data fetch, you can kick off asynchronous work prior to rendering the component. +Bằng cách lưu vào bộ nhớ đệm một lần tìm nạp dữ liệu chạy dài, bạn có thể bắt đầu công việc không đồng bộ trước khi hiển thị component. ```jsx [[2, 6, "await getUser(id)"], [1, 17, "getUser(id)"]] const getUser = cache(async (id) => { @@ -239,9 +239,9 @@ async function Profile({id}) { } function Page({id}) { - // ✅ Good: start fetching the user data + // ✅ Tốt: bắt đầu tìm nạp dữ liệu người dùng getUser(id); - // ... some computational work + // ... một số công việc tính toán return ( <> <Profile id={id} /> @@ -250,17 +250,17 @@ function Page({id}) { } ``` -When rendering `Page`, the component calls <CodeStep step={1}>`getUser`</CodeStep> but note that it doesn't use the returned data. This early <CodeStep step={1}>`getUser`</CodeStep> call kicks off the asynchronous database query that occurs while `Page` is doing other computational work and rendering children. +Khi hiển thị `Page`, component gọi <CodeStep step={1}>`getUser`</CodeStep> nhưng lưu ý rằng nó không sử dụng dữ liệu trả về. Lời gọi <CodeStep step={1}>`getUser`</CodeStep> sớm này bắt đầu truy vấn cơ sở dữ liệu không đồng bộ xảy ra trong khi `Page` đang thực hiện các công việc tính toán khác và hiển thị các component con. -When rendering `Profile`, we call <CodeStep step={2}>`getUser`</CodeStep> again. If the initial <CodeStep step={1}>`getUser`</CodeStep> call has already returned and cached the user data, when `Profile` <CodeStep step={2}>asks and waits for this data</CodeStep>, it can simply read from the cache without requiring another remote procedure call. If the <CodeStep step={1}> initial data request</CodeStep> hasn't been completed, preloading data in this pattern reduces delay in data-fetching. +Khi hiển thị `Profile`, chúng ta gọi lại <CodeStep step={2}>`getUser`</CodeStep>. Nếu lời gọi <CodeStep step={1}>`getUser`</CodeStep> ban đầu đã trả về và lưu dữ liệu người dùng vào bộ nhớ đệm, khi `Profile` <CodeStep step={2}>yêu cầu và chờ đợi dữ liệu này</CodeStep>, nó có thể chỉ cần đọc từ bộ nhớ đệm mà không yêu cầu một lệnh gọi thủ tục từ xa khác. Nếu <CodeStep step={1}>yêu cầu dữ liệu ban đầu</CodeStep> chưa hoàn thành, việc tải trước dữ liệu theo mẫu này sẽ giảm độ trễ trong việc tìm nạp dữ liệu. <DeepDive> -#### Caching asynchronous work {/*caching-asynchronous-work*/} +#### Lưu vào bộ nhớ đệm công việc không đồng bộ {/*caching-asynchronous-work*/} -When evaluating an [asynchronous function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), you will receive a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) for that work. The promise holds the state of that work (_pending_, _fulfilled_, _failed_) and its eventual settled result. +Khi đánh giá một [hàm không đồng bộ](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), bạn sẽ nhận được một [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) cho công việc đó. Promise giữ trạng thái của công việc đó (_pending_, _fulfilled_, _failed_) và kết quả cuối cùng đã được giải quyết của nó. -In this example, the asynchronous function <CodeStep step={1}>`fetchData`</CodeStep> returns a promise that is awaiting the `fetch`. +Trong ví dụ này, hàm không đồng bộ <CodeStep step={1}>`fetchData`</CodeStep> trả về một promise đang chờ `fetch`. ```js [[1, 1, "fetchData()"], [2, 8, "getData()"], [3, 10, "getData()"]] async function fetchData() { @@ -271,24 +271,24 @@ const getData = cache(fetchData); async function MyComponent() { getData(); - // ... some computational work + // ... một số công việc tính toán await getData(); // ... } ``` -In calling <CodeStep step={2}>`getData`</CodeStep> the first time, the promise returned from <CodeStep step={1}>`fetchData`</CodeStep> is cached. Subsequent look-ups will then return the same promise. +Khi gọi <CodeStep step={2}>`getData`</CodeStep> lần đầu tiên, promise được trả về từ <CodeStep step={1}>`fetchData`</CodeStep> được lưu vào bộ nhớ đệm. Các lần tra cứu tiếp theo sau đó sẽ trả về cùng một promise. -Notice that the first <CodeStep step={2}>`getData`</CodeStep> call does not `await` whereas the <CodeStep step={3}>second</CodeStep> does. [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) is a JavaScript operator that will wait and return the settled result of the promise. The first <CodeStep step={2}>`getData`</CodeStep> call simply initiates the `fetch` to cache the promise for the second <CodeStep step={3}>`getData`</CodeStep> to look-up. +Lưu ý rằng lời gọi <CodeStep step={2}>`getData`</CodeStep> đầu tiên không có `await` trong khi <CodeStep step={3}>lần thứ hai</CodeStep> thì có. [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) là một toán tử JavaScript sẽ chờ và trả về kết quả đã được giải quyết của promise. Lời gọi <CodeStep step={2}>`getData`</CodeStep> đầu tiên chỉ đơn giản là bắt đầu `fetch` để lưu promise vào bộ nhớ đệm cho <CodeStep step={3}>`getData`</CodeStep> thứ hai tra cứu. -If by the <CodeStep step={3}>second call</CodeStep> the promise is still _pending_, then `await` will pause for the result. The optimization is that while we wait on the `fetch`, React can continue with computational work, thus reducing the wait time for the <CodeStep step={3}>second call</CodeStep>. +Nếu đến <CodeStep step={3}>lần gọi thứ hai</CodeStep> promise vẫn đang ở trạng thái _pending_, thì `await` sẽ tạm dừng để chờ kết quả. Tối ưu hóa là trong khi chúng ta chờ `fetch`, React có thể tiếp tục với công việc tính toán, do đó giảm thời gian chờ cho <CodeStep step={3}>lần gọi thứ hai</CodeStep>. -If the promise is already settled, either to an error or the _fulfilled_ result, `await` will return that value immediately. In both outcomes, there is a performance benefit. +Nếu promise đã được giải quyết, cho dù là một lỗi hay kết quả _fulfilled_, `await` sẽ trả về giá trị đó ngay lập tức. Trong cả hai kết quả, đều có một lợi ích về hiệu suất. </DeepDive> <Pitfall> -##### Calling a memoized function outside of a component will not use the cache. {/*pitfall-memoized-call-outside-component*/} +##### Gọi một hàm đã ghi nhớ bên ngoài một component sẽ không sử dụng bộ nhớ đệm. {/*pitfall-memoized-call-outside-component*/} ```jsx [[1, 3, "getUser"]] import {cache} from 'react'; @@ -297,31 +297,31 @@ const getUser = cache(async (userId) => { return await db.user.query(userId); }); -// 🚩 Wrong: Calling memoized function outside of component will not memoize. +// 🚩 Sai: Gọi hàm đã ghi nhớ bên ngoài component sẽ không ghi nhớ. getUser('demo-id'); async function DemoProfile() { - // ✅ Good: `getUser` will memoize. + // ✅ Tốt: `getUser` sẽ ghi nhớ. const user = await getUser('demo-id'); return <Profile user={user} />; } ``` -React only provides cache access to the memoized function in a component. When calling <CodeStep step={1}>`getUser`</CodeStep> outside of a component, it will still evaluate the function but not read or update the cache. +React chỉ cung cấp quyền truy cập bộ nhớ đệm cho hàm đã ghi nhớ trong một component. Khi gọi <CodeStep step={1}>`getUser`</CodeStep> bên ngoài một component, nó vẫn sẽ đánh giá hàm nhưng không đọc hoặc cập nhật bộ nhớ đệm. -This is because cache access is provided through a [context](/learn/passing-data-deeply-with-context) which is only accessible from a component. +Điều này là do quyền truy cập bộ nhớ đệm được cung cấp thông qua một [context](/learn/passing-data-deeply-with-context) chỉ có thể truy cập được từ một component. </Pitfall> <DeepDive> -#### When should I use `cache`, [`memo`](/reference/react/memo), or [`useMemo`](/reference/react/useMemo)? {/*cache-memo-usememo*/} +#### Khi nào tôi nên sử dụng `cache`, [`memo`](/reference/react/memo) hoặc [`useMemo`](/reference/react/useMemo)? {/*cache-memo-usememo*/} -All mentioned APIs offer memoization but the difference is what they're intended to memoize, who can access the cache, and when their cache is invalidated. +Tất cả các API được đề cập đều cung cấp khả năng ghi nhớ, nhưng sự khác biệt là những gì chúng dự định ghi nhớ, ai có thể truy cập bộ nhớ đệm và khi nào bộ nhớ đệm của chúng bị vô hiệu. #### `useMemo` {/*deep-dive-use-memo*/} -In general, you should use [`useMemo`](/reference/react/useMemo) for caching a expensive computation in a Client Component across renders. As an example, to memoize a transformation of data within a component. +Nói chung, bạn nên sử dụng [`useMemo`](/reference/react/useMemo) để lưu vào bộ nhớ đệm một phép tính tốn kém trong một Client Component trên các lần hiển thị. Ví dụ: để ghi nhớ một phép biến đổi dữ liệu trong một component. ```jsx {4} 'use client'; @@ -341,13 +341,13 @@ function App() { ); } ``` -In this example, `App` renders two `WeatherReport`s with the same record. Even though both components do the same work, they cannot share work. `useMemo`'s cache is only local to the component. +Trong ví dụ này, `App` hiển thị hai `WeatherReport` với cùng một bản ghi. Mặc dù cả hai component đều thực hiện cùng một công việc, nhưng chúng không thể chia sẻ công việc. Bộ nhớ đệm của `useMemo` chỉ cục bộ cho component. -However, `useMemo` does ensure that if `App` re-renders and the `record` object doesn't change, each component instance would skip work and use the memoized value of `avgTemp`. `useMemo` will only cache the last computation of `avgTemp` with the given dependencies. +Tuy nhiên, `useMemo` đảm bảo rằng nếu `App` hiển thị lại và đối tượng `record` không thay đổi, mỗi phiên bản component sẽ bỏ qua công việc và sử dụng giá trị đã ghi nhớ của `avgTemp`. `useMemo` sẽ chỉ lưu vào bộ nhớ đệm phép tính cuối cùng của `avgTemp` với các dependency đã cho. #### `cache` {/*deep-dive-cache*/} -In general, you should use `cache` in Server Components to memoize work that can be shared across components. +Nói chung, bạn nên sử dụng `cache` trong Server Components để ghi nhớ công việc có thể được chia sẻ giữa các component. ```js [[1, 12, "<WeatherReport city={city} />"], [3, 13, "<WeatherReport city={city} />"], [2, 1, "cache(fetchReport)"]] const cachedFetchReport = cache(fetchReport); @@ -367,13 +367,13 @@ function App() { ); } ``` -Re-writing the previous example to use `cache`, in this case the <CodeStep step={3}>second instance of `WeatherReport`</CodeStep> will be able to skip duplicate work and read from the same cache as the <CodeStep step={1}>first `WeatherReport`</CodeStep>. Another difference from the previous example is that `cache` is also recommended for <CodeStep step={2}>memoizing data fetches</CodeStep>, unlike `useMemo` which should only be used for computations. +Viết lại ví dụ trước để sử dụng `cache`, trong trường hợp này, <CodeStep step={3}>phiên bản thứ hai của `WeatherReport`</CodeStep> sẽ có thể bỏ qua công việc trùng lặp và đọc từ cùng một bộ nhớ đệm như <CodeStep step={1}>`WeatherReport` đầu tiên</CodeStep>. Một điểm khác biệt nữa so với ví dụ trước là `cache` cũng được khuyến nghị cho <CodeStep step={2}>ghi nhớ các lần tìm nạp dữ liệu</CodeStep>, không giống như `useMemo` chỉ nên được sử dụng cho các phép tính. -At this time, `cache` should only be used in Server Components and the cache will be invalidated across server requests. +Tại thời điểm này, `cache` chỉ nên được sử dụng trong Server Components và bộ nhớ đệm sẽ bị vô hiệu trên các yêu cầu máy chủ. #### `memo` {/*deep-dive-memo*/} -You should use [`memo`](reference/react/memo) to prevent a component re-rendering if its props are unchanged. +Bạn nên sử dụng [`memo`](reference/react/memo) để ngăn một component hiển thị lại nếu các prop của nó không thay đổi. ```js 'use client'; @@ -396,27 +396,27 @@ function App() { } ``` -In this example, both `MemoWeatherReport` components will call `calculateAvg` when first rendered. However, if `App` re-renders, with no changes to `record`, none of the props have changed and `MemoWeatherReport` will not re-render. +Trong ví dụ này, cả hai component `MemoWeatherReport` sẽ gọi `calculateAvg` khi được hiển thị lần đầu tiên. Tuy nhiên, nếu `App` hiển thị lại, mà không có thay đổi nào đối với `record`, không có prop nào thay đổi và `MemoWeatherReport` sẽ không hiển thị lại. -Compared to `useMemo`, `memo` memoizes the component render based on props vs. specific computations. Similar to `useMemo`, the memoized component only caches the last render with the last prop values. Once the props change, the cache invalidates and the component re-renders. +So với `useMemo`, `memo` ghi nhớ quá trình hiển thị component dựa trên các prop so với các phép tính cụ thể. Tương tự như `useMemo`, component đã ghi nhớ chỉ lưu vào bộ nhớ đệm lần hiển thị cuối cùng với các giá trị prop cuối cùng. Khi các prop thay đổi, bộ nhớ đệm sẽ bị vô hiệu và component hiển thị lại. </DeepDive> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My memoized function still runs even though I've called it with the same arguments {/*memoized-function-still-runs*/} +### Hàm đã ghi nhớ của tôi vẫn chạy mặc dù tôi đã gọi nó với cùng các đối số {/*memoized-function-still-runs*/} -See prior mentioned pitfalls -* [Calling different memoized functions will read from different caches.](#pitfall-different-memoized-functions) -* [Calling a memoized function outside of a component will not use the cache.](#pitfall-memoized-call-outside-component) +Xem các cạm bẫy đã đề cập trước đó +* [Gọi các hàm đã ghi nhớ khác nhau sẽ đọc từ các bộ nhớ đệm khác nhau.](#pitfall-different-memoized-functions) +* [Gọi một hàm đã ghi nhớ bên ngoài một component sẽ không sử dụng bộ nhớ đệm.](#pitfall-memoized-call-outside-component) -If none of the above apply, it may be a problem with how React checks if something exists in cache. +Nếu không có điều nào ở trên áp dụng, thì có thể có vấn đề với cách React kiểm tra xem một cái gì đó có tồn tại trong bộ nhớ đệm hay không. -If your arguments are not [primitives](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) (ex. objects, functions, arrays), ensure you're passing the same object reference. +Nếu các đối số của bạn không phải là [kiểu nguyên thủy](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) (ví dụ: đối tượng, hàm, mảng), hãy đảm bảo bạn đang truyền cùng một tham chiếu đối tượng. -When calling a memoized function, React will look up the input arguments to see if a result is already cached. React will use shallow equality of the arguments to determine if there is a cache hit. +Khi gọi một hàm đã ghi nhớ, React sẽ tra cứu các đối số đầu vào để xem kết quả đã được lưu vào bộ nhớ đệm hay chưa. React sẽ sử dụng so sánh nông của các đối số để xác định xem có cache hit hay không. ```js import {cache} from 'react'; @@ -426,7 +426,7 @@ const calculateNorm = cache((vector) => { }); function MapMarker(props) { - // 🚩 Wrong: props is an object that changes every render. + // 🚩 Sai: props là một đối tượng thay đổi mỗi lần hiển thị. const length = calculateNorm(props); // ... } @@ -441,9 +441,9 @@ function App() { } ``` -In this case the two `MapMarker`s look like they're doing the same work and calling `calculateNorm` with the same value of `{x: 10, y: 10, z:10}`. Even though the objects contain the same values, they are not the same object reference as each component creates its own `props` object. +Trong trường hợp này, hai `MapMarker` trông như thể chúng đang thực hiện cùng một công việc và gọi `calculateNorm` với cùng một giá trị của `{x: 10, y: 10, z:10}`. Mặc dù các đối tượng chứa cùng các giá trị, nhưng chúng không phải là cùng một tham chiếu đối tượng vì mỗi component tạo đối tượng `props` riêng của nó. -React will call [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) on the input to verify if there is a cache hit. +React sẽ gọi [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) trên đầu vào để xác minh xem có cache hit hay không. ```js {3,9} import {cache} from 'react'; @@ -453,7 +453,7 @@ const calculateNorm = cache((x, y, z) => { }); function MapMarker(props) { - // ✅ Good: Pass primitives to memoized function + // ✅ Tốt: Truyền các kiểu nguyên thủy cho hàm đã ghi nhớ const length = calculateNorm(props.x, props.y, props.z); // ... } @@ -468,9 +468,9 @@ function App() { } ``` -One way to address this could be to pass the vector dimensions to `calculateNorm`. This works because the dimensions themselves are primitives. +Một cách để giải quyết vấn đề này có thể là truyền các chiều của vector cho `calculateNorm`. Điều này hoạt động vì bản thân các chiều là các kiểu nguyên thủy. -Another solution may be to pass the vector object itself as a prop to the component. We'll need to pass the same object to both component instances. +Một giải pháp khác có thể là truyền chính đối tượng vector làm một prop cho component. Chúng ta sẽ cần truyền cùng một đối tượng cho cả hai phiên bản component. ```js {3,9,14} import {cache} from 'react'; @@ -480,7 +480,7 @@ const calculateNorm = cache((vector) => { }); function MapMarker(props) { - // ✅ Good: Pass the same `vector` object + // ✅ Tốt: Truyền cùng một đối tượng `vector` const length = calculateNorm(props.vector); // ... } @@ -495,4 +495,3 @@ function App() { ); } ``` - diff --git a/src/content/reference/react/captureOwnerStack.md b/src/content/reference/react/captureOwnerStack.md index 6d8cc502d..f91222142 100644 --- a/src/content/reference/react/captureOwnerStack.md +++ b/src/content/reference/react/captureOwnerStack.md @@ -4,7 +4,7 @@ title: captureOwnerStack <Intro> -`captureOwnerStack` reads the current Owner Stack in development and returns it as a string if available. +`captureOwnerStack` đọc Owner Stack hiện tại trong quá trình phát triển và trả về nó dưới dạng một chuỗi nếu có. ```js const stack = captureOwnerStack(); @@ -16,11 +16,11 @@ const stack = captureOwnerStack(); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `captureOwnerStack()` {/*captureownerstack*/} -Call `captureOwnerStack` to get the current Owner Stack. +Gọi `captureOwnerStack` để lấy Owner Stack hiện tại. ```js {5,5} import * as React from 'react'; @@ -33,33 +33,33 @@ function Component() { } ``` -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -`captureOwnerStack` does not take any parameters. +`captureOwnerStack` không nhận bất kỳ tham số nào. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`captureOwnerStack` returns `string | null`. +`captureOwnerStack` trả về `string | null`. -Owner Stacks are available in -- Component render -- Effects (e.g. `useEffect`) -- React's event handlers (e.g. `<button onClick={...} />`) -- React error handlers ([React Root options](/reference/react-dom/client/createRoot#parameters) `onCaughtError`, `onRecoverableError`, and `onUncaughtError`) +Owner Stack có sẵn trong +- Quá trình render Component +- Các Effect (ví dụ: `useEffect`) +- Các trình xử lý sự kiện của React (ví dụ: `<button onClick={...} />`) +- Các trình xử lý lỗi của React ([Tùy chọn React Root](/reference/react-dom/client/createRoot#parameters) `onCaughtError`, `onRecoverableError` và `onUncaughtError`) -If no Owner Stack is available, `null` is returned (see [Troubleshooting: The Owner Stack is `null`](#the-owner-stack-is-null)). +Nếu không có Owner Stack nào khả dụng, `null` sẽ được trả về (xem [Khắc phục sự cố: Owner Stack là `null`](#the-owner-stack-is-null)). -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -- Owner Stacks are only available in development. `captureOwnerStack` will always return `null` outside of development. +- Owner Stack chỉ khả dụng trong quá trình phát triển. `captureOwnerStack` sẽ luôn trả về `null` bên ngoài quá trình phát triển. <DeepDive> -#### Owner Stack vs Component Stack {/*owner-stack-vs-component-stack*/} +#### Owner Stack so với Component Stack {/*owner-stack-vs-component-stack*/} -The Owner Stack is different from the Component Stack available in React error handlers like [`errorInfo.componentStack` in `onUncaughtError`](/reference/react-dom/client/hydrateRoot#show-a-dialog-for-uncaught-errors). +Owner Stack khác với Component Stack có sẵn trong các trình xử lý lỗi của React như [`errorInfo.componentStack` trong `onUncaughtError`](/reference/react-dom/client/hydrateRoot#show-a-dialog-for-uncaught-errors). -For example, consider the following code: +Ví dụ: xem xét đoạn mã sau: <Sandpack> @@ -105,11 +105,11 @@ import './styles.css'; createRoot(document.createElement('div'), { onUncaughtError: (error, errorInfo) => { - // The stacks are logged instead of showing them in the UI directly to - // highlight that browsers will apply sourcemaps to the logged stacks. - // Note that sourcemapping is only applied in the real browser console not - // in the fake one displayed on this page. - // Press "fork" to be able to view the sourcemapped stack in a real console. + // Các stack được ghi lại thay vì hiển thị chúng trực tiếp trong UI để + // làm nổi bật rằng các trình duyệt sẽ áp dụng sourcemap cho các stack đã ghi lại. + // Lưu ý rằng sourcemapping chỉ được áp dụng trong bảng điều khiển trình duyệt thực chứ không + // trong bảng điều khiển giả được hiển thị trên trang này. + // Nhấn "fork" để có thể xem stack đã được sourcemap trong một bảng điều khiển thực. console.log(errorInfo.componentStack); console.log(captureOwnerStack()); }, @@ -129,15 +129,15 @@ createRoot(document.createElement('div'), { <title>Document</title> </head> <body> - <p>Check the console output.</p> + <p>Kiểm tra đầu ra của console.</p> </body> </html> ``` </Sandpack> -`SubComponent` would throw an error. -The Component Stack of that error would be +`SubComponent` sẽ ném ra một lỗi. +Component Stack của lỗi đó sẽ là ``` at SubComponent @@ -148,23 +148,23 @@ at React.Suspense at App ``` -However, the Owner Stack would only read +Tuy nhiên, Owner Stack sẽ chỉ đọc ``` at Component ``` -Neither `App` nor the DOM components (e.g. `fieldset`) are considered Owners in this Stack since they didn't contribute to "creating" the node containing `SubComponent`. `App` and DOM components only forwarded the node. `App` just rendered the `children` node as opposed to `Component` which created a node containing `SubComponent` via `<SubComponent />`. +Cả `App` và các component DOM (ví dụ: `fieldset`) đều không được coi là Owner trong Stack này vì chúng không đóng góp vào việc "tạo" node chứa `SubComponent`. `App` và các component DOM chỉ chuyển tiếp node. `App` chỉ render node `children` trái ngược với `Component`, component này đã tạo một node chứa `SubComponent` thông qua `<SubComponent />`. -Neither `Navigation` nor `legend` are in the stack at all since it's only a sibling to a node containing `<SubComponent />`. +Cả `Navigation` và `legend` đều không có trong stack vì nó chỉ là một sibling của một node chứa `<SubComponent />`. -`SubComponent` is omitted because it's already part of the callstack. +`SubComponent` bị bỏ qua vì nó đã là một phần của callstack. </DeepDive> -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Enhance a custom error overlay {/*enhance-a-custom-error-overlay*/} +### Cải thiện lớp phủ lỗi tùy chỉnh {/*enhance-a-custom-error-overlay*/} ```js [[1, 5, "console.error"], [4, 7, "captureOwnerStack"]] import { captureOwnerStack } from "react"; @@ -175,15 +175,15 @@ console.error = function patchedConsoleError(...args) { originalConsoleError.apply(console, args); const ownerStack = captureOwnerStack(); onConsoleError({ - // Keep in mind that in a real application, console.error can be - // called with multiple arguments which you should account for. + // Lưu ý rằng trong một ứng dụng thực tế, console.error có thể được + // gọi với nhiều đối số mà bạn nên tính đến. consoleMessage: args[0], ownerStack, }); }; ``` -If you intercept <CodeStep step={1}>`console.error`</CodeStep> calls to highlight them in an error overlay, you can call <CodeStep step={2}>`captureOwnerStack`</CodeStep> to include the Owner Stack. +Nếu bạn chặn các lệnh gọi <CodeStep step={1}>`console.error`</CodeStep> để làm nổi bật chúng trong lớp phủ lỗi, bạn có thể gọi <CodeStep step={2}>`captureOwnerStack`</CodeStep> để bao gồm Owner Stack. <Sandpack> @@ -265,15 +265,15 @@ pre.nowrap { <!DOCTYPE html> <html> <head> - <title>My app</title> + <title>Ứng dụng của tôi</title> </head> <body> <!-- - Error dialog in raw HTML - since an error in the React app may crash. + Hộp thoại lỗi trong HTML thuần + vì lỗi trong ứng dụng React có thể bị sập. --> <div id="error-dialog" class="hidden"> - <h1 id="error-title" class="text-red">Error</h1> + <h1 id="error-title" class="text-red">Lỗi</h1> <p> <pre id="error-body"></pre> </p> @@ -284,10 +284,10 @@ pre.nowrap { class="mb-10" onclick="document.getElementById('error-dialog').classList.add('hidden')" > - Close + Đóng </button> </div> -<!-- This is the DOM node --> +<!-- Đây là node DOM --> <div id="root"></div> </body> </html> @@ -301,13 +301,13 @@ export function onConsoleError({ consoleMessage, ownerStack }) { const errorBody = document.getElementById("error-body"); const errorOwnerStack = document.getElementById("error-owner-stack"); - // Display console.error() message + // Hiển thị thông báo console.error() errorBody.innerText = consoleMessage; - // Display owner stack + // Hiển thị owner stack errorOwnerStack.innerText = ownerStack; - // Show the dialog + // Hiển thị hộp thoại errorDialog.classList.remove("hidden"); } ``` @@ -324,8 +324,8 @@ console.error = function patchedConsoleError(...args) { originalConsoleError.apply(console, args); const ownerStack = captureOwnerStack(); onConsoleError({ - // Keep in mind that in a real application, console.error can be - // called with multiple arguments which you should account for. + // Lưu ý rằng trong một ứng dụng thực tế, console.error có thể được + // gọi với nhiều đối số mà bạn nên tính đến. consoleMessage: args[0], ownerStack, }); @@ -337,7 +337,7 @@ createRoot(container).render(<App />); ```js src/App.js function Component() { - return <button onClick={() => console.error('Some console error')}>Trigger console.error()</button>; + return <button onClick={() => console.error('Some console error')}>Kích hoạt console.error()</button>; } export default function App() { @@ -347,13 +347,13 @@ export default function App() { </Sandpack> -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### The Owner Stack is `null` {/*the-owner-stack-is-null*/} +### Owner Stack là `null` {/*the-owner-stack-is-null*/} -The call of `captureOwnerStack` happened outside of a React controlled function e.g. in a `setTimeout` callback, after a `fetch` call or in a custom DOM event handler. During render, Effects, React event handlers, and React error handlers (e.g. `hydrateRoot#options.onCaughtError`) Owner Stacks should be available. +Lệnh gọi `captureOwnerStack` xảy ra bên ngoài một hàm được kiểm soát bởi React, ví dụ: trong một callback `setTimeout`, sau một lệnh gọi `fetch` hoặc trong một trình xử lý sự kiện DOM tùy chỉnh. Trong quá trình render, Effects, trình xử lý sự kiện React và trình xử lý lỗi React (ví dụ: `hydrateRoot#options.onCaughtError`), Owner Stack sẽ khả dụng. -In the example below, clicking the button will log an empty Owner Stack because `captureOwnerStack` was called during a custom DOM event handler. The Owner Stack must be captured earlier e.g. by moving the call of `captureOwnerStack` into the Effect body. +Trong ví dụ dưới đây, việc nhấp vào nút sẽ ghi lại một Owner Stack trống vì `captureOwnerStack` đã được gọi trong một trình xử lý sự kiện DOM tùy chỉnh. Owner Stack phải được chụp sớm hơn, ví dụ: bằng cách di chuyển lệnh gọi `captureOwnerStack` vào phần thân Effect. <Sandpack> ```js @@ -361,10 +361,10 @@ import {captureOwnerStack, useEffect} from 'react'; export default function App() { useEffect(() => { - // Should call `captureOwnerStack` here. + // Nên gọi `captureOwnerStack` ở đây. function handleEvent() { - // Calling it in a custom DOM event handler is too late. - // The Owner Stack will be `null` at this point. + // Gọi nó trong một trình xử lý sự kiện DOM tùy chỉnh là quá muộn. + // Owner Stack sẽ là `null` tại thời điểm này. console.log('Owner Stack: ', captureOwnerStack()); } @@ -375,20 +375,20 @@ export default function App() { } }) - return <button>Click me to see that Owner Stacks are not available in custom DOM event handlers</button>; + return <button>Nhấp vào tôi để thấy rằng Owner Stack không khả dụng trong trình xử lý sự kiện DOM tùy chỉnh</button>; } ``` </Sandpack> -### `captureOwnerStack` is not available {/*captureownerstack-is-not-available*/} +### `captureOwnerStack` không khả dụng {/*captureownerstack-is-not-available*/} -`captureOwnerStack` is only exported in development builds. It will be `undefined` in production builds. If `captureOwnerStack` is used in files that are bundled for production and development, you should conditionally access it from a namespace import. +`captureOwnerStack` chỉ được xuất trong các bản dựng dành cho quá trình phát triển. Nó sẽ là `undefined` trong các bản dựng production. Nếu `captureOwnerStack` được sử dụng trong các tệp được đóng gói cho cả production và development, bạn nên truy cập có điều kiện từ một namespace import. ```js -// Don't use named imports of `captureOwnerStack` in files that are bundled for development and production. +// Không sử dụng named import của `captureOwnerStack` trong các tệp được đóng gói cho development và production. import {captureOwnerStack} from 'react'; -// Use a namespace import instead and access `captureOwnerStack` conditionally. +// Sử dụng namespace import thay thế và truy cập `captureOwnerStack` có điều kiện. import * as React from 'react'; if (process.env.NODE_ENV !== 'production') { diff --git a/src/content/reference/react/cloneElement.md b/src/content/reference/react/cloneElement.md index 6bcea51b0..0cab417ac 100644 --- a/src/content/reference/react/cloneElement.md +++ b/src/content/reference/react/cloneElement.md @@ -4,13 +4,13 @@ title: cloneElement <Pitfall> -Using `cloneElement` is uncommon and can lead to fragile code. [See common alternatives.](#alternatives) +Việc sử dụng `cloneElement` là không phổ biến và có thể dẫn đến code dễ bị lỗi. [Xem các lựa chọn thay thế phổ biến.](#alternatives) </Pitfall> <Intro> -`cloneElement` lets you create a new React element using another element as a starting point. +`cloneElement` cho phép bạn tạo một React element mới bằng cách sử dụng một element khác làm điểm bắt đầu. ```js const clonedElement = cloneElement(element, props, ...children) @@ -22,11 +22,11 @@ const clonedElement = cloneElement(element, props, ...children) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `cloneElement(element, props, ...children)` {/*cloneelement*/} -Call `cloneElement` to create a React element based on the `element`, but with different `props` and `children`: +Gọi `cloneElement` để tạo một React element dựa trên `element`, nhưng với `props` và `children` khác: ```js import { cloneElement } from 'react'; @@ -43,42 +43,42 @@ const clonedElement = cloneElement( console.log(clonedElement); // <Row title="Cabbage" isHighlighted={true}>Goodbye</Row> ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `element`: The `element` argument must be a valid React element. For example, it could be a JSX node like `<Something />`, the result of calling [`createElement`](/reference/react/createElement), or the result of another `cloneElement` call. +* `element`: Đối số `element` phải là một React element hợp lệ. Ví dụ: nó có thể là một JSX node như `<Something />`, kết quả của việc gọi [`createElement`](/reference/react/createElement), hoặc kết quả của một lệnh gọi `cloneElement` khác. -* `props`: The `props` argument must either be an object or `null`. If you pass `null`, the cloned element will retain all of the original `element.props`. Otherwise, for every prop in the `props` object, the returned element will "prefer" the value from `props` over the value from `element.props`. The rest of the props will be filled from the original `element.props`. If you pass `props.key` or `props.ref`, they will replace the original ones. +* `props`: Đối số `props` phải là một object hoặc `null`. Nếu bạn truyền `null`, element được clone sẽ giữ lại tất cả các `element.props` ban đầu. Nếu không, đối với mỗi prop trong object `props`, element trả về sẽ "ưu tiên" giá trị từ `props` hơn giá trị từ `element.props`. Các prop còn lại sẽ được lấy từ `element.props` ban đầu. Nếu bạn truyền `props.key` hoặc `props.ref`, chúng sẽ thay thế các giá trị ban đầu. -* **optional** `...children`: Zero or more child nodes. They can be any React nodes, including React elements, strings, numbers, [portals](/reference/react-dom/createPortal), empty nodes (`null`, `undefined`, `true`, and `false`), and arrays of React nodes. If you don't pass any `...children` arguments, the original `element.props.children` will be preserved. +* **tùy chọn** `...children`: Không hoặc nhiều child node. Chúng có thể là bất kỳ React node nào, bao gồm React element, string, number, [portal](/reference/react-dom/createPortal), empty node (`null`, `undefined`, `true` và `false`) và mảng các React node. Nếu bạn không truyền bất kỳ đối số `...children` nào, `element.props.children` ban đầu sẽ được giữ nguyên. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`cloneElement` returns a React element object with a few properties: +`cloneElement` trả về một đối tượng React element với một vài thuộc tính: -* `type`: Same as `element.type`. -* `props`: The result of shallowly merging `element.props` with the overriding `props` you have passed. -* `ref`: The original `element.ref`, unless it was overridden by `props.ref`. -* `key`: The original `element.key`, unless it was overridden by `props.key`. +* `type`: Giống như `element.type`. +* `props`: Kết quả của việc hợp nhất nông `element.props` với `props` ghi đè mà bạn đã truyền. +* `ref`: `element.ref` ban đầu, trừ khi nó bị ghi đè bởi `props.ref`. +* `key`: `element.key` ban đầu, trừ khi nó bị ghi đè bởi `props.key`. -Usually, you'll return the element from your component or make it a child of another element. Although you may read the element's properties, it's best to treat every element as opaque after it's created, and only render it. +Thông thường, bạn sẽ trả về element từ component của mình hoặc tạo nó thành một child của một element khác. Mặc dù bạn có thể đọc các thuộc tính của element, nhưng tốt nhất là coi mọi element là không rõ ràng sau khi nó được tạo và chỉ render nó. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Cloning an element **does not modify the original element.** +* Việc clone một element **không sửa đổi element ban đầu.** -* You should only **pass children as multiple arguments to `cloneElement` if they are all statically known,** like `cloneElement(element, null, child1, child2, child3)`. If your children are dynamic, pass the entire array as the third argument: `cloneElement(element, null, listItems)`. This ensures that React will [warn you about missing `key`s](/learn/rendering-lists#keeping-list-items-in-order-with-key) for any dynamic lists. For static lists this is not necessary because they never reorder. +* Bạn chỉ nên **truyền children dưới dạng nhiều đối số cho `cloneElement` nếu tất cả chúng đều được biết tĩnh,** như `cloneElement(element, null, child1, child2, child3)`. Nếu children của bạn là động, hãy truyền toàn bộ mảng làm đối số thứ ba: `cloneElement(element, null, listItems)`. Điều này đảm bảo rằng React sẽ [cảnh báo bạn về việc thiếu `key`](/learn/rendering-lists#keeping-list-items-in-order-with-key) cho bất kỳ danh sách động nào. Đối với danh sách tĩnh, điều này là không cần thiết vì chúng không bao giờ sắp xếp lại. -* `cloneElement` makes it harder to trace the data flow, so **try the [alternatives](#alternatives) instead.** +* `cloneElement` làm cho việc theo dõi luồng dữ liệu trở nên khó khăn hơn, vì vậy **hãy thử các [lựa chọn thay thế](#alternatives) thay thế.** --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Overriding props of an element {/*overriding-props-of-an-element*/} +### Ghi đè props của một element {/*overriding-props-of-an-element*/} -To override the props of some <CodeStep step={1}>React element</CodeStep>, pass it to `cloneElement` with the <CodeStep step={2}>props you want to override</CodeStep>: +Để ghi đè các props của một <CodeStep step={1}>React element</CodeStep>, hãy truyền nó cho `cloneElement` với <CodeStep step={2}>các props bạn muốn ghi đè</CodeStep>: ```js [[1, 5, "<Row title=\\"Cabbage\\" />"], [2, 6, "{ isHighlighted: true }"], [3, 4, "clonedElement"]] import { cloneElement } from 'react'; @@ -90,11 +90,11 @@ const clonedElement = cloneElement( ); ``` -Here, the resulting <CodeStep step={3}>cloned element</CodeStep> will be `<Row title="Cabbage" isHighlighted={true} />`. +Ở đây, <CodeStep step={3}>element được clone</CodeStep> kết quả sẽ là `<Row title="Cabbage" isHighlighted={true} />`. -**Let's walk through an example to see when it's useful.** +**Hãy xem qua một ví dụ để xem khi nào nó hữu ích.** -Imagine a `List` component that renders its [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) as a list of selectable rows with a "Next" button that changes which row is selected. The `List` component needs to render the selected `Row` differently, so it clones every `<Row>` child that it has received, and adds an extra `isHighlighted: true` or `isHighlighted: false` prop: +Hãy tưởng tượng một component `List` render [`children`](/learn/passing-props-to-a-component#passing-jsx-as-children) của nó dưới dạng một danh sách các hàng có thể chọn với một nút "Next" thay đổi hàng nào được chọn. Component `List` cần render `Row` đã chọn khác nhau, vì vậy nó clone mọi child `<Row>` mà nó đã nhận và thêm một prop `isHighlighted: true` hoặc `isHighlighted: false` bổ sung: ```js {6-8} export default function List({ children }) { @@ -108,7 +108,7 @@ export default function List({ children }) { )} ``` -Let's say the original JSX received by `List` looks like this: +Giả sử JSX ban đầu được `List` nhận trông như thế này: ```js {2-4} <List> @@ -118,7 +118,7 @@ Let's say the original JSX received by `List` looks like this: </List> ``` -By cloning its children, the `List` can pass extra information to every `Row` inside. The result looks like this: +Bằng cách clone children của nó, `List` có thể truyền thêm thông tin cho mọi `Row` bên trong. Kết quả trông như thế này: ```js {4,8,12} <List> @@ -137,7 +137,7 @@ By cloning its children, the `List` can pass extra information to every `Row` in </List> ``` -Notice how pressing "Next" updates the state of the `List`, and highlights a different row: +Lưu ý cách nhấn "Next" cập nhật trạng thái của `List` và làm nổi bật một hàng khác: <Sandpack> @@ -232,21 +232,21 @@ button { </Sandpack> -To summarize, the `List` cloned the `<Row />` elements it received and added an extra prop to them. +Tóm lại, `List` đã clone các element `<Row />` mà nó nhận được và thêm một prop bổ sung cho chúng. <Pitfall> -Cloning children makes it hard to tell how the data flows through your app. Try one of the [alternatives.](#alternatives) +Việc clone children gây khó khăn cho việc biết dữ liệu truyền qua ứng dụng của bạn như thế nào. Hãy thử một trong các [lựa chọn thay thế.](#alternatives) </Pitfall> --- -## Alternatives {/*alternatives*/} +## Các lựa chọn thay thế {/*alternatives*/} -### Passing data with a render prop {/*passing-data-with-a-render-prop*/} +### Truyền dữ liệu bằng render prop {/*passing-data-with-a-render-prop*/} -Instead of using `cloneElement`, consider accepting a *render prop* like `renderItem`. Here, `List` receives `renderItem` as a prop. `List` calls `renderItem` for every item and passes `isHighlighted` as an argument: +Thay vì sử dụng `cloneElement`, hãy cân nhắc chấp nhận một *render prop* như `renderItem`. Ở đây, `List` nhận `renderItem` làm một prop. `List` gọi `renderItem` cho mỗi item và truyền `isHighlighted` làm một đối số: ```js {1,7} export default function List({ items, renderItem }) { @@ -259,7 +259,7 @@ export default function List({ items, renderItem }) { })} ``` -The `renderItem` prop is called a "render prop" because it's a prop that specifies how to render something. For example, you can pass a `renderItem` implementation that renders a `<Row>` with the given `isHighlighted` value: +Prop `renderItem` được gọi là "render prop" vì nó là một prop chỉ định cách render một thứ gì đó. Ví dụ: bạn có thể truyền một implementation `renderItem` render một `<Row>` với giá trị `isHighlighted` đã cho: ```js {3,7} <List @@ -274,7 +274,7 @@ The `renderItem` prop is called a "render prop" because it's a prop that specifi /> ``` -The end result is the same as with `cloneElement`: +Kết quả cuối cùng giống như với `cloneElement`: ```js {4,8,12} <List> @@ -293,7 +293,7 @@ The end result is the same as with `cloneElement`: </List> ``` -However, you can clearly trace where the `isHighlighted` value is coming from. +Tuy nhiên, bạn có thể theo dõi rõ ràng giá trị `isHighlighted` đến từ đâu. <Sandpack> @@ -389,22 +389,21 @@ button { </Sandpack> -This pattern is preferred to `cloneElement` because it is more explicit. +Mô hình này được ưu tiên hơn `cloneElement` vì nó rõ ràng hơn. --- -### Passing data through context {/*passing-data-through-context*/} +### Truyền dữ liệu qua context {/*passing-data-through-context*/} -Another alternative to `cloneElement` is to [pass data through context.](/learn/passing-data-deeply-with-context) +Một lựa chọn thay thế khác cho `cloneElement` là [truyền dữ liệu qua context.](/learn/passing-data-deeply-with-context) - -For example, you can call [`createContext`](/reference/react/createContext) to define a `HighlightContext`: +Ví dụ: bạn có thể gọi [`createContext`](/reference/react/createContext) để xác định `HighlightContext`: ```js export const HighlightContext = createContext(false); ``` -Your `List` component can wrap every item it renders into a `HighlightContext` provider: +Component `List` của bạn có thể bọc mọi item mà nó render vào một provider `HighlightContext`: ```js {8,10} export default function List({ items, renderItem }) { @@ -421,7 +420,7 @@ export default function List({ items, renderItem }) { })} ``` -With this approach, `Row` does not need to receive an `isHighlighted` prop at all. Instead, it reads the context: +Với cách tiếp cận này, `Row` không cần nhận một prop `isHighlighted` nào cả. Thay vào đó, nó đọc context: ```js src/Row.js {2} export default function Row({ title }) { @@ -429,7 +428,7 @@ export default function Row({ title }) { // ... ``` -This allows the calling component to not know or worry about passing `isHighlighted` to `<Row>`: +Điều này cho phép component gọi không biết hoặc lo lắng về việc truyền `isHighlighted` cho `<Row>`: ```js {4} <List @@ -440,7 +439,7 @@ This allows the calling component to not know or worry about passing `isHighligh /> ``` -Instead, `List` and `Row` coordinate the highlighting logic through context. +Thay vào đó, `List` và `Row` phối hợp logic làm nổi bật thông qua context. <Sandpack> @@ -550,13 +549,13 @@ button { </Sandpack> -[Learn more about passing data through context.](/reference/react/useContext#passing-data-deeply-into-the-tree) +[Tìm hiểu thêm về việc truyền dữ liệu qua context.](/reference/react/useContext#passing-data-deeply-into-the-tree) --- -### Extracting logic into a custom Hook {/*extracting-logic-into-a-custom-hook*/} +### Trích xuất logic vào một Hook tùy chỉnh {/*extracting-logic-into-a-custom-hook*/} -Another approach you can try is to extract the "non-visual" logic into your own Hook, and use the information returned by your Hook to decide what to render. For example, you could write a `useList` custom Hook like this: +Một cách tiếp cận khác mà bạn có thể thử là trích xuất logic "phi trực quan" vào Hook của riêng bạn và sử dụng thông tin được trả về bởi Hook của bạn để quyết định những gì cần render. Ví dụ: bạn có thể viết một Hook tùy chỉnh `useList` như thế này: ```js import { useState } from 'react'; @@ -575,7 +574,7 @@ export default function useList(items) { } ``` -Then you could use it like this: +Sau đó, bạn có thể sử dụng nó như thế này: ```js {2,9,13} export default function App() { @@ -598,7 +597,7 @@ export default function App() { } ``` -The data flow is explicit, but the state is inside the `useList` custom Hook that you can use from any component: +Luồng dữ liệu là rõ ràng, nhưng trạng thái nằm bên trong Hook tùy chỉnh `useList` mà bạn có thể sử dụng từ bất kỳ component nào: <Sandpack> @@ -691,4 +690,4 @@ button { </Sandpack> -This approach is particularly useful if you want to reuse this logic between different components. +Cách tiếp cận này đặc biệt hữu ích nếu bạn muốn sử dụng lại logic này giữa các component khác nhau. diff --git a/src/content/reference/react/components.md b/src/content/reference/react/components.md index 7ce3fab63..3666e5462 100644 --- a/src/content/reference/react/components.md +++ b/src/content/reference/react/components.md @@ -1,24 +1,24 @@ --- -title: "Built-in React Components" +title: "Các Component Tích Hợp Sẵn của React" --- <Intro> -React exposes a few built-in components that you can use in your JSX. +React cung cấp một vài component tích hợp sẵn mà bạn có thể sử dụng trong JSX của mình. </Intro> --- -## Built-in components {/*built-in-components*/} +## Các component tích hợp sẵn {/*built-in-components*/} -* [`<Fragment>`](/reference/react/Fragment), alternatively written as `<>...</>`, lets you group multiple JSX nodes together. -* [`<Profiler>`](/reference/react/Profiler) lets you measure rendering performance of a React tree programmatically. -* [`<Suspense>`](/reference/react/Suspense) lets you display a fallback while the child components are loading. -* [`<StrictMode>`](/reference/react/StrictMode) enables extra development-only checks that help you find bugs early. +* [`<Fragment>`](/reference/react/Fragment), có thể được viết là `<>...</>`, cho phép bạn nhóm nhiều node JSX lại với nhau. +* [`<Profiler>`](/reference/react/Profiler) cho phép bạn đo lường hiệu năng hiển thị của một cây React một cách программmatically. +* [`<Suspense>`](/reference/react/Suspense) cho phép bạn hiển thị nội dung dự phòng trong khi các component con đang tải. +* [`<StrictMode>`](/reference/react/StrictMode) kích hoạt các kiểm tra bổ sung chỉ dành cho quá trình phát triển, giúp bạn tìm ra lỗi sớm. --- -## Your own components {/*your-own-components*/} +## Các component của riêng bạn {/*your-own-components*/} -You can also [define your own components](/learn/your-first-component) as JavaScript functions. +Bạn cũng có thể [định nghĩa các component của riêng bạn](/learn/your-first-component) dưới dạng các hàm JavaScript. diff --git a/src/content/reference/react/createContext.md b/src/content/reference/react/createContext.md index 03b09f8af..7b2af6536 100644 --- a/src/content/reference/react/createContext.md +++ b/src/content/reference/react/createContext.md @@ -4,7 +4,7 @@ title: createContext <Intro> -`createContext` lets you create a [context](/learn/passing-data-deeply-with-context) that components can provide or read. +`createContext` cho phép bạn tạo một [context](/learn/passing-data-deeply-with-context) mà các component có thể cung cấp hoặc đọc. ```js const SomeContext = createContext(defaultValue) @@ -16,11 +16,11 @@ const SomeContext = createContext(defaultValue) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `createContext(defaultValue)` {/*createcontext*/} -Call `createContext` outside of any components to create a context. +Gọi `createContext` bên ngoài bất kỳ component nào để tạo một context. ```js import { createContext } from 'react'; @@ -28,26 +28,26 @@ import { createContext } from 'react'; const ThemeContext = createContext('light'); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `defaultValue`: The value that you want the context to have when there is no matching context provider in the tree above the component that reads context. If you don't have any meaningful default value, specify `null`. The default value is meant as a "last resort" fallback. It is static and never changes over time. +* `defaultValue`: Giá trị mà bạn muốn context có khi không có context provider phù hợp nào trong cây phía trên component đọc context. Nếu bạn không có bất kỳ giá trị mặc định có ý nghĩa nào, hãy chỉ định `null`. Giá trị mặc định được dùng như một phương sách cuối cùng. Nó là tĩnh và không bao giờ thay đổi theo thời gian. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`createContext` returns a context object. +`createContext` trả về một đối tượng context. -**The context object itself does not hold any information.** It represents _which_ context other components read or provide. Typically, you will use [`SomeContext.Provider`](#provider) in components above to specify the context value, and call [`useContext(SomeContext)`](/reference/react/useContext) in components below to read it. The context object has a few properties: +**Bản thân đối tượng context không chứa bất kỳ thông tin nào.** Nó đại diện cho _context nào_ mà các component khác đọc hoặc cung cấp. Thông thường, bạn sẽ sử dụng [`SomeContext.Provider`](#provider) trong các component phía trên để chỉ định giá trị context và gọi [`useContext(SomeContext)`](/reference/react/useContext) trong các component phía dưới để đọc nó. Đối tượng context có một vài thuộc tính: -* `SomeContext.Provider` lets you provide the context value to components. -* `SomeContext.Consumer` is an alternative and rarely used way to read the context value. +* `SomeContext.Provider` cho phép bạn cung cấp giá trị context cho các component. +* `SomeContext.Consumer` là một cách thay thế và hiếm khi được sử dụng để đọc giá trị context. --- ### `SomeContext.Provider` {/*provider*/} -Wrap your components into a context provider to specify the value of this context for all components inside: +Bọc các component của bạn vào một context provider để chỉ định giá trị của context này cho tất cả các component bên trong: ```js function App() { @@ -63,17 +63,17 @@ function App() { #### Props {/*provider-props*/} -* `value`: The value that you want to pass to all the components reading this context inside this provider, no matter how deep. The context value can be of any type. A component calling [`useContext(SomeContext)`](/reference/react/useContext) inside of the provider receives the `value` of the innermost corresponding context provider above it. +* `value`: Giá trị mà bạn muốn truyền cho tất cả các component đọc context này bên trong provider này, bất kể độ sâu. Giá trị context có thể thuộc bất kỳ loại nào. Một component gọi [`useContext(SomeContext)`](/reference/react/useContext) bên trong provider sẽ nhận được `value` của context provider tương ứng gần nhất phía trên nó. --- ### `SomeContext.Consumer` {/*consumer*/} -Before `useContext` existed, there was an older way to read context: +Trước khi `useContext` tồn tại, có một cách cũ hơn để đọc context: ```js function Button() { - // 🟡 Legacy way (not recommended) + // 🟡 Cách cũ (không được khuyến nghị) return ( <ThemeContext.Consumer> {theme => ( @@ -84,11 +84,11 @@ function Button() { } ``` -Although this older way still works, **newly written code should read context with [`useContext()`](/reference/react/useContext) instead:** +Mặc dù cách cũ này vẫn hoạt động, **code mới nên đọc context bằng [`useContext()`](/reference/react/useContext) thay thế:** ```js function Button() { - // ✅ Recommended way + // ✅ Cách được khuyến nghị const theme = useContext(ThemeContext); return <button className={theme} />; } @@ -96,17 +96,17 @@ function Button() { #### Props {/*consumer-props*/} -* `children`: A function. React will call the function you pass with the current context value determined by the same algorithm as [`useContext()`](/reference/react/useContext) does, and render the result you return from this function. React will also re-run this function and update the UI whenever the context from the parent components changes. +* `children`: Một function. React sẽ gọi function bạn truyền vào với giá trị context hiện tại được xác định bởi cùng một thuật toán như [`useContext()`](/reference/react/useContext), và render kết quả bạn trả về từ function này. React cũng sẽ chạy lại function này và cập nhật UI bất cứ khi nào context từ các component cha thay đổi. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Creating context {/*creating-context*/} +### Tạo context {/*creating-context*/} -Context lets components [pass information deep down](/learn/passing-data-deeply-with-context) without explicitly passing props. +Context cho phép các component [truyền thông tin xuống sâu](/learn/passing-data-deeply-with-context) mà không cần truyền props một cách rõ ràng. -Call `createContext` outside any components to create one or more contexts. +Gọi `createContext` bên ngoài bất kỳ component nào để tạo một hoặc nhiều context. ```js [[1, 3, "ThemeContext"], [1, 4, "AuthContext"], [3, 3, "'light'"], [3, 4, "null"]] import { createContext } from 'react'; @@ -115,7 +115,7 @@ const ThemeContext = createContext('light'); const AuthContext = createContext(null); ``` -`createContext` returns a <CodeStep step={1}>context object</CodeStep>. Components can read context by passing it to [`useContext()`](/reference/react/useContext): +`createContext` trả về một <CodeStep step={1}>đối tượng context</CodeStep>. Các component có thể đọc context bằng cách truyền nó cho [`useContext()`](/reference/react/useContext): ```js [[1, 2, "ThemeContext"], [1, 7, "AuthContext"]] function Button() { @@ -129,9 +129,9 @@ function Profile() { } ``` -By default, the values they receive will be the <CodeStep step={3}>default values</CodeStep> you have specified when creating the contexts. However, by itself this isn't useful because the default values never change. +Theo mặc định, các giá trị chúng nhận được sẽ là các <CodeStep step={3}>giá trị mặc định</CodeStep> mà bạn đã chỉ định khi tạo context. Tuy nhiên, bản thân điều này không hữu ích vì các giá trị mặc định không bao giờ thay đổi. -Context is useful because you can **provide other, dynamic values from your components:** +Context rất hữu ích vì bạn có thể **cung cấp các giá trị động khác từ các component của bạn:** ```js {8-9,11-12} function App() { @@ -150,15 +150,15 @@ function App() { } ``` -Now the `Page` component and any components inside it, no matter how deep, will "see" the passed context values. If the passed context values change, React will re-render the components reading the context as well. +Bây giờ component `Page` và bất kỳ component nào bên trong nó, bất kể độ sâu, sẽ "nhìn thấy" các giá trị context được truyền. Nếu các giá trị context được truyền thay đổi, React cũng sẽ render lại các component đọc context. -[Read more about reading and providing context and see examples.](/reference/react/useContext) +[Đọc thêm về đọc và cung cấp context và xem các ví dụ.](/reference/react/useContext) --- -### Importing and exporting context from a file {/*importing-and-exporting-context-from-a-file*/} +### Nhập và xuất context từ một file {/*importing-and-exporting-context-from-a-file*/} -Often, components in different files will need access to the same context. This is why it's common to declare contexts in a separate file. Then you can use the [`export` statement](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) to make context available for other files: +Thông thường, các component trong các file khác nhau sẽ cần truy cập vào cùng một context. Đây là lý do tại sao việc khai báo context trong một file riêng là phổ biến. Sau đó, bạn có thể sử dụng câu lệnh [`export`](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) để cung cấp context cho các file khác: ```js {4-5} // Contexts.js @@ -168,7 +168,7 @@ export const ThemeContext = createContext('light'); export const AuthContext = createContext(null); ``` -Components declared in other files can then use the [`import`](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/import) statement to read or provide this context: +Các component được khai báo trong các file khác sau đó có thể sử dụng câu lệnh [`import`](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/import) để đọc hoặc cung cấp context này: ```js {2} // Button.js @@ -196,22 +196,20 @@ function App() { } ``` -This works similar to [importing and exporting components.](/learn/importing-and-exporting-components) +Điều này hoạt động tương tự như [nhập và xuất các component.](/learn/importing-and-exporting-components) --- -## Troubleshooting {/*troubleshooting*/} +## Gỡ rối {/*troubleshooting*/} -### I can't find a way to change the context value {/*i-cant-find-a-way-to-change-the-context-value*/} +### Tôi không thể tìm thấy cách để thay đổi giá trị context {/*i-cant-find-a-way-to-change-the-context-value*/} - -Code like this specifies the *default* context value: +Code như thế này chỉ định giá trị context *mặc định*: ```js const ThemeContext = createContext('light'); ``` -This value never changes. React only uses this value as a fallback if it can't find a matching provider above. - -To make context change over time, [add state and wrap components in a context provider.](/reference/react/useContext#updating-data-passed-via-context) +Giá trị này không bao giờ thay đổi. React chỉ sử dụng giá trị này như một fallback nếu nó không thể tìm thấy một provider phù hợp ở trên. +Để làm cho context thay đổi theo thời gian, [thêm state và bọc các component trong một context provider.](/reference/react/useContext#updating-data-passed-via-context) diff --git a/src/content/reference/react/createElement.md b/src/content/reference/react/createElement.md index 87110a71a..eefadc8f9 100644 --- a/src/content/reference/react/createElement.md +++ b/src/content/reference/react/createElement.md @@ -4,7 +4,7 @@ title: createElement <Intro> -`createElement` lets you create a React element. It serves as an alternative to writing [JSX.](/learn/writing-markup-with-jsx) +`createElement` cho phép bạn tạo một phần tử React. Nó đóng vai trò như một giải pháp thay thế cho việc viết [JSX.](/learn/writing-markup-with-jsx) ```js const element = createElement(type, props, ...children) @@ -16,11 +16,11 @@ const element = createElement(type, props, ...children) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `createElement(type, props, ...children)` {/*createelement*/} -Call `createElement` to create a React element with the given `type`, `props`, and `children`. +Gọi `createElement` để tạo một phần tử React với `type`, `props` và `children` đã cho. ```js import { createElement } from 'react'; @@ -34,44 +34,44 @@ function Greeting({ name }) { } ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `type`: The `type` argument must be a valid React component type. For example, it could be a tag name string (such as `'div'` or `'span'`), or a React component (a function, a class, or a special component like [`Fragment`](/reference/react/Fragment)). +* `type`: Đối số `type` phải là một kiểu component React hợp lệ. Ví dụ: nó có thể là một chuỗi tên thẻ (chẳng hạn như `'div'` hoặc `'span'`), hoặc một component React (một hàm, một class hoặc một component đặc biệt như [`Fragment`](/reference/react/Fragment)). -* `props`: The `props` argument must either be an object or `null`. If you pass `null`, it will be treated the same as an empty object. React will create an element with props matching the `props` you have passed. Note that `ref` and `key` from your `props` object are special and will *not* be available as `element.props.ref` and `element.props.key` on the returned `element`. They will be available as `element.ref` and `element.key`. +* `props`: Đối số `props` phải là một đối tượng hoặc `null`. Nếu bạn truyền `null`, nó sẽ được coi như một đối tượng trống. React sẽ tạo một phần tử với các props khớp với `props` mà bạn đã truyền. Lưu ý rằng `ref` và `key` từ đối tượng `props` của bạn là đặc biệt và sẽ *không* khả dụng dưới dạng `element.props.ref` và `element.props.key` trên `element` được trả về. Chúng sẽ có sẵn dưới dạng `element.ref` và `element.key`. -* **optional** `...children`: Zero or more child nodes. They can be any React nodes, including React elements, strings, numbers, [portals](/reference/react-dom/createPortal), empty nodes (`null`, `undefined`, `true`, and `false`), and arrays of React nodes. +* **tùy chọn** `...children`: Không hoặc nhiều node con. Chúng có thể là bất kỳ node React nào, bao gồm các phần tử React, chuỗi, số, [portal](/reference/react-dom/createPortal), các node trống (`null`, `undefined`, `true` và `false`) và mảng các node React. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`createElement` returns a React element object with a few properties: +`createElement` trả về một đối tượng phần tử React với một vài thuộc tính: -* `type`: The `type` you have passed. -* `props`: The `props` you have passed except for `ref` and `key`. -* `ref`: The `ref` you have passed. If missing, `null`. -* `key`: The `key` you have passed, coerced to a string. If missing, `null`. +* `type`: `type` bạn đã truyền. +* `props`: `props` bạn đã truyền ngoại trừ `ref` và `key`. +* `ref`: `ref` bạn đã truyền. Nếu thiếu, `null`. +* `key`: `key` bạn đã truyền, được ép thành một chuỗi. Nếu thiếu, `null`. -Usually, you'll return the element from your component or make it a child of another element. Although you may read the element's properties, it's best to treat every element as opaque after it's created, and only render it. +Thông thường, bạn sẽ trả về phần tử từ component của mình hoặc tạo nó thành một phần tử con của một phần tử khác. Mặc dù bạn có thể đọc các thuộc tính của phần tử, nhưng tốt nhất là coi mọi phần tử là không rõ ràng sau khi nó được tạo và chỉ hiển thị nó. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* You must **treat React elements and their props as [immutable](https://en.wikipedia.org/wiki/Immutable_object)** and never change their contents after creation. In development, React will [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) the returned element and its `props` property shallowly to enforce this. +* Bạn phải **coi các phần tử React và các props của chúng là [bất biến](https://en.wikipedia.org/wiki/Immutable_object)** và không bao giờ thay đổi nội dung của chúng sau khi tạo. Trong quá trình phát triển, React sẽ [đóng băng](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) phần tử được trả về và thuộc tính `props` của nó một cách nông cạn để thực thi điều này. -* When you use JSX, **you must start a tag with a capital letter to render your own custom component.** In other words, `<Something />` is equivalent to `createElement(Something)`, but `<something />` (lowercase) is equivalent to `createElement('something')` (note it's a string, so it will be treated as a built-in HTML tag). +* Khi bạn sử dụng JSX, **bạn phải bắt đầu một thẻ bằng một chữ cái viết hoa để hiển thị component tùy chỉnh của riêng bạn.** Nói cách khác, `<Something />` tương đương với `createElement(Something)`, nhưng `<something />` (chữ thường) tương đương với `createElement('something')` (lưu ý nó là một chuỗi, vì vậy nó sẽ được coi là một thẻ HTML tích hợp). -* You should only **pass children as multiple arguments to `createElement` if they are all statically known,** like `createElement('h1', {}, child1, child2, child3)`. If your children are dynamic, pass the entire array as the third argument: `createElement('ul', {}, listItems)`. This ensures that React will [warn you about missing `key`s](/learn/rendering-lists#keeping-list-items-in-order-with-key) for any dynamic lists. For static lists this is not necessary because they never reorder. +* Bạn chỉ nên **truyền children dưới dạng nhiều đối số cho `createElement` nếu tất cả chúng đều được biết tĩnh,** như `createElement('h1', {}, child1, child2, child3)`. Nếu children của bạn là động, hãy truyền toàn bộ mảng làm đối số thứ ba: `createElement('ul', {}, listItems)`. Điều này đảm bảo rằng React sẽ [cảnh báo bạn về việc thiếu `key`](/learn/rendering-lists#keeping-list-items-in-order-with-key) cho bất kỳ danh sách động nào. Đối với danh sách tĩnh, điều này là không cần thiết vì chúng không bao giờ sắp xếp lại. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Creating an element without JSX {/*creating-an-element-without-jsx*/} +### Tạo một phần tử mà không cần JSX {/*creating-an-element-without-jsx*/} -If you don't like [JSX](/learn/writing-markup-with-jsx) or can't use it in your project, you can use `createElement` as an alternative. +Nếu bạn không thích [JSX](/learn/writing-markup-with-jsx) hoặc không thể sử dụng nó trong dự án của mình, bạn có thể sử dụng `createElement` như một giải pháp thay thế. -To create an element without JSX, call `createElement` with some <CodeStep step={1}>type</CodeStep>, <CodeStep step={2}>props</CodeStep>, and <CodeStep step={3}>children</CodeStep>: +Để tạo một phần tử mà không cần JSX, hãy gọi `createElement` với một <CodeStep step={1}>type</CodeStep>, <CodeStep step={2}>props</CodeStep> và <CodeStep step={3}>children</CodeStep>: ```js [[1, 5, "'h1'"], [2, 6, "{ className: 'greeting' }"], [3, 7, "'Hello ',"], [3, 8, "createElement('i', null, name),"], [3, 9, "'. Welcome!'"]] import { createElement } from 'react'; @@ -87,7 +87,7 @@ function Greeting({ name }) { } ``` -The <CodeStep step={3}>children</CodeStep> are optional, and you can pass as many as you need (the example above has three children). This code will display a `<h1>` header with a greeting. For comparison, here is the same example rewritten with JSX: +Các <CodeStep step={3}>children</CodeStep> là tùy chọn và bạn có thể truyền bao nhiêu tùy ý (ví dụ trên có ba children). Đoạn code này sẽ hiển thị một tiêu đề `<h1>` với lời chào. Để so sánh, đây là ví dụ tương tự được viết lại bằng JSX: ```js [[1, 3, "h1"], [2, 3, "className=\\"greeting\\""], [3, 4, "Hello <i>{name}</i>. Welcome!"], [1, 5, "h1"]] function Greeting({ name }) { @@ -99,7 +99,7 @@ function Greeting({ name }) { } ``` -To render your own React component, pass a function like `Greeting` as the <CodeStep step={1}>type</CodeStep> instead of a string like `'h1'`: +Để hiển thị component React của riêng bạn, hãy truyền một hàm như `Greeting` làm <CodeStep step={1}>type</CodeStep> thay vì một chuỗi như `'h1'`: ```js [[1, 2, "Greeting"], [2, 2, "{ name: 'Taylor' }"]] export default function App() { @@ -107,7 +107,7 @@ export default function App() { } ``` -With JSX, it would look like this: +Với JSX, nó sẽ trông như thế này: ```js [[1, 2, "Greeting"], [2, 2, "name=\\"Taylor\\""]] export default function App() { @@ -115,7 +115,7 @@ export default function App() { } ``` -Here is a complete example written with `createElement`: +Đây là một ví dụ hoàn chỉnh được viết bằng `createElement`: <Sandpack> @@ -149,7 +149,7 @@ export default function App() { </Sandpack> -And here is the same example written using JSX: +Và đây là ví dụ tương tự được viết bằng JSX: <Sandpack> @@ -176,16 +176,16 @@ export default function App() { </Sandpack> -Both coding styles are fine, so you can use whichever one you prefer for your project. The main benefit of using JSX compared to `createElement` is that it's easy to see which closing tag corresponds to which opening tag. +Cả hai kiểu code đều ổn, vì vậy bạn có thể sử dụng bất kỳ kiểu nào bạn thích cho dự án của mình. Lợi ích chính của việc sử dụng JSX so với `createElement` là dễ dàng nhận thấy thẻ đóng nào tương ứng với thẻ mở nào. <DeepDive> -#### What is a React element, exactly? {/*what-is-a-react-element-exactly*/} +#### Chính xác thì một phần tử React là gì? {/*what-is-a-react-element-exactly*/} -An element is a lightweight description of a piece of the user interface. For example, both `<Greeting name="Taylor" />` and `createElement(Greeting, { name: 'Taylor' })` produce an object like this: +Một phần tử là một mô tả nhẹ về một phần của giao diện người dùng. Ví dụ: cả `<Greeting name="Taylor" />` và `createElement(Greeting, { name: 'Taylor' })` đều tạo ra một đối tượng như thế này: ```js -// Slightly simplified +// Đơn giản hóa một chút { type: Greeting, props: { @@ -196,10 +196,10 @@ An element is a lightweight description of a piece of the user interface. For ex } ``` -**Note that creating this object does not render the `Greeting` component or create any DOM elements.** +**Lưu ý rằng việc tạo đối tượng này không hiển thị component `Greeting` hoặc tạo bất kỳ phần tử DOM nào.** -A React element is more like a description--an instruction for React to later render the `Greeting` component. By returning this object from your `App` component, you tell React what to do next. +Một phần tử React giống như một mô tả--một hướng dẫn để React hiển thị component `Greeting` sau này. Bằng cách trả về đối tượng này từ component `App` của bạn, bạn cho React biết phải làm gì tiếp theo. -Creating elements is extremely cheap so you don't need to try to optimize or avoid it. +Việc tạo các phần tử cực kỳ rẻ nên bạn không cần cố gắng tối ưu hóa hoặc tránh nó. </DeepDive> diff --git a/src/content/reference/react/createRef.md b/src/content/reference/react/createRef.md index 2a3ba6aa3..7b471e916 100644 --- a/src/content/reference/react/createRef.md +++ b/src/content/reference/react/createRef.md @@ -4,13 +4,13 @@ title: createRef <Pitfall> -`createRef` is mostly used for [class components.](/reference/react/Component) Function components typically rely on [`useRef`](/reference/react/useRef) instead. +`createRef` chủ yếu được sử dụng cho [component class.](/reference/react/Component) Các component function thường dựa vào [`useRef`](/reference/react/useRef) để thay thế. </Pitfall> <Intro> -`createRef` creates a [ref](/learn/referencing-values-with-refs) object which can contain arbitrary value. +`createRef` tạo ra một đối tượng [ref](/learn/referencing-values-with-refs) có thể chứa giá trị tùy ý. ```js class MyInput extends Component { @@ -25,11 +25,11 @@ class MyInput extends Component { --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `createRef()` {/*createref*/} -Call `createRef` to declare a [ref](/learn/referencing-values-with-refs) inside a [class component.](/reference/react/Component) +Gọi `createRef` để khai báo một [ref](/learn/referencing-values-with-refs) bên trong một [component class.](/reference/react/Component) ```js import { createRef, Component } from 'react'; @@ -40,31 +40,31 @@ class MyComponent extends Component { // ... ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -`createRef` takes no parameters. +`createRef` không có tham số. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`createRef` returns an object with a single property: +`createRef` trả về một đối tượng với một thuộc tính duy nhất: -* `current`: Initially, it's set to the `null`. You can later set it to something else. If you pass the ref object to React as a `ref` attribute to a JSX node, React will set its `current` property. +* `current`: Ban đầu, nó được đặt thành `null`. Bạn có thể đặt nó thành một giá trị khác sau này. Nếu bạn truyền đối tượng ref cho React dưới dạng một thuộc tính `ref` cho một nút JSX, React sẽ đặt thuộc tính `current` của nó. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `createRef` always returns a *different* object. It's equivalent to writing `{ current: null }` yourself. -* In a function component, you probably want [`useRef`](/reference/react/useRef) instead which always returns the same object. -* `const ref = useRef()` is equivalent to `const [ref, _] = useState(() => createRef(null))`. +* `createRef` luôn trả về một đối tượng *khác*. Nó tương đương với việc tự viết `{ current: null }`. +* Trong một component function, bạn có thể muốn sử dụng [`useRef`](/reference/react/useRef) thay thế, nó luôn trả về cùng một đối tượng. +* `const ref = useRef()` tương đương với `const [ref, _] = useState(() => createRef(null))`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Declaring a ref in a class component {/*declaring-a-ref-in-a-class-component*/} +### Khai báo một ref trong một component class {/*declaring-a-ref-in-a-class-component*/} -To declare a ref inside a [class component,](/reference/react/Component) call `createRef` and assign its result to a class field: +Để khai báo một ref bên trong một [component class,](/reference/react/Component) gọi `createRef` và gán kết quả của nó cho một trường class: ```js {4} import { Component, createRef } from 'react'; @@ -76,7 +76,7 @@ class Form extends Component { } ``` -If you now pass `ref={this.inputRef}` to an `<input>` in your JSX, React will populate `this.inputRef.current` with the input DOM node. For example, here is how you make a button that focuses the input: +Nếu bây giờ bạn truyền `ref={this.inputRef}` cho một `<input>` trong JSX của bạn, React sẽ điền `this.inputRef.current` với nút DOM input. Ví dụ: đây là cách bạn tạo một nút tập trung vào input: <Sandpack> @@ -107,17 +107,17 @@ export default class Form extends Component { <Pitfall> -`createRef` is mostly used for [class components.](/reference/react/Component) Function components typically rely on [`useRef`](/reference/react/useRef) instead. +`createRef` chủ yếu được sử dụng cho [component class.](/reference/react/Component) Các component function thường dựa vào [`useRef`](/reference/react/useRef) để thay thế. </Pitfall> --- -## Alternatives {/*alternatives*/} +## Các lựa chọn thay thế {/*alternatives*/} -### Migrating from a class with `createRef` to a function with `useRef` {/*migrating-from-a-class-with-createref-to-a-function-with-useref*/} +### Chuyển đổi từ một class với `createRef` sang một function với `useRef` {/*migrating-from-a-class-with-createref-to-a-function-with-useref*/} -We recommend using function components instead of [class components](/reference/react/Component) in new code. If you have some existing class components using `createRef`, here is how you can convert them. This is the original code: +Chúng tôi khuyên bạn nên sử dụng component function thay vì [component class](/reference/react/Component) trong code mới. Nếu bạn có một số component class hiện tại đang sử dụng `createRef`, đây là cách bạn có thể chuyển đổi chúng. Đây là code gốc: <Sandpack> @@ -146,7 +146,7 @@ export default class Form extends Component { </Sandpack> -When you [convert this component from a class to a function,](/reference/react/Component#alternatives) replace calls to `createRef` with calls to [`useRef`:](/reference/react/useRef) +Khi bạn [chuyển đổi component này từ một class sang một function,](/reference/react/Component#alternatives) hãy thay thế các lệnh gọi đến `createRef` bằng các lệnh gọi đến [`useRef`:](/reference/react/useRef) <Sandpack> diff --git a/src/content/reference/react/experimental_taintObjectReference.md b/src/content/reference/react/experimental_taintObjectReference.md index b5b9e513d..b6b50c2b4 100644 --- a/src/content/reference/react/experimental_taintObjectReference.md +++ b/src/content/reference/react/experimental_taintObjectReference.md @@ -4,30 +4,29 @@ title: experimental_taintObjectReference <Wip> -**This API is experimental and is not available in a stable version of React yet.** +**API này đang trong giai đoạn thử nghiệm và chưa có sẵn trong phiên bản ổn định của React.** -You can try it by upgrading React packages to the most recent experimental version: +Bạn có thể thử nó bằng cách nâng cấp các gói React lên phiên bản thử nghiệm mới nhất: - `react@experimental` - `react-dom@experimental` - `eslint-plugin-react-hooks@experimental` -Experimental versions of React may contain bugs. Don't use them in production. +Các phiên bản thử nghiệm của React có thể chứa lỗi. Không sử dụng chúng trong môi trường production. -This API is only available inside React Server Components. +API này chỉ khả dụng bên trong React Server Components. </Wip> - <Intro> -`taintObjectReference` lets you prevent a specific object instance from being passed to a Client Component like a `user` object. +`taintObjectReference` cho phép bạn ngăn chặn một thể hiện đối tượng cụ thể được truyền đến một Client Component, chẳng hạn như đối tượng `user`. ```js experimental_taintObjectReference(message, object); ``` -To prevent passing a key, hash or token, see [`taintUniqueValue`](/reference/react/experimental_taintUniqueValue). +Để ngăn chặn việc truyền một key, hash hoặc token, hãy xem [`taintUniqueValue`](/reference/react/experimental_taintUniqueValue). </Intro> @@ -35,50 +34,50 @@ To prevent passing a key, hash or token, see [`taintUniqueValue`](/reference/rea --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `taintObjectReference(message, object)` {/*taintobjectreference*/} -Call `taintObjectReference` with an object to register it with React as something that should not be allowed to be passed to the Client as is: +Gọi `taintObjectReference` với một đối tượng để đăng ký nó với React như một thứ không nên được phép truyền đến Client Component như hiện tại: ```js import {experimental_taintObjectReference} from 'react'; experimental_taintObjectReference( - 'Do not pass ALL environment variables to the client.', + 'Không truyền TẤT CẢ các biến môi trường cho client.', process.env ); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `message`: The message you want to display if the object gets passed to a Client Component. This message will be displayed as a part of the Error that will be thrown if the object gets passed to a Client Component. +* `message`: Thông báo bạn muốn hiển thị nếu đối tượng được truyền đến một Client Component. Thông báo này sẽ được hiển thị như một phần của Lỗi sẽ được đưa ra nếu đối tượng được truyền đến một Client Component. -* `object`: The object to be tainted. Functions and class instances can be passed to `taintObjectReference` as `object`. Functions and classes are already blocked from being passed to Client Components but the React's default error message will be replaced by what you defined in `message`. When a specific instance of a Typed Array is passed to `taintObjectReference` as `object`, any other copies of the Typed Array will not be tainted. +* `object`: Đối tượng cần được đánh dấu. Các hàm và thể hiện lớp có thể được truyền cho `taintObjectReference` dưới dạng `object`. Các hàm và lớp đã bị chặn truyền đến Client Components nhưng thông báo lỗi mặc định của React sẽ được thay thế bằng những gì bạn đã xác định trong `message`. Khi một thể hiện cụ thể của Typed Array được truyền cho `taintObjectReference` dưới dạng `object`, bất kỳ bản sao nào khác của Typed Array sẽ không bị đánh dấu. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`experimental_taintObjectReference` returns `undefined`. +`experimental_taintObjectReference` trả về `undefined`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -- Recreating or cloning a tainted object creates a new untainted object which may contain sensitive data. For example, if you have a tainted `user` object, `const userInfo = {name: user.name, ssn: user.ssn}` or `{...user}` will create new objects which are not tainted. `taintObjectReference` only protects against simple mistakes when the object is passed through to a Client Component unchanged. +- Việc tạo lại hoặc sao chép một đối tượng bị đánh dấu sẽ tạo ra một đối tượng không bị đánh dấu mới có thể chứa dữ liệu nhạy cảm. Ví dụ: nếu bạn có một đối tượng `user` bị đánh dấu, `const userInfo = {name: user.name, ssn: user.ssn}` hoặc `{...user}` sẽ tạo ra các đối tượng mới không bị đánh dấu. `taintObjectReference` chỉ bảo vệ chống lại những sai lầm đơn giản khi đối tượng được truyền đến một Client Component mà không thay đổi. <Pitfall> -**Do not rely on just tainting for security.** Tainting an object doesn't prevent leaking of every possible derived value. For example, the clone of a tainted object will create a new untainted object. Using data from a tainted object (e.g. `{secret: taintedObj.secret}`) will create a new value or object that is not tainted. Tainting is a layer of protection; a secure app will have multiple layers of protection, well designed APIs, and isolation patterns. +**Không chỉ dựa vào việc đánh dấu để bảo mật.** Việc đánh dấu một đối tượng không ngăn chặn việc rò rỉ mọi giá trị phái sinh có thể. Ví dụ: bản sao của một đối tượng bị đánh dấu sẽ tạo ra một đối tượng không bị đánh dấu mới. Sử dụng dữ liệu từ một đối tượng bị đánh dấu (ví dụ: `{secret: taintedObj.secret}`) sẽ tạo ra một giá trị hoặc đối tượng mới không bị đánh dấu. Đánh dấu là một lớp bảo vệ; một ứng dụng an toàn sẽ có nhiều lớp bảo vệ, các API được thiết kế tốt và các mẫu cách ly. </Pitfall> --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Prevent user data from unintentionally reaching the client {/*prevent-user-data-from-unintentionally-reaching-the-client*/} +### Ngăn dữ liệu người dùng vô tình đến client {/*prevent-user-data-from-unintentionally-reaching-the-client*/} -A Client Component should never accept objects that carry sensitive data. Ideally, the data fetching functions should not expose data that the current user should not have access to. Sometimes mistakes happen during refactoring. To protect against these mistakes happening down the line we can "taint" the user object in our data API. +Một Client Component không bao giờ nên chấp nhận các đối tượng mang dữ liệu nhạy cảm. Lý tưởng nhất là các hàm tìm nạp dữ liệu không nên hiển thị dữ liệu mà người dùng hiện tại không được phép truy cập. Đôi khi sai lầm xảy ra trong quá trình tái cấu trúc. Để bảo vệ chống lại những sai lầm này xảy ra sau này, chúng ta có thể "đánh dấu" đối tượng người dùng trong API dữ liệu của chúng ta. ```js import {experimental_taintObjectReference} from 'react'; @@ -86,21 +85,21 @@ import {experimental_taintObjectReference} from 'react'; export async function getUser(id) { const user = await db`SELECT * FROM users WHERE id = ${id}`; experimental_taintObjectReference( - 'Do not pass the entire user object to the client. ' + - 'Instead, pick off the specific properties you need for this use case.', + 'Không truyền toàn bộ đối tượng người dùng cho client. ' + + 'Thay vào đó, hãy chọn các thuộc tính cụ thể bạn cần cho trường hợp sử dụng này.', user, ); return user; } ``` -Now whenever anyone tries to pass this object to a Client Component, an error will be thrown with the passed in error message instead. +Bây giờ, bất cứ khi nào ai đó cố gắng truyền đối tượng này cho một Client Component, một lỗi sẽ được đưa ra với thông báo lỗi đã truyền vào. <DeepDive> -#### Protecting against leaks in data fetching {/*protecting-against-leaks-in-data-fetching*/} +#### Bảo vệ chống lại rò rỉ trong quá trình tìm nạp dữ liệu {/*protecting-against-leaks-in-data-fetching*/} -If you're running a Server Components environment that has access to sensitive data, you have to be careful not to pass objects straight through: +Nếu bạn đang chạy một môi trường Server Components có quyền truy cập vào dữ liệu nhạy cảm, bạn phải cẩn thận không truyền trực tiếp các đối tượng: ```js // api.js @@ -116,7 +115,7 @@ import { InfoCard } from 'components.js'; export async function Profile(props) { const user = await getUser(props.userId); - // DO NOT DO THIS + // KHÔNG LÀM ĐIỀU NÀY return <InfoCard user={user} />; } ``` @@ -130,8 +129,7 @@ export async function InfoCard({ user }) { } ``` -Ideally, the `getUser` should not expose data that the current user should not have access to. To prevent passing the `user` object to a Client Component down the line we can "taint" the user object: - +Lý tưởng nhất là `getUser` không nên hiển thị dữ liệu mà người dùng hiện tại không được phép truy cập. Để ngăn chặn việc truyền đối tượng `user` cho một Client Component sau này, chúng ta có thể "đánh dấu" đối tượng người dùng: ```js // api.js @@ -140,14 +138,14 @@ import {experimental_taintObjectReference} from 'react'; export async function getUser(id) { const user = await db`SELECT * FROM users WHERE id = ${id}`; experimental_taintObjectReference( - 'Do not pass the entire user object to the client. ' + - 'Instead, pick off the specific properties you need for this use case.', + 'Không truyền toàn bộ đối tượng người dùng cho client. ' + + 'Thay vào đó, hãy chọn các thuộc tính cụ thể bạn cần cho trường hợp sử dụng này.', user, ); return user; } ``` -Now if anyone tries to pass the `user` object to a Client Component, an error will be thrown with the passed in error message. +Bây giờ, nếu ai đó cố gắng truyền đối tượng `user` cho một Client Component, một lỗi sẽ được đưa ra với thông báo lỗi đã truyền vào. </DeepDive> diff --git a/src/content/reference/react/experimental_taintUniqueValue.md b/src/content/reference/react/experimental_taintUniqueValue.md index de9a9beda..997c8cefd 100644 --- a/src/content/reference/react/experimental_taintUniqueValue.md +++ b/src/content/reference/react/experimental_taintUniqueValue.md @@ -4,30 +4,29 @@ title: experimental_taintUniqueValue <Wip> -**This API is experimental and is not available in a stable version of React yet.** +**API này là thử nghiệm và chưa có sẵn trong phiên bản ổn định của React.** -You can try it by upgrading React packages to the most recent experimental version: +Bạn có thể thử nó bằng cách nâng cấp các gói React lên phiên bản thử nghiệm mới nhất: - `react@experimental` - `react-dom@experimental` - `eslint-plugin-react-hooks@experimental` -Experimental versions of React may contain bugs. Don't use them in production. +Các phiên bản thử nghiệm của React có thể chứa lỗi. Không sử dụng chúng trong sản xuất. -This API is only available inside [React Server Components](/reference/rsc/use-client). +API này chỉ khả dụng bên trong [React Server Components](/reference/rsc/use-client). </Wip> - <Intro> -`taintUniqueValue` lets you prevent unique values from being passed to Client Components like passwords, keys, or tokens. +`taintUniqueValue` cho phép bạn ngăn các giá trị duy nhất được truyền đến Client Components như mật khẩu, khóa hoặc mã thông báo. ```js taintUniqueValue(errMessage, lifetime, value) ``` -To prevent passing an object containing sensitive data, see [`taintObjectReference`](/reference/react/experimental_taintObjectReference). +Để ngăn việc truyền một đối tượng chứa dữ liệu nhạy cảm, hãy xem [`taintObjectReference`](/reference/react/experimental_taintObjectReference). </Intro> @@ -35,62 +34,62 @@ To prevent passing an object containing sensitive data, see [`taintObjectReferen --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `taintUniqueValue(message, lifetime, value)` {/*taintuniquevalue*/} -Call `taintUniqueValue` with a password, token, key or hash to register it with React as something that should not be allowed to be passed to the Client as is: +Gọi `taintUniqueValue` với mật khẩu, mã thông báo, khóa hoặc hàm băm để đăng ký nó với React như một thứ gì đó không được phép chuyển đến Client như hiện tại: ```js import {experimental_taintUniqueValue} from 'react'; experimental_taintUniqueValue( - 'Do not pass secret keys to the client.', + 'Không chuyển khóa bí mật cho máy khách.', process, process.env.SECRET_KEY ); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `message`: The message you want to display if `value` is passed to a Client Component. This message will be displayed as a part of the Error that will be thrown if `value` is passed to a Client Component. +* `message`: Thông báo bạn muốn hiển thị nếu `value` được chuyển đến Client Component. Thông báo này sẽ được hiển thị như một phần của Lỗi sẽ được đưa ra nếu `value` được chuyển đến Client Component. -* `lifetime`: Any object that indicates how long `value` should be tainted. `value` will be blocked from being sent to any Client Component while this object still exists. For example, passing `globalThis` blocks the value for the lifetime of an app. `lifetime` is typically an object whose properties contains `value`. +* `lifetime`: Bất kỳ đối tượng nào cho biết thời gian `value` nên bị nhiễm độc. `value` sẽ bị chặn gửi đến bất kỳ Client Component nào khi đối tượng này vẫn tồn tại. Ví dụ: chuyển `globalThis` sẽ chặn giá trị trong suốt thời gian tồn tại của một ứng dụng. `lifetime` thường là một đối tượng có các thuộc tính chứa `value`. -* `value`: A string, bigint or TypedArray. `value` must be a unique sequence of characters or bytes with high entropy such as a cryptographic token, private key, hash, or a long password. `value` will be blocked from being sent to any Client Component. +* `value`: Một chuỗi, bigint hoặc TypedArray. `value` phải là một chuỗi các ký tự hoặc byte duy nhất có entropy cao, chẳng hạn như mã thông báo mật mã, khóa riêng, hàm băm hoặc mật khẩu dài. `value` sẽ bị chặn gửi đến bất kỳ Client Component nào. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`experimental_taintUniqueValue` returns `undefined`. +`experimental_taintUniqueValue` trả về `undefined`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* Deriving new values from tainted values can compromise tainting protection. New values created by uppercasing tainted values, concatenating tainted string values into a larger string, converting tainted values to base64, substringing tainted values, and other similar transformations are not tainted unless you explicitly call `taintUniqueValue` on these newly created values. -* Do not use `taintUniqueValue` to protect low-entropy values such as PIN codes or phone numbers. If any value in a request is controlled by an attacker, they could infer which value is tainted by enumerating all possible values of the secret. +* Tạo các giá trị mới từ các giá trị bị nhiễm độc có thể làm tổn hại đến việc bảo vệ chống nhiễm độc. Các giá trị mới được tạo bằng cách viết hoa các giá trị bị nhiễm độc, nối các giá trị chuỗi bị nhiễm độc thành một chuỗi lớn hơn, chuyển đổi các giá trị bị nhiễm độc thành base64, lấy chuỗi con của các giá trị bị nhiễm độc và các chuyển đổi tương tự khác không bị nhiễm độc trừ khi bạn gọi rõ ràng `taintUniqueValue` trên các giá trị mới được tạo này. +* Không sử dụng `taintUniqueValue` để bảo vệ các giá trị entropy thấp như mã PIN hoặc số điện thoại. Nếu bất kỳ giá trị nào trong yêu cầu bị kẻ tấn công kiểm soát, chúng có thể suy ra giá trị nào bị nhiễm độc bằng cách liệt kê tất cả các giá trị có thể có của bí mật. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Prevent a token from being passed to Client Components {/*prevent-a-token-from-being-passed-to-client-components*/} +### Ngăn mã thông báo được chuyển đến Client Components {/*prevent-a-token-from-being-passed-to-client-components*/} -To ensure that sensitive information such as passwords, session tokens, or other unique values do not inadvertently get passed to Client Components, the `taintUniqueValue` function provides a layer of protection. When a value is tainted, any attempt to pass it to a Client Component will result in an error. +Để đảm bảo rằng thông tin nhạy cảm như mật khẩu, mã thông báo phiên hoặc các giá trị duy nhất khác không vô tình được chuyển đến Client Components, hàm `taintUniqueValue` cung cấp một lớp bảo vệ. Khi một giá trị bị nhiễm độc, bất kỳ nỗ lực nào để chuyển nó đến Client Component sẽ dẫn đến lỗi. -The `lifetime` argument defines the duration for which the value remains tainted. For values that should remain tainted indefinitely, objects like [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) or `process` can serve as the `lifetime` argument. These objects have a lifespan that spans the entire duration of your app's execution. +Đối số `lifetime` xác định khoảng thời gian giá trị vẫn bị nhiễm độc. Đối với các giá trị nên bị nhiễm độc vô thời hạn, các đối tượng như [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) hoặc `process` có thể đóng vai trò là đối số `lifetime`. Các đối tượng này có tuổi thọ kéo dài toàn bộ thời gian thực thi ứng dụng của bạn. ```js import {experimental_taintUniqueValue} from 'react'; experimental_taintUniqueValue( - 'Do not pass a user password to the client.', + 'Không chuyển mật khẩu người dùng cho máy khách.', globalThis, process.env.SECRET_KEY ); ``` -If the tainted value's lifespan is tied to a object, the `lifetime` should be the object that encapsulates the value. This ensures the tainted value remains protected for the lifetime of the encapsulating object. +Nếu tuổi thọ của giá trị bị nhiễm độc gắn liền với một đối tượng, thì `lifetime` phải là đối tượng bao bọc giá trị. Điều này đảm bảo giá trị bị nhiễm độc vẫn được bảo vệ trong suốt thời gian tồn tại của đối tượng bao bọc. ```js import {experimental_taintUniqueValue} from 'react'; @@ -98,7 +97,7 @@ import {experimental_taintUniqueValue} from 'react'; export async function getUser(id) { const user = await db`SELECT * FROM users WHERE id = ${id}`; experimental_taintUniqueValue( - 'Do not pass a user session token to the client.', + 'Không chuyển mã thông báo phiên người dùng cho máy khách.', user, user.session.token ); @@ -106,12 +105,11 @@ export async function getUser(id) { } ``` -In this example, the `user` object serves as the `lifetime` argument. If this object gets stored in a global cache or is accessible by another request, the session token remains tainted. +Trong ví dụ này, đối tượng `user` đóng vai trò là đối số `lifetime`. Nếu đối tượng này được lưu trữ trong bộ nhớ cache toàn cục hoặc có thể truy cập được bởi một yêu cầu khác, thì mã thông báo phiên vẫn bị nhiễm độc. <Pitfall> -**Do not rely solely on tainting for security.** Tainting a value doesn't block every possible derived value. For example, creating a new value by upper casing a tainted string will not taint the new value. - +**Không chỉ dựa vào việc nhiễm độc để bảo mật.** Việc nhiễm độc một giá trị không chặn mọi giá trị phái sinh có thể có. Ví dụ: tạo một giá trị mới bằng cách viết hoa một chuỗi bị nhiễm độc sẽ không làm nhiễm độc giá trị mới. ```js import {experimental_taintUniqueValue} from 'react'; @@ -119,31 +117,31 @@ import {experimental_taintUniqueValue} from 'react'; const password = 'correct horse battery staple'; experimental_taintUniqueValue( - 'Do not pass the password to the client.', + 'Không chuyển mật khẩu cho máy khách.', globalThis, password ); -const uppercasePassword = password.toUpperCase() // `uppercasePassword` is not tainted +const uppercasePassword = password.toUpperCase() // `uppercasePassword` không bị nhiễm độc ``` -In this example, the constant `password` is tainted. Then `password` is used to create a new value `uppercasePassword` by calling the `toUpperCase` method on `password`. The newly created `uppercasePassword` is not tainted. +Trong ví dụ này, hằng số `password` bị nhiễm độc. Sau đó, `password` được sử dụng để tạo một giá trị mới `uppercasePassword` bằng cách gọi phương thức `toUpperCase` trên `password`. `uppercasePassword` mới được tạo không bị nhiễm độc. -Other similar ways of deriving new values from tainted values like concatenating it into a larger string, converting it to base64, or returning a substring create untained values. +Các cách tương tự khác để tạo các giá trị mới từ các giá trị bị nhiễm độc như nối nó vào một chuỗi lớn hơn, chuyển đổi nó thành base64 hoặc trả về một chuỗi con tạo ra các giá trị không bị nhiễm độc. -Tainting only protects against simple mistakes like explicitly passing secret values to the client. Mistakes in calling the `taintUniqueValue` like using a global store outside of React, without the corresponding lifetime object, can cause the tainted value to become untainted. Tainting is a layer of protection; a secure app will have multiple layers of protection, well designed APIs, and isolation patterns. +Việc nhiễm độc chỉ bảo vệ chống lại những sai lầm đơn giản như chuyển rõ ràng các giá trị bí mật cho máy khách. Những sai lầm khi gọi `taintUniqueValue` như sử dụng kho lưu trữ toàn cục bên ngoài React, mà không có đối tượng lifetime tương ứng, có thể khiến giá trị bị nhiễm độc trở nên không bị nhiễm độc. Việc nhiễm độc là một lớp bảo vệ; một ứng dụng an toàn sẽ có nhiều lớp bảo vệ, các API được thiết kế tốt và các mẫu cách ly. </Pitfall> <DeepDive> -#### Using `server-only` and `taintUniqueValue` to prevent leaking secrets {/*using-server-only-and-taintuniquevalue-to-prevent-leaking-secrets*/} +#### Sử dụng `server-only` và `taintUniqueValue` để ngăn chặn rò rỉ bí mật {/*using-server-only-and-taintuniquevalue-to-prevent-leaking-secrets*/} -If you're running a Server Components environment that has access to private keys or passwords such as database passwords, you have to be careful not to pass that to a Client Component. +Nếu bạn đang chạy một môi trường Server Components có quyền truy cập vào các khóa riêng hoặc mật khẩu như mật khẩu cơ sở dữ liệu, bạn phải cẩn thận để không chuyển nó cho Client Component. ```js export async function Dashboard(props) { - // DO NOT DO THIS + // KHÔNG LÀM ĐIỀU NÀY return <Overview password={process.env.API_PASSWORD} />; } ``` @@ -162,11 +160,11 @@ export async function Overview({ password }) { } ``` -This example would leak the secret API token to the client. If this API token can be used to access data this particular user shouldn't have access to, it could lead to a data breach. +Ví dụ này sẽ làm rò rỉ mã thông báo API bí mật cho máy khách. Nếu mã thông báo API này có thể được sử dụng để truy cập dữ liệu mà người dùng cụ thể này không được phép truy cập, nó có thể dẫn đến vi phạm dữ liệu. -[comment]: <> (TODO: Link to `server-only` docs once they are written) +[comment]: <> (TODO: Liên kết đến tài liệu `server-only` sau khi chúng được viết) -Ideally, secrets like this are abstracted into a single helper file that can only be imported by trusted data utilities on the server. The helper can even be tagged with [`server-only`](https://www.npmjs.com/package/server-only) to ensure that this file isn't imported on the client. +Lý tưởng nhất là các bí mật như thế này được trừu tượng hóa thành một tệp trợ giúp duy nhất chỉ có thể được nhập bởi các tiện ích dữ liệu đáng tin cậy trên máy chủ. Trình trợ giúp thậm chí có thể được gắn thẻ bằng [`server-only`](https://www.npmjs.com/package/server-only) để đảm bảo rằng tệp này không được nhập trên máy khách. ```js import "server-only"; @@ -177,22 +175,22 @@ export function fetchAPI(url) { } ``` -Sometimes mistakes happen during refactoring and not all of your colleagues might know about this. -To protect against this mistakes happening down the line we can "taint" the actual password: +Đôi khi những sai lầm xảy ra trong quá trình tái cấu trúc và không phải tất cả đồng nghiệp của bạn có thể biết về điều này. +Để bảo vệ chống lại những sai lầm này xảy ra sau này, chúng ta có thể "làm nhiễm độc" mật khẩu thực tế: ```js import "server-only"; import {experimental_taintUniqueValue} from 'react'; experimental_taintUniqueValue( - 'Do not pass the API token password to the client. ' + - 'Instead do all fetches on the server.' + 'Không chuyển mật khẩu mã thông báo API cho máy khách. ' + + 'Thay vào đó, hãy thực hiện tất cả các tìm nạp trên máy chủ.' process, process.env.API_PASSWORD ); ``` -Now whenever anyone tries to pass this password to a Client Component, or send the password to a Client Component with a Server Function, an error will be thrown with message you defined when you called `taintUniqueValue`. +Bây giờ, bất cứ khi nào ai đó cố gắng chuyển mật khẩu này cho Client Component hoặc gửi mật khẩu cho Client Component bằng Server Function, một lỗi sẽ được đưa ra với thông báo bạn đã xác định khi bạn gọi `taintUniqueValue`. </DeepDive> diff --git a/src/content/reference/react/experimental_useEffectEvent.md b/src/content/reference/react/experimental_useEffectEvent.md index e819f041c..94e27be29 100644 --- a/src/content/reference/react/experimental_useEffectEvent.md +++ b/src/content/reference/react/experimental_useEffectEvent.md @@ -4,22 +4,21 @@ title: experimental_useEffectEvent <Wip> -**This API is experimental and is not available in a stable version of React yet.** +**API này là thử nghiệm và chưa có sẵn trong phiên bản ổn định của React.** -You can try it by upgrading React packages to the most recent experimental version: +Bạn có thể thử bằng cách nâng cấp các gói React lên phiên bản thử nghiệm mới nhất: - `react@experimental` - `react-dom@experimental` - `eslint-plugin-react-hooks@experimental` -Experimental versions of React may contain bugs. Don't use them in production. +Các phiên bản thử nghiệm của React có thể chứa lỗi. Không sử dụng chúng trong môi trường production. </Wip> - <Intro> -`useEffectEvent` is a React Hook that lets you extract non-reactive logic into an [Effect Event.](/learn/separating-events-from-effects#declaring-an-effect-event) +`useEffectEvent` là một React Hook cho phép bạn trích xuất logic không phản ứng vào một [Effect Event.](/learn/separating-events-from-effects#declaring-an-effect-event) ```js const onSomething = useEffectEvent(callback) diff --git a/src/content/reference/react/forwardRef.md b/src/content/reference/react/forwardRef.md index 5b0e679b8..c39a9e0d0 100644 --- a/src/content/reference/react/forwardRef.md +++ b/src/content/reference/react/forwardRef.md @@ -4,15 +4,15 @@ title: forwardRef <Deprecated> -In React 19, `forwardRef` is no longer necessary. Pass `ref` as a prop instead. +Trong React 19, `forwardRef` không còn cần thiết nữa. Thay vào đó, hãy truyền `ref` như một prop. -`forwardRef` will deprecated in a future release. Learn more [here](/blog/2024/04/25/react-19#ref-as-a-prop). +`forwardRef` sẽ bị loại bỏ trong một bản phát hành trong tương lai. Tìm hiểu thêm [tại đây](/blog/2024/04/25/react-19#ref-as-a-prop). </Deprecated> <Intro> -`forwardRef` lets your component expose a DOM node to parent component with a [ref.](/learn/manipulating-the-dom-with-refs) +`forwardRef` cho phép component của bạn hiển thị một DOM node cho component cha bằng một [ref.](/learn/manipulating-the-dom-with-refs) ```js const SomeComponent = forwardRef(render) @@ -24,11 +24,11 @@ const SomeComponent = forwardRef(render) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `forwardRef(render)` {/*forwardref*/} -Call `forwardRef()` to let your component receive a ref and forward it to a child component: +Gọi `forwardRef()` để cho phép component của bạn nhận một ref và chuyển nó đến một component con: ```js import { forwardRef } from 'react'; @@ -38,26 +38,26 @@ const MyInput = forwardRef(function MyInput(props, ref) { }); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `render`: The render function for your component. React calls this function with the props and `ref` that your component received from its parent. The JSX you return will be the output of your component. +* `render`: Hàm render cho component của bạn. React gọi hàm này với các props và `ref` mà component của bạn nhận được từ component cha. JSX bạn trả về sẽ là đầu ra của component của bạn. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`forwardRef` returns a React component that you can render in JSX. Unlike React components defined as plain functions, a component returned by `forwardRef` is also able to receive a `ref` prop. +`forwardRef` trả về một React component mà bạn có thể render trong JSX. Không giống như các React component được định nghĩa là các hàm thuần túy, một component được trả về bởi `forwardRef` cũng có thể nhận một prop `ref`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* In Strict Mode, React will **call your render function twice** in order to [help you find accidental impurities.](/reference/react/useState#my-initializer-or-updater-function-runs-twice) This is development-only behavior and does not affect production. If your render function is pure (as it should be), this should not affect the logic of your component. The result from one of the calls will be ignored. +* Trong Strict Mode, React sẽ **gọi hàm render của bạn hai lần** để [giúp bạn tìm các tạp chất vô tình.](/reference/react/useState#my-initializer-or-updater-function-runs-twice) Đây là hành vi chỉ dành cho quá trình phát triển và không ảnh hưởng đến production. Nếu hàm render của bạn là thuần túy (như nó phải vậy), điều này sẽ không ảnh hưởng đến logic của component của bạn. Kết quả từ một trong các lệnh gọi sẽ bị bỏ qua. --- -### `render` function {/*render-function*/} +### Hàm `render` {/*render-function*/} -`forwardRef` accepts a render function as an argument. React calls this function with `props` and `ref`: +`forwardRef` chấp nhận một hàm render làm đối số. React gọi hàm này với `props` và `ref`: ```js const MyInput = forwardRef(function MyInput(props, ref) { @@ -70,23 +70,23 @@ const MyInput = forwardRef(function MyInput(props, ref) { }); ``` -#### Parameters {/*render-parameters*/} +#### Tham số {/*render-parameters*/} -* `props`: The props passed by the parent component. +* `props`: Các props được truyền bởi component cha. -* `ref`: The `ref` attribute passed by the parent component. The `ref` can be an object or a function. If the parent component has not passed a ref, it will be `null`. You should either pass the `ref` you receive to another component, or pass it to [`useImperativeHandle`.](/reference/react/useImperativeHandle) +* `ref`: Thuộc tính `ref` được truyền bởi component cha. `ref` có thể là một đối tượng hoặc một hàm. Nếu component cha không truyền ref, nó sẽ là `null`. Bạn nên truyền `ref` bạn nhận được cho một component khác hoặc truyền nó cho [`useImperativeHandle`.](/reference/react/useImperativeHandle) -#### Returns {/*render-returns*/} +#### Giá trị trả về {/*render-returns*/} -`forwardRef` returns a React component that you can render in JSX. Unlike React components defined as plain functions, the component returned by `forwardRef` is able to take a `ref` prop. +`forwardRef` trả về một React component mà bạn có thể render trong JSX. Không giống như các React component được định nghĩa là các hàm thuần túy, component được trả về bởi `forwardRef` có thể nhận một prop `ref`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Exposing a DOM node to the parent component {/*exposing-a-dom-node-to-the-parent-component*/} +### Hiển thị một DOM node cho component cha {/*exposing-a-dom-node-to-the-parent-component*/} -By default, each component's DOM nodes are private. However, sometimes it's useful to expose a DOM node to the parent--for example, to allow focusing it. To opt in, wrap your component definition into `forwardRef()`: +Theo mặc định, các DOM node của mỗi component là riêng tư. Tuy nhiên, đôi khi hữu ích khi hiển thị một DOM node cho component cha--ví dụ: để cho phép focus nó. Để chọn tham gia, hãy bọc định nghĩa component của bạn vào `forwardRef()`: ```js {3,11} import { forwardRef } from 'react'; @@ -102,7 +102,7 @@ const MyInput = forwardRef(function MyInput(props, ref) { }); ``` -You will receive a <CodeStep step={1}>ref</CodeStep> as the second argument after props. Pass it to the DOM node that you want to expose: +Bạn sẽ nhận được một <CodeStep step={1}>ref</CodeStep> làm đối số thứ hai sau props. Truyền nó đến DOM node mà bạn muốn hiển thị: ```js {8} [[1, 3, "ref"], [1, 8, "ref", 30]] import { forwardRef } from 'react'; @@ -118,7 +118,7 @@ const MyInput = forwardRef(function MyInput(props, ref) { }); ``` -This lets the parent `Form` component access the <CodeStep step={2}>`<input>` DOM node</CodeStep> exposed by `MyInput`: +Điều này cho phép component `Form` cha truy cập <CodeStep step={2}>`<input>` DOM node</CodeStep> được hiển thị bởi `MyInput`: ```js [[1, 2, "ref"], [1, 10, "ref", 41], [2, 5, "ref.current"]] function Form() { @@ -139,15 +139,15 @@ function Form() { } ``` -This `Form` component [passes a ref](/reference/react/useRef#manipulating-the-dom-with-a-ref) to `MyInput`. The `MyInput` component *forwards* that ref to the `<input>` browser tag. As a result, the `Form` component can access that `<input>` DOM node and call [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on it. +Component `Form` này [truyền một ref](/reference/react/useRef#manipulating-the-dom-with-a-ref) cho `MyInput`. Component `MyInput` *chuyển tiếp* ref đó đến thẻ trình duyệt `<input>`. Do đó, component `Form` có thể truy cập DOM node `<input>` đó và gọi [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) trên nó. -Keep in mind that exposing a ref to the DOM node inside your component makes it harder to change your component's internals later. You will typically expose DOM nodes from reusable low-level components like buttons or text inputs, but you won't do it for application-level components like an avatar or a comment. +Hãy nhớ rằng việc hiển thị một ref cho DOM node bên trong component của bạn sẽ khiến việc thay đổi các thành phần bên trong của component sau này trở nên khó khăn hơn. Bạn thường sẽ hiển thị các DOM node từ các component cấp thấp có thể tái sử dụng như nút hoặc đầu vào văn bản, nhưng bạn sẽ không làm điều đó cho các component cấp ứng dụng như hình đại diện hoặc nhận xét. -<Recipes titleText="Examples of forwarding a ref"> +<Recipes titleText="Ví dụ về chuyển tiếp ref"> -#### Focusing a text input {/*focusing-a-text-input*/} +#### Tập trung vào một đầu vào văn bản {/*focusing-a-text-input*/} -Clicking the button will focus the input. The `Form` component defines a ref and passes it to the `MyInput` component. The `MyInput` component forwards that ref to the browser `<input>`. This lets the `Form` component focus the `<input>`. +Nhấp vào nút sẽ tập trung vào đầu vào. Component `Form` xác định một ref và truyền nó cho component `MyInput`. Component `MyInput` chuyển tiếp ref đó đến trình duyệt `<input>`. Điều này cho phép component `Form` tập trung vào `<input>`. <Sandpack> @@ -199,9 +199,9 @@ input { <Solution /> -#### Playing and pausing a video {/*playing-and-pausing-a-video*/} +#### Phát và tạm dừng video {/*playing-and-pausing-a-video*/} -Clicking the button will call [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) and [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) on a `<video>` DOM node. The `App` component defines a ref and passes it to the `MyVideoPlayer` component. The `MyVideoPlayer` component forwards that ref to the browser `<video>` node. This lets the `App` component play and pause the `<video>`. +Nhấp vào nút sẽ gọi [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) và [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) trên một DOM node `<video>`. Component `App` xác định một ref và truyền nó cho component `MyVideoPlayer`. Component `MyVideoPlayer` chuyển tiếp ref đó đến node trình duyệt `<video>`. Điều này cho phép component `App` phát và tạm dừng `<video>`. <Sandpack> @@ -260,9 +260,9 @@ button { margin-bottom: 10px; margin-right: 10px; } --- -### Forwarding a ref through multiple components {/*forwarding-a-ref-through-multiple-components*/} +### Chuyển tiếp một ref qua nhiều component {/*forwarding-a-ref-through-multiple-components*/} -Instead of forwarding a `ref` to a DOM node, you can forward it to your own component like `MyInput`: +Thay vì chuyển tiếp một `ref` đến một DOM node, bạn có thể chuyển tiếp nó đến component của riêng bạn như `MyInput`: ```js {1,5} const FormField = forwardRef(function FormField(props, ref) { @@ -276,7 +276,7 @@ const FormField = forwardRef(function FormField(props, ref) { }); ``` -If that `MyInput` component forwards a ref to its `<input>`, a ref to `FormField` will give you that `<input>`: +Nếu component `MyInput` đó chuyển tiếp một ref đến `<input>` của nó, một ref đến `FormField` sẽ cung cấp cho bạn `<input>` đó: ```js {2,5,10} function Form() { @@ -297,7 +297,7 @@ function Form() { } ``` -The `Form` component defines a ref and passes it to `FormField`. The `FormField` component forwards that ref to `MyInput`, which forwards it to a browser `<input>` DOM node. This is how `Form` accesses that DOM node. +Component `Form` xác định một ref và truyền nó cho `FormField`. Component `FormField` chuyển tiếp ref đó đến `MyInput`, và `MyInput` chuyển tiếp nó đến một DOM node `<input>` của trình duyệt. Đây là cách `Form` truy cập DOM node đó. <Sandpack> @@ -375,9 +375,9 @@ input, button { --- -### Exposing an imperative handle instead of a DOM node {/*exposing-an-imperative-handle-instead-of-a-dom-node*/} +### Hiển thị một imperative handle thay vì một DOM node {/*exposing-an-imperative-handle-instead-of-a-dom-node*/} -Instead of exposing an entire DOM node, you can expose a custom object, called an *imperative handle,* with a more constrained set of methods. To do this, you'd need to define a separate ref to hold the DOM node: +Thay vì hiển thị toàn bộ DOM node, bạn có thể hiển thị một đối tượng tùy chỉnh, được gọi là *imperative handle,* với một tập hợp các phương thức bị ràng buộc hơn. Để làm điều này, bạn cần xác định một ref riêng biệt để giữ DOM node: ```js {2,6} const MyInput = forwardRef(function MyInput(props, ref) { @@ -389,7 +389,7 @@ const MyInput = forwardRef(function MyInput(props, ref) { }); ``` -Pass the `ref` you received to [`useImperativeHandle`](/reference/react/useImperativeHandle) and specify the value you want to expose to the `ref`: +Truyền `ref` bạn nhận được cho [`useImperativeHandle`](/reference/react/useImperativeHandle) và chỉ định giá trị bạn muốn hiển thị cho `ref`: ```js {6-15} import { forwardRef, useRef, useImperativeHandle } from 'react'; @@ -412,7 +412,7 @@ const MyInput = forwardRef(function MyInput(props, ref) { }); ``` -If some component gets a ref to `MyInput`, it will only receive your `{ focus, scrollIntoView }` object instead of the DOM node. This lets you limit the information you expose about your DOM node to the minimum. +Nếu một số component nhận được một ref đến `MyInput`, nó sẽ chỉ nhận được đối tượng `{ focus, scrollIntoView }` của bạn thay vì DOM node. Điều này cho phép bạn giới hạn thông tin bạn hiển thị về DOM node của mình ở mức tối thiểu. <Sandpack> @@ -471,25 +471,25 @@ input { </Sandpack> -[Read more about using imperative handles.](/reference/react/useImperativeHandle) +[Đọc thêm về sử dụng imperative handle.](/reference/react/useImperativeHandle) <Pitfall> -**Do not overuse refs.** You should only use refs for *imperative* behaviors that you can't express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on. +**Không lạm dụng refs.** Bạn chỉ nên sử dụng refs cho các hành vi *bắt buộc* mà bạn không thể thể hiện dưới dạng props: ví dụ: cuộn đến một node, tập trung vào một node, kích hoạt một hoạt ảnh, chọn văn bản, v.v. -**If you can express something as a prop, you should not use a ref.** For example, instead of exposing an imperative handle like `{ open, close }` from a `Modal` component, it is better to take `isOpen` as a prop like `<Modal isOpen={isOpen} />`. [Effects](/learn/synchronizing-with-effects) can help you expose imperative behaviors via props. +**Nếu bạn có thể thể hiện một cái gì đó như một prop, bạn không nên sử dụng ref.** Ví dụ: thay vì hiển thị một imperative handle như `{ open, close }` từ một component `Modal`, tốt hơn là lấy `isOpen` làm một prop như `<Modal isOpen={isOpen} />`. [Effects](/learn/synchronizing-with-effects) có thể giúp bạn hiển thị các hành vi bắt buộc thông qua props. </Pitfall> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My component is wrapped in `forwardRef`, but the `ref` to it is always `null` {/*my-component-is-wrapped-in-forwardref-but-the-ref-to-it-is-always-null*/} +### Component của tôi được bọc trong `forwardRef`, nhưng `ref` đến nó luôn là `null` {/*my-component-is-wrapped-in-forwardref-but-the-ref-to-it-is-always-null*/} -This usually means that you forgot to actually use the `ref` that you received. +Điều này thường có nghĩa là bạn đã quên sử dụng `ref` mà bạn đã nhận được. -For example, this component doesn't do anything with its `ref`: +Ví dụ: component này không làm gì với `ref` của nó: ```js {1} const MyInput = forwardRef(function MyInput({ label }, ref) { @@ -502,7 +502,7 @@ const MyInput = forwardRef(function MyInput({ label }, ref) { }); ``` -To fix it, pass the `ref` down to a DOM node or another component that can accept a ref: +Để khắc phục, hãy truyền `ref` xuống một DOM node hoặc một component khác có thể chấp nhận một ref: ```js {1,5} const MyInput = forwardRef(function MyInput({ label }, ref) { @@ -515,7 +515,7 @@ const MyInput = forwardRef(function MyInput({ label }, ref) { }); ``` -The `ref` to `MyInput` could also be `null` if some of the logic is conditional: +`ref` đến `MyInput` cũng có thể là `null` nếu một số logic là có điều kiện: ```js {1,5} const MyInput = forwardRef(function MyInput({ label, showInput }, ref) { @@ -528,7 +528,7 @@ const MyInput = forwardRef(function MyInput({ label, showInput }, ref) { }); ``` -If `showInput` is `false`, then the ref won't be forwarded to any node, and a ref to `MyInput` will remain empty. This is particularly easy to miss if the condition is hidden inside another component, like `Panel` in this example: +Nếu `showInput` là `false`, thì ref sẽ không được chuyển tiếp đến bất kỳ node nào và một ref đến `MyInput` sẽ vẫn trống. Điều này đặc biệt dễ bỏ lỡ nếu điều kiện được ẩn bên trong một component khác, như `Panel` trong ví dụ này: ```js {5,7} const MyInput = forwardRef(function MyInput({ label, showInput }, ref) { diff --git a/src/content/reference/react/hooks.md b/src/content/reference/react/hooks.md index 6dea3a0fd..00654e29f 100644 --- a/src/content/reference/react/hooks.md +++ b/src/content/reference/react/hooks.md @@ -1,10 +1,10 @@ --- -title: "Built-in React Hooks" +title: "Các React Hook tích hợp sẵn" --- <Intro> -*Hooks* let you use different React features from your components. You can either use the built-in Hooks or combine them to build your own. This page lists all built-in Hooks in React. +*Hook* cho phép bạn sử dụng các tính năng khác nhau của React từ các component của bạn. Bạn có thể sử dụng các Hook tích hợp sẵn hoặc kết hợp chúng để xây dựng Hook của riêng bạn. Trang này liệt kê tất cả các Hook tích hợp sẵn trong React. </Intro> @@ -12,12 +12,12 @@ title: "Built-in React Hooks" ## State Hooks {/*state-hooks*/} -*State* lets a component ["remember" information like user input.](/learn/state-a-components-memory) For example, a form component can use state to store the input value, while an image gallery component can use state to store the selected image index. +*State* cho phép một component ["ghi nhớ" thông tin như đầu vào của người dùng.](/learn/state-a-components-memory) Ví dụ: một component form có thể sử dụng state để lưu trữ giá trị đầu vào, trong khi một component thư viện ảnh có thể sử dụng state để lưu trữ chỉ mục ảnh đã chọn. -To add state to a component, use one of these Hooks: +Để thêm state vào một component, hãy sử dụng một trong các Hook sau: -* [`useState`](/reference/react/useState) declares a state variable that you can update directly. -* [`useReducer`](/reference/react/useReducer) declares a state variable with the update logic inside a [reducer function.](/learn/extracting-state-logic-into-a-reducer) +* [`useState`](/reference/react/useState) khai báo một biến state mà bạn có thể cập nhật trực tiếp. +* [`useReducer`](/reference/react/useReducer) khai báo một biến state với logic cập nhật bên trong một [hàm reducer.](/learn/extracting-state-logic-into-a-reducer) ```js function ImageGallery() { @@ -29,9 +29,9 @@ function ImageGallery() { ## Context Hooks {/*context-hooks*/} -*Context* lets a component [receive information from distant parents without passing it as props.](/learn/passing-props-to-a-component) For example, your app's top-level component can pass the current UI theme to all components below, no matter how deep. +*Context* cho phép một component [nhận thông tin từ các component cha ở xa mà không cần truyền nó dưới dạng props.](/learn/passing-props-to-a-component) Ví dụ: component cấp cao nhất của ứng dụng của bạn có thể truyền theme UI hiện tại cho tất cả các component bên dưới, bất kể độ sâu. -* [`useContext`](/reference/react/useContext) reads and subscribes to a context. +* [`useContext`](/reference/react/useContext) đọc và đăng ký một context. ```js function Button() { @@ -43,10 +43,10 @@ function Button() { ## Ref Hooks {/*ref-hooks*/} -*Refs* let a component [hold some information that isn't used for rendering,](/learn/referencing-values-with-refs) like a DOM node or a timeout ID. Unlike with state, updating a ref does not re-render your component. Refs are an "escape hatch" from the React paradigm. They are useful when you need to work with non-React systems, such as the built-in browser APIs. +*Refs* cho phép một component [giữ một số thông tin không được sử dụng để hiển thị,](/learn/referencing-values-with-refs) như một DOM node hoặc một ID timeout. Không giống như state, việc cập nhật một ref không làm component của bạn render lại. Refs là một "cửa thoát hiểm" khỏi mô hình React. Chúng hữu ích khi bạn cần làm việc với các hệ thống không phải React, chẳng hạn như các API trình duyệt tích hợp sẵn. -* [`useRef`](/reference/react/useRef) declares a ref. You can hold any value in it, but most often it's used to hold a DOM node. -* [`useImperativeHandle`](/reference/react/useImperativeHandle) lets you customize the ref exposed by your component. This is rarely used. +* [`useRef`](/reference/react/useRef) khai báo một ref. Bạn có thể giữ bất kỳ giá trị nào trong đó, nhưng thường được sử dụng để giữ một DOM node. +* [`useImperativeHandle`](/reference/react/useImperativeHandle) cho phép bạn tùy chỉnh ref được hiển thị bởi component của bạn. Điều này hiếm khi được sử dụng. ```js function Form() { @@ -58,9 +58,9 @@ function Form() { ## Effect Hooks {/*effect-hooks*/} -*Effects* let a component [connect to and synchronize with external systems.](/learn/synchronizing-with-effects) This includes dealing with network, browser DOM, animations, widgets written using a different UI library, and other non-React code. +*Effects* cho phép một component [kết nối và đồng bộ hóa với các hệ thống bên ngoài.](/learn/synchronizing-with-effects) Điều này bao gồm việc xử lý mạng, DOM của trình duyệt, hoạt ảnh, các widget được viết bằng một thư viện UI khác và các code không phải React khác. -* [`useEffect`](/reference/react/useEffect) connects a component to an external system. +* [`useEffect`](/reference/react/useEffect) kết nối một component với một hệ thống bên ngoài. ```js function ChatRoom({ roomId }) { @@ -72,23 +72,23 @@ function ChatRoom({ roomId }) { // ... ``` -Effects are an "escape hatch" from the React paradigm. Don't use Effects to orchestrate the data flow of your application. If you're not interacting with an external system, [you might not need an Effect.](/learn/you-might-not-need-an-effect) +Effects là một "cửa thoát hiểm" khỏi mô hình React. Không sử dụng Effects để điều phối luồng dữ liệu của ứng dụng của bạn. Nếu bạn không tương tác với một hệ thống bên ngoài, [bạn có thể không cần Effect.](/learn/you-might-not-need-an-effect) -There are two rarely used variations of `useEffect` with differences in timing: +Có hai biến thể hiếm khi được sử dụng của `useEffect` với sự khác biệt về thời gian: -* [`useLayoutEffect`](/reference/react/useLayoutEffect) fires before the browser repaints the screen. You can measure layout here. -* [`useInsertionEffect`](/reference/react/useInsertionEffect) fires before React makes changes to the DOM. Libraries can insert dynamic CSS here. +* [`useLayoutEffect`](/reference/react/useLayoutEffect) kích hoạt trước khi trình duyệt vẽ lại màn hình. Bạn có thể đo layout ở đây. +* [`useInsertionEffect`](/reference/react/useInsertionEffect) kích hoạt trước khi React thực hiện các thay đổi đối với DOM. Các thư viện có thể chèn CSS động ở đây. --- ## Performance Hooks {/*performance-hooks*/} -A common way to optimize re-rendering performance is to skip unnecessary work. For example, you can tell React to reuse a cached calculation or to skip a re-render if the data has not changed since the previous render. +Một cách phổ biến để tối ưu hóa hiệu suất render lại là bỏ qua các công việc không cần thiết. Ví dụ: bạn có thể yêu cầu React sử dụng lại một phép tính đã được lưu trong bộ nhớ cache hoặc bỏ qua việc render lại nếu dữ liệu không thay đổi kể từ lần render trước. -To skip calculations and unnecessary re-rendering, use one of these Hooks: +Để bỏ qua các phép tính và render lại không cần thiết, hãy sử dụng một trong các Hook sau: -- [`useMemo`](/reference/react/useMemo) lets you cache the result of an expensive calculation. -- [`useCallback`](/reference/react/useCallback) lets you cache a function definition before passing it down to an optimized component. +- [`useMemo`](/reference/react/useMemo) cho phép bạn lưu vào bộ nhớ cache kết quả của một phép tính tốn kém. +- [`useCallback`](/reference/react/useCallback) cho phép bạn lưu vào bộ nhớ cache một định nghĩa hàm trước khi truyền nó xuống một component đã được tối ưu hóa. ```js function TodoList({ todos, tab, theme }) { @@ -97,26 +97,26 @@ function TodoList({ todos, tab, theme }) { } ``` -Sometimes, you can't skip re-rendering because the screen actually needs to update. In that case, you can improve performance by separating blocking updates that must be synchronous (like typing into an input) from non-blocking updates which don't need to block the user interface (like updating a chart). +Đôi khi, bạn không thể bỏ qua việc render lại vì màn hình thực sự cần được cập nhật. Trong trường hợp đó, bạn có thể cải thiện hiệu suất bằng cách tách các cập nhật chặn phải đồng bộ (như nhập vào một input) khỏi các cập nhật không chặn không cần chặn giao diện người dùng (như cập nhật biểu đồ). -To prioritize rendering, use one of these Hooks: +Để ưu tiên render, hãy sử dụng một trong các Hook sau: -- [`useTransition`](/reference/react/useTransition) lets you mark a state transition as non-blocking and allow other updates to interrupt it. -- [`useDeferredValue`](/reference/react/useDeferredValue) lets you defer updating a non-critical part of the UI and let other parts update first. +- [`useTransition`](/reference/react/useTransition) cho phép bạn đánh dấu một chuyển đổi state là không chặn và cho phép các cập nhật khác làm gián đoạn nó. +- [`useDeferredValue`](/reference/react/useDeferredValue) cho phép bạn trì hoãn việc cập nhật một phần không quan trọng của UI và cho phép các phần khác cập nhật trước. --- ## Other Hooks {/*other-hooks*/} -These Hooks are mostly useful to library authors and aren't commonly used in the application code. +Các Hook này chủ yếu hữu ích cho các tác giả thư viện và không được sử dụng phổ biến trong code ứng dụng. -- [`useDebugValue`](/reference/react/useDebugValue) lets you customize the label React DevTools displays for your custom Hook. -- [`useId`](/reference/react/useId) lets a component associate a unique ID with itself. Typically used with accessibility APIs. -- [`useSyncExternalStore`](/reference/react/useSyncExternalStore) lets a component subscribe to an external store. -* [`useActionState`](/reference/react/useActionState) allows you to manage state of actions. +- [`useDebugValue`](/reference/react/useDebugValue) cho phép bạn tùy chỉnh nhãn mà React DevTools hiển thị cho Hook tùy chỉnh của bạn. +- [`useId`](/reference/react/useId) cho phép một component liên kết một ID duy nhất với chính nó. Thường được sử dụng với các API hỗ trợ tiếp cận. +- [`useSyncExternalStore`](/reference/react/useSyncExternalStore) cho phép một component đăng ký một kho bên ngoài. +* [`useActionState`](/reference/react/useActionState) cho phép bạn quản lý trạng thái của các hành động. --- ## Your own Hooks {/*your-own-hooks*/} -You can also [define your own custom Hooks](/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component) as JavaScript functions. +Bạn cũng có thể [xác định các Hook tùy chỉnh của riêng bạn](/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component) dưới dạng các hàm JavaScript. diff --git a/src/content/reference/react/index.md b/src/content/reference/react/index.md index a68ddc014..a8d1484bb 100644 --- a/src/content/reference/react/index.md +++ b/src/content/reference/react/index.md @@ -1,42 +1,42 @@ --- -title: React Reference Overview +title: Tổng quan tài liệu tham khảo React --- <Intro> -This section provides detailed reference documentation for working with React. For an introduction to React, please visit the [Learn](/learn) section. +Phần này cung cấp tài liệu tham khảo chi tiết để làm việc với React. Để có phần giới thiệu về React, vui lòng truy cập phần [Học tập](/learn). </Intro> -The React reference documentation is broken down into functional subsections: +Tài liệu tham khảo React được chia thành các phần chức năng sau: ## React {/*react*/} -Programmatic React features: +Các tính năng React theo chương trình: -* [Hooks](/reference/react/hooks) - Use different React features from your components. -* [Components](/reference/react/components) - Built-in components that you can use in your JSX. -* [APIs](/reference/react/apis) - APIs that are useful for defining components. -* [Directives](/reference/rsc/directives) - Provide instructions to bundlers compatible with React Server Components. +* [Hooks](/reference/react/hooks) - Sử dụng các tính năng React khác nhau từ các components của bạn. +* [Components](/reference/react/components) - Các components dựng sẵn mà bạn có thể sử dụng trong JSX của mình. +* [APIs](/reference/react/apis) - Các API hữu ích để xác định các components. +* [Directives](/reference/rsc/directives) - Cung cấp hướng dẫn cho các bundlers tương thích với React Server Components. ## React DOM {/*react-dom*/} -React-dom contains features that are only supported for web applications (which run in the browser DOM environment). This section is broken into the following: +React-dom chứa các tính năng chỉ được hỗ trợ cho các ứng dụng web (chạy trong môi trường DOM của trình duyệt). Phần này được chia thành các phần sau: -* [Hooks](/reference/react-dom/hooks) - Hooks for web applications which run in the browser DOM environment. -* [Components](/reference/react-dom/components) - React supports all of the browser built-in HTML and SVG components. -* [APIs](/reference/react-dom) - The `react-dom` package contains methods supported only in web applications. -* [Client APIs](/reference/react-dom/client) - The `react-dom/client` APIs let you render React components on the client (in the browser). -* [Server APIs](/reference/react-dom/server) - The `react-dom/server` APIs let you render React components to HTML on the server. +* [Hooks](/reference/react-dom/hooks) - Hooks cho các ứng dụng web chạy trong môi trường DOM của trình duyệt. +* [Components](/reference/react-dom/components) - React hỗ trợ tất cả các components HTML và SVG được tích hợp sẵn của trình duyệt. +* [APIs](/reference/react-dom) - Gói `react-dom` chứa các phương thức chỉ được hỗ trợ trong các ứng dụng web. +* [Client APIs](/reference/react-dom/client) - Các API `react-dom/client` cho phép bạn hiển thị các React components trên máy khách (trong trình duyệt). +* [Server APIs](/reference/react-dom/server) - Các API `react-dom/server` cho phép bạn hiển thị các React components thành HTML trên máy chủ. -## Rules of React {/*rules-of-react*/} +## Các quy tắc của React {/*rules-of-react*/} -React has idioms — or rules — for how to express patterns in a way that is easy to understand and yields high-quality applications: +React có các thành ngữ — hoặc quy tắc — về cách thể hiện các mẫu theo cách dễ hiểu và mang lại các ứng dụng chất lượng cao: -* [Components and Hooks must be pure](/reference/rules/components-and-hooks-must-be-pure) – Purity makes your code easier to understand, debug, and allows React to automatically optimize your components and hooks correctly. -* [React calls Components and Hooks](/reference/rules/react-calls-components-and-hooks) – React is responsible for rendering components and hooks when necessary to optimize the user experience. -* [Rules of Hooks](/reference/rules/rules-of-hooks) – Hooks are defined using JavaScript functions, but they represent a special type of reusable UI logic with restrictions on where they can be called. +* [Components và Hooks phải thuần khiết](/reference/rules/components-and-hooks-must-be-pure) – Sự thuần khiết giúp mã của bạn dễ hiểu, gỡ lỗi và cho phép React tự động tối ưu hóa các components và hooks của bạn một cách chính xác. +* [React gọi Components và Hooks](/reference/rules/react-calls-components-and-hooks) – React chịu trách nhiệm hiển thị các components và hooks khi cần thiết để tối ưu hóa trải nghiệm người dùng. +* [Quy tắc của Hooks](/reference/rules/rules-of-hooks) – Hooks được định nghĩa bằng các hàm JavaScript, nhưng chúng đại diện cho một loại logic UI có thể tái sử dụng đặc biệt với các hạn chế về nơi chúng có thể được gọi. -## Legacy APIs {/*legacy-apis*/} +## API kế thừa {/*legacy-apis*/} -* [Legacy APIs](/reference/react/legacy) - Exported from the `react` package, but not recommended for use in newly written code. +* [API kế thừa](/reference/react/legacy) - Được xuất từ gói `react`, nhưng không được khuyến nghị sử dụng trong mã mới được viết. diff --git a/src/content/reference/react/isValidElement.md b/src/content/reference/react/isValidElement.md index bc4fbaef4..c87706b88 100644 --- a/src/content/reference/react/isValidElement.md +++ b/src/content/reference/react/isValidElement.md @@ -4,7 +4,7 @@ title: isValidElement <Intro> -`isValidElement` checks whether a value is a React element. +`isValidElement` kiểm tra xem một giá trị có phải là một React element hay không. ```js const isElement = isValidElement(value) @@ -16,11 +16,11 @@ const isElement = isValidElement(value) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `isValidElement(value)` {/*isvalidelement*/} -Call `isValidElement(value)` to check whether `value` is a React element. +Gọi `isValidElement(value)` để kiểm tra xem `value` có phải là một React element hay không. ```js import { isValidElement, createElement } from 'react'; @@ -29,59 +29,59 @@ import { isValidElement, createElement } from 'react'; console.log(isValidElement(<p />)); // true console.log(isValidElement(createElement('p'))); // true -// ❌ Not React elements +// ❌ Không phải React elements console.log(isValidElement(25)); // false console.log(isValidElement('Hello')); // false console.log(isValidElement({ age: 42 })); // false ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `value`: The `value` you want to check. It can be any a value of any type. +* `value`: Giá trị bạn muốn kiểm tra. Nó có thể là bất kỳ giá trị nào thuộc bất kỳ kiểu dữ liệu nào. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`isValidElement` returns `true` if the `value` is a React element. Otherwise, it returns `false`. +`isValidElement` trả về `true` nếu `value` là một React element. Ngược lại, nó trả về `false`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* **Only [JSX tags](/learn/writing-markup-with-jsx) and objects returned by [`createElement`](/reference/react/createElement) are considered to be React elements.** For example, even though a number like `42` is a valid React *node* (and can be returned from a component), it is not a valid React element. Arrays and portals created with [`createPortal`](/reference/react-dom/createPortal) are also *not* considered to be React elements. +* **Chỉ [thẻ JSX](/learn/writing-markup-with-jsx) và các đối tượng được trả về bởi [`createElement`](/reference/react/createElement) được coi là React elements.** Ví dụ: mặc dù một số như `42` là một React *node* hợp lệ (và có thể được trả về từ một component), nhưng nó không phải là một React element hợp lệ. Mảng và portals được tạo bằng [`createPortal`](/reference/react-dom/createPortal) cũng *không* được coi là React elements. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Checking if something is a React element {/*checking-if-something-is-a-react-element*/} +### Kiểm tra xem một thứ có phải là một React element hay không {/*checking-if-something-is-a-react-element*/} -Call `isValidElement` to check if some value is a *React element.* +Gọi `isValidElement` để kiểm tra xem một giá trị nào đó có phải là một *React element* hay không. -React elements are: +React elements là: -- Values produced by writing a [JSX tag](/learn/writing-markup-with-jsx) -- Values produced by calling [`createElement`](/reference/react/createElement) +- Các giá trị được tạo ra bằng cách viết một [thẻ JSX](/learn/writing-markup-with-jsx) +- Các giá trị được tạo ra bằng cách gọi [`createElement`](/reference/react/createElement) -For React elements, `isValidElement` returns `true`: +Đối với React elements, `isValidElement` trả về `true`: ```js import { isValidElement, createElement } from 'react'; -// ✅ JSX tags are React elements +// ✅ Thẻ JSX là React elements console.log(isValidElement(<p />)); // true console.log(isValidElement(<MyComponent />)); // true -// ✅ Values returned by createElement are React elements +// ✅ Các giá trị được trả về bởi createElement là React elements console.log(isValidElement(createElement('p'))); // true console.log(isValidElement(createElement(MyComponent))); // true ``` -Any other values, such as strings, numbers, or arbitrary objects and arrays, are not React elements. +Bất kỳ giá trị nào khác, chẳng hạn như chuỗi, số hoặc các đối tượng và mảng tùy ý, không phải là React elements. -For them, `isValidElement` returns `false`: +Đối với chúng, `isValidElement` trả về `false`: ```js -// ❌ These are *not* React elements +// ❌ Đây *không phải* là React elements console.log(isValidElement(null)); // false console.log(isValidElement(25)); // false console.log(isValidElement('Hello')); // false @@ -90,39 +90,39 @@ console.log(isValidElement([<div />, <div />])); // false console.log(isValidElement(MyComponent)); // false ``` -It is very uncommon to need `isValidElement`. It's mostly useful if you're calling another API that *only* accepts elements (like [`cloneElement`](/reference/react/cloneElement) does) and you want to avoid an error when your argument is not a React element. +Rất hiếm khi cần đến `isValidElement`. Nó chủ yếu hữu ích nếu bạn đang gọi một API khác *chỉ* chấp nhận các elements (như [`cloneElement`](/reference/react/cloneElement) chẳng hạn) và bạn muốn tránh lỗi khi đối số của bạn không phải là một React element. -Unless you have some very specific reason to add an `isValidElement` check, you probably don't need it. +Trừ khi bạn có một lý do rất cụ thể để thêm một kiểm tra `isValidElement`, có lẽ bạn không cần nó. <DeepDive> -#### React elements vs React nodes {/*react-elements-vs-react-nodes*/} +#### React elements so với React nodes {/*react-elements-vs-react-nodes*/} -When you write a component, you can return any kind of *React node* from it: +Khi bạn viết một component, bạn có thể trả về bất kỳ loại *React node* nào từ nó: ```js function MyComponent() { - // ... you can return any React node ... + // ... bạn có thể trả về bất kỳ React node nào ... } ``` -A React node can be: +Một React node có thể là: -- A React element created like `<div />` or `createElement('div')` -- A portal created with [`createPortal`](/reference/react-dom/createPortal) -- A string -- A number -- `true`, `false`, `null`, or `undefined` (which are not displayed) -- An array of other React nodes +- Một React element được tạo như `<div />` hoặc `createElement('div')` +- Một portal được tạo bằng [`createPortal`](/reference/react-dom/createPortal) +- Một chuỗi +- Một số +- `true`, `false`, `null` hoặc `undefined` (không được hiển thị) +- Một mảng các React nodes khác -**Note `isValidElement` checks whether the argument is a *React element,* not whether it's a React node.** For example, `42` is not a valid React element. However, it is a perfectly valid React node: +**Lưu ý `isValidElement` kiểm tra xem đối số có phải là một *React element* hay không, chứ không phải là một React node.** Ví dụ: `42` không phải là một React element hợp lệ. Tuy nhiên, nó là một React node hoàn toàn hợp lệ: ```js function MyComponent() { - return 42; // It's ok to return a number from component + return 42; // Bạn có thể trả về một số từ component } ``` -This is why you shouldn't use `isValidElement` as a way to check whether something can be rendered. +Đây là lý do tại sao bạn không nên sử dụng `isValidElement` như một cách để kiểm tra xem một thứ gì đó có thể được render hay không. </DeepDive> diff --git a/src/content/reference/react/legacy.md b/src/content/reference/react/legacy.md index b22a9c97e..98259cd46 100644 --- a/src/content/reference/react/legacy.md +++ b/src/content/reference/react/legacy.md @@ -1,35 +1,35 @@ --- -title: "Legacy React APIs" +title: "API React Kế Thừa" --- <Intro> -These APIs are exported from the `react` package, but they are not recommended for use in newly written code. See the linked individual API pages for the suggested alternatives. +Các API này được xuất từ gói `react`, nhưng chúng không được khuyến nghị sử dụng trong mã mới viết. Xem các trang API riêng lẻ được liên kết để biết các giải pháp thay thế được đề xuất. </Intro> --- -## Legacy APIs {/*legacy-apis*/} +## Các API Kế Thừa {/*legacy-apis*/} -* [`Children`](/reference/react/Children) lets you manipulate and transform the JSX received as the `children` prop. [See alternatives.](/reference/react/Children#alternatives) -* [`cloneElement`](/reference/react/cloneElement) lets you create a React element using another element as a starting point. [See alternatives.](/reference/react/cloneElement#alternatives) -* [`Component`](/reference/react/Component) lets you define a React component as a JavaScript class. [See alternatives.](/reference/react/Component#alternatives) -* [`createElement`](/reference/react/createElement) lets you create a React element. Typically, you'll use JSX instead. -* [`createRef`](/reference/react/createRef) creates a ref object which can contain arbitrary value. [See alternatives.](/reference/react/createRef#alternatives) -* [`forwardRef`](/reference/react/forwardRef) lets your component expose a DOM node to parent component with a [ref.](/learn/manipulating-the-dom-with-refs) -* [`isValidElement`](/reference/react/isValidElement) checks whether a value is a React element. Typically used with [`cloneElement`.](/reference/react/cloneElement) -* [`PureComponent`](/reference/react/PureComponent) is similar to [`Component`,](/reference/react/Component) but it skip re-renders with same props. [See alternatives.](/reference/react/PureComponent#alternatives) +* [`Children`](/reference/react/Children) cho phép bạn thao tác và chuyển đổi JSX nhận được dưới dạng prop `children`. [Xem các giải pháp thay thế.](/reference/react/Children#alternatives) +* [`cloneElement`](/reference/react/cloneElement) cho phép bạn tạo một phần tử React bằng cách sử dụng một phần tử khác làm điểm bắt đầu. [Xem các giải pháp thay thế.](/reference/react/cloneElement#alternatives) +* [`Component`](/reference/react/Component) cho phép bạn định nghĩa một component React dưới dạng một lớp JavaScript. [Xem các giải pháp thay thế.](/reference/react/Component#alternatives) +* [`createElement`](/reference/react/createElement) cho phép bạn tạo một phần tử React. Thông thường, bạn sẽ sử dụng JSX thay thế. +* [`createRef`](/reference/react/createRef) tạo một đối tượng ref có thể chứa giá trị tùy ý. [Xem các giải pháp thay thế.](/reference/react/createRef#alternatives) +* [`forwardRef`](/reference/react/forwardRef) cho phép component của bạn hiển thị một nút DOM cho component cha với một [ref.](/learn/manipulating-the-dom-with-refs) +* [`isValidElement`](/reference/react/isValidElement) kiểm tra xem một giá trị có phải là một phần tử React hay không. Thường được sử dụng với [`cloneElement`.](/reference/react/cloneElement) +* [`PureComponent`](/reference/react/PureComponent) tương tự như [`Component`,](/reference/react/Component) nhưng nó bỏ qua việc render lại với các props giống nhau. [Xem các giải pháp thay thế.](/reference/react/PureComponent#alternatives) --- -## Removed APIs {/*removed-apis*/} +## Các API Đã Xóa {/*removed-apis*/} -These APIs were removed in React 19: +Các API này đã bị xóa trong React 19: -* [`createFactory`](https://18.react.dev/reference/react/createFactory): use JSX instead. -* Class Components: [`static contextTypes`](https://18.react.dev//reference/react/Component#static-contexttypes): use [`static contextType`](#static-contexttype) instead. -* Class Components: [`static childContextTypes`](https://18.react.dev//reference/react/Component#static-childcontexttypes): use [`static contextType`](#static-contexttype) instead. -* Class Components: [`static getChildContext`](https://18.react.dev//reference/react/Component#getchildcontext): use [`Context.Provider`](/reference/react/createContext#provider) instead. -* Class Components: [`static propTypes`](https://18.react.dev//reference/react/Component#static-proptypes): use a type system like [TypeScript](https://www.typescriptlang.org/) instead. -* Class Components: [`this.refs`](https://18.react.dev//reference/react/Component#refs): use [`createRef`](/reference/react/createRef) instead. +* [`createFactory`](https://18.react.dev/reference/react/createFactory): sử dụng JSX thay thế. +* Class Components: [`static contextTypes`](https://18.react.dev//reference/react/Component#static-contexttypes): sử dụng [`static contextType`](#static-contexttype) thay thế. +* Class Components: [`static childContextTypes`](https://18.react.dev//reference/react/Component#static-childcontexttypes): sử dụng [`static contextType`](#static-contexttype) thay thế. +* Class Components: [`static getChildContext`](https://18.react.dev//reference/react/Component#getchildcontext): sử dụng [`Context.Provider`](/reference/react/createContext#provider) thay thế. +* Class Components: [`static propTypes`](https://18.react.dev//reference/react/Component#static-proptypes): sử dụng một hệ thống kiểu như [TypeScript](https://www.typescriptlang.org/) thay thế. +* Class Components: [`this.refs`](https://18.react.dev//reference/react/Component#refs): sử dụng [`createRef`](/reference/react/createRef) thay thế. diff --git a/src/content/reference/react/memo.md b/src/content/reference/react/memo.md index 26fa9ed9c..091f7a37e 100644 --- a/src/content/reference/react/memo.md +++ b/src/content/reference/react/memo.md @@ -4,7 +4,7 @@ title: memo <Intro> -`memo` lets you skip re-rendering a component when its props are unchanged. +`memo` cho phép bạn bỏ qua việc kết xuất lại một thành phần khi các đạo cụ của nó không thay đổi. ``` const MemoizedComponent = memo(SomeComponent, arePropsEqual?) @@ -16,11 +16,11 @@ const MemoizedComponent = memo(SomeComponent, arePropsEqual?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `memo(Component, arePropsEqual?)` {/*memo*/} -Wrap a component in `memo` to get a *memoized* version of that component. This memoized version of your component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed. But React may still re-render it: memoization is a performance optimization, not a guarantee. +Bọc một thành phần trong `memo` để có được một phiên bản *ghi nhớ* của thành phần đó. Phiên bản ghi nhớ này của thành phần của bạn thường sẽ không được kết xuất lại khi thành phần cha của nó được kết xuất lại miễn là các đạo cụ của nó không thay đổi. Nhưng React vẫn có thể kết xuất lại nó: ghi nhớ là một tối ưu hóa hiệu suất, không phải là một sự đảm bảo. ```js import { memo } from 'react'; @@ -30,39 +30,39 @@ const SomeComponent = memo(function SomeComponent(props) { }); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `Component`: The component that you want to memoize. The `memo` does not modify this component, but returns a new, memoized component instead. Any valid React component, including functions and [`forwardRef`](/reference/react/forwardRef) components, is accepted. +* `Component`: Thành phần mà bạn muốn ghi nhớ. `memo` không sửa đổi thành phần này, nhưng thay vào đó trả về một thành phần mới, đã ghi nhớ. Bất kỳ thành phần React hợp lệ nào, bao gồm các hàm và các thành phần [`forwardRef`](/reference/react/forwardRef), đều được chấp nhận. -* **optional** `arePropsEqual`: A function that accepts two arguments: the component's previous props, and its new props. It should return `true` if the old and new props are equal: that is, if the component will render the same output and behave in the same way with the new props as with the old. Otherwise it should return `false`. Usually, you will not specify this function. By default, React will compare each prop with [`Object.is`.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +* **tùy chọn** `arePropsEqual`: Một hàm chấp nhận hai đối số: các đạo cụ trước đó của thành phần và các đạo cụ mới của nó. Nó sẽ trả về `true` nếu các đạo cụ cũ và mới bằng nhau: nghĩa là, nếu thành phần sẽ kết xuất cùng một đầu ra và hoạt động theo cùng một cách với các đạo cụ mới như với các đạo cụ cũ. Nếu không, nó sẽ trả về `false`. Thông thường, bạn sẽ không chỉ định hàm này. Theo mặc định, React sẽ so sánh từng đạo cụ với [`Object.is`.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`memo` returns a new React component. It behaves the same as the component provided to `memo` except that React will not always re-render it when its parent is being re-rendered unless its props have changed. +`memo` trả về một thành phần React mới. Nó hoạt động giống như thành phần được cung cấp cho `memo` ngoại trừ việc React sẽ không phải lúc nào cũng kết xuất lại nó khi thành phần cha của nó đang được kết xuất lại trừ khi các đạo cụ của nó đã thay đổi. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Skipping re-rendering when props are unchanged {/*skipping-re-rendering-when-props-are-unchanged*/} +### Bỏ qua việc kết xuất lại khi các đạo cụ không thay đổi {/*skipping-re-rendering-when-props-are-unchanged*/} -React normally re-renders a component whenever its parent re-renders. With `memo`, you can create a component that React will not re-render when its parent re-renders so long as its new props are the same as the old props. Such a component is said to be *memoized*. +React thường kết xuất lại một thành phần bất cứ khi nào thành phần cha của nó kết xuất lại. Với `memo`, bạn có thể tạo một thành phần mà React sẽ không kết xuất lại khi thành phần cha của nó kết xuất lại miễn là các đạo cụ mới của nó giống với các đạo cụ cũ. Một thành phần như vậy được gọi là *đã ghi nhớ*. -To memoize a component, wrap it in `memo` and use the value that it returns in place of your original component: +Để ghi nhớ một thành phần, hãy bọc nó trong `memo` và sử dụng giá trị mà nó trả về thay cho thành phần gốc của bạn: ```js const Greeting = memo(function Greeting({ name }) { - return <h1>Hello, {name}!</h1>; + return <h1>Xin chào, {name}!</h1>; }); export default Greeting; ``` -A React component should always have [pure rendering logic.](/learn/keeping-components-pure) This means that it must return the same output if its props, state, and context haven't changed. By using `memo`, you are telling React that your component complies with this requirement, so React doesn't need to re-render as long as its props haven't changed. Even with `memo`, your component will re-render if its own state changes or if a context that it's using changes. +Một thành phần React phải luôn có [logic kết xuất thuần túy.](/learn/keeping-components-pure) Điều này có nghĩa là nó phải trả về cùng một đầu ra nếu các đạo cụ, trạng thái và ngữ cảnh của nó không thay đổi. Bằng cách sử dụng `memo`, bạn đang nói với React rằng thành phần của bạn tuân thủ yêu cầu này, vì vậy React không cần phải kết xuất lại miễn là các đạo cụ của nó không thay đổi. Ngay cả với `memo`, thành phần của bạn sẽ kết xuất lại nếu trạng thái riêng của nó thay đổi hoặc nếu một ngữ cảnh mà nó đang sử dụng thay đổi. -In this example, notice that the `Greeting` component re-renders whenever `name` is changed (because that's one of its props), but not when `address` is changed (because it's not passed to `Greeting` as a prop): +Trong ví dụ này, hãy lưu ý rằng thành phần `Greeting` kết xuất lại bất cứ khi nào `name` thay đổi (vì đó là một trong các đạo cụ của nó), nhưng không phải khi `address` thay đổi (vì nó không được truyền cho `Greeting` dưới dạng một đạo cụ): <Sandpack> @@ -75,11 +75,11 @@ export default function MyApp() { return ( <> <label> - Name{': '} + Tên{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> - Address{': '} + Địa chỉ{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> @@ -88,8 +88,8 @@ export default function MyApp() { } const Greeting = memo(function Greeting({ name }) { - console.log("Greeting was rendered at", new Date().toLocaleTimeString()); - return <h3>Hello{name && ', '}{name}!</h3>; + console.log("Greeting đã được kết xuất vào lúc", new Date().toLocaleTimeString()); + return <h3>Xin chào{name && ', '}{name}!</h3>; }); ``` @@ -104,37 +104,37 @@ label { <Note> -**You should only rely on `memo` as a performance optimization.** If your code doesn't work without it, find the underlying problem and fix it first. Then you may add `memo` to improve performance. +**Bạn chỉ nên dựa vào `memo` như một tối ưu hóa hiệu suất.** Nếu mã của bạn không hoạt động nếu không có nó, hãy tìm vấn đề cơ bản và khắc phục nó trước. Sau đó, bạn có thể thêm `memo` để cải thiện hiệu suất. </Note> <DeepDive> -#### Should you add memo everywhere? {/*should-you-add-memo-everywhere*/} +#### Bạn có nên thêm memo ở mọi nơi không? {/*should-you-add-memo-everywhere*/} -If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful. +Nếu ứng dụng của bạn giống như trang web này và hầu hết các tương tác đều thô (như thay thế một trang hoặc toàn bộ một phần), thì việc ghi nhớ thường là không cần thiết. Mặt khác, nếu ứng dụng của bạn giống như một trình chỉnh sửa bản vẽ hơn và hầu hết các tương tác đều chi tiết (như di chuyển hình dạng), thì bạn có thể thấy việc ghi nhớ rất hữu ích. -Optimizing with `memo` is only valuable when your component re-renders often with the same exact props, and its re-rendering logic is expensive. If there is no perceptible lag when your component re-renders, `memo` is unnecessary. Keep in mind that `memo` is completely useless if the props passed to your component are *always different,* such as if you pass an object or a plain function defined during rendering. This is why you will often need [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) and [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) together with `memo`. +Tối ưu hóa với `memo` chỉ có giá trị khi thành phần của bạn kết xuất lại thường xuyên với cùng một đạo cụ chính xác và logic kết xuất lại của nó tốn kém. Nếu không có độ trễ nhận thấy khi thành phần của bạn kết xuất lại, thì `memo` là không cần thiết. Hãy nhớ rằng `memo` hoàn toàn vô dụng nếu các đạo cụ được truyền cho thành phần của bạn *luôn khác nhau,* chẳng hạn như nếu bạn truyền một đối tượng hoặc một hàm thuần túy được xác định trong quá trình kết xuất. Đây là lý do tại sao bạn thường cần [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) và [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) cùng với `memo`. -There is no benefit to wrapping a component in `memo` in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside of this approach is that code becomes less readable. Also, not all memoization is effective: a single value that's "always new" is enough to break memoization for an entire component. +Không có lợi ích gì khi bọc một thành phần trong `memo` trong các trường hợp khác. Cũng không có hại đáng kể nào khi làm điều đó, vì vậy một số nhóm chọn không nghĩ về các trường hợp riêng lẻ và ghi nhớ càng nhiều càng tốt. Nhược điểm của phương pháp này là mã trở nên khó đọc hơn. Ngoài ra, không phải tất cả các hoạt động ghi nhớ đều hiệu quả: một giá trị duy nhất "luôn mới" là đủ để phá vỡ hoạt động ghi nhớ cho toàn bộ một thành phần. -**In practice, you can make a lot of memoization unnecessary by following a few principles:** +**Trong thực tế, bạn có thể làm cho rất nhiều hoạt động ghi nhớ trở nên không cần thiết bằng cách tuân theo một vài nguyên tắc:** -1. When a component visually wraps other components, let it [accept JSX as children.](/learn/passing-props-to-a-component#passing-jsx-as-children) This way, when the wrapper component updates its own state, React knows that its children don't need to re-render. -1. Prefer local state and don't [lift state up](/learn/sharing-state-between-components) any further than necessary. For example, don't keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library. -1. Keep your [rendering logic pure.](/learn/keeping-components-pure) If re-rendering a component causes a problem or produces some noticeable visual artifact, it's a bug in your component! Fix the bug instead of adding memoization. -1. Avoid [unnecessary Effects that update state.](/learn/you-might-not-need-an-effect) Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over. -1. Try to [remove unnecessary dependencies from your Effects.](/learn/removing-effect-dependencies) For example, instead of memoization, it's often simpler to move some object or a function inside an Effect or outside the component. +1. Khi một thành phần bao bọc trực quan các thành phần khác, hãy để nó [chấp nhận JSX làm con.](/learn/passing-props-to-a-component#passing-jsx-as-children) Bằng cách này, khi thành phần bao bọc cập nhật trạng thái của chính nó, React biết rằng các thành phần con của nó không cần phải kết xuất lại. +2. Ưu tiên trạng thái cục bộ và không [nâng trạng thái lên](/learn/sharing-state-between-components) xa hơn mức cần thiết. Ví dụ: không giữ trạng thái tạm thời như biểu mẫu và liệu một mục có được di chuột hay không ở đầu cây của bạn hoặc trong một thư viện trạng thái toàn cầu. +3. Giữ cho [logic kết xuất của bạn thuần túy.](/learn/keeping-components-pure) Nếu việc kết xuất lại một thành phần gây ra sự cố hoặc tạo ra một số tạo tác trực quan đáng chú ý, thì đó là một lỗi trong thành phần của bạn! Hãy sửa lỗi thay vì thêm hoạt động ghi nhớ. +4. Tránh [các Effect không cần thiết cập nhật trạng thái.](/learn/you-might-not-need-an-effect) Hầu hết các vấn đề về hiệu suất trong các ứng dụng React đều do chuỗi các bản cập nhật bắt nguồn từ các Effect khiến các thành phần của bạn kết xuất đi kết xuất lại. +5. Cố gắng [xóa các phụ thuộc không cần thiết khỏi các Effect của bạn.](/learn/removing-effect-dependencies) Ví dụ: thay vì ghi nhớ, thường đơn giản hơn là di chuyển một số đối tượng hoặc một hàm bên trong một Effect hoặc bên ngoài thành phần. -If a specific interaction still feels laggy, [use the React Developer Tools profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) to see which components would benefit the most from memoization, and add memoization where needed. These principles make your components easier to debug and understand, so it's good to follow them in any case. In the long term, we're researching [doing granular memoization automatically](https://www.youtube.com/watch?v=lGEMwh32soc) to solve this once and for all. +Nếu một tương tác cụ thể vẫn cảm thấy chậm trễ, hãy [sử dụng trình hồ sơ React Developer Tools](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) để xem thành phần nào sẽ được hưởng lợi nhiều nhất từ việc ghi nhớ và thêm hoạt động ghi nhớ khi cần thiết. Các nguyên tắc này giúp các thành phần của bạn dễ gỡ lỗi và hiểu hơn, vì vậy tốt nhất là tuân theo chúng trong mọi trường hợp. Về lâu dài, chúng tôi đang nghiên cứu [tự động thực hiện hoạt động ghi nhớ chi tiết](https://www.youtube.com/watch?v=lGEMwh32soc) để giải quyết vấn đề này một lần và mãi mãi. </DeepDive> --- -### Updating a memoized component using state {/*updating-a-memoized-component-using-state*/} +### Cập nhật một thành phần đã ghi nhớ bằng cách sử dụng trạng thái {/*updating-a-memoized-component-using-state*/} -Even when a component is memoized, it will still re-render when its own state changes. Memoization only has to do with props that are passed to the component from its parent. +Ngay cả khi một thành phần được ghi nhớ, nó vẫn sẽ kết xuất lại khi trạng thái riêng của nó thay đổi. Hoạt động ghi nhớ chỉ liên quan đến các đạo cụ được truyền cho thành phần từ thành phần cha của nó. <Sandpack> @@ -147,11 +147,11 @@ export default function MyApp() { return ( <> <label> - Name{': '} + Tên{': '} <input value={name} onChange={e => setName(e.target.value)} /> </label> <label> - Address{': '} + Địa chỉ{': '} <input value={address} onChange={e => setAddress(e.target.value)} /> </label> <Greeting name={name} /> @@ -160,8 +160,8 @@ export default function MyApp() { } const Greeting = memo(function Greeting({ name }) { - console.log('Greeting was rendered at', new Date().toLocaleTimeString()); - const [greeting, setGreeting] = useState('Hello'); + console.log('Greeting đã được kết xuất vào lúc', new Date().toLocaleTimeString()); + const [greeting, setGreeting] = useState('Xin chào'); return ( <> <h3>{greeting}{name && ', '}{name}!</h3> @@ -176,18 +176,18 @@ function GreetingSelector({ value, onChange }) { <label> <input type="radio" - checked={value === 'Hello'} - onChange={e => onChange('Hello')} + checked={value === 'Xin chào'} + onChange={e => onChange('Xin chào')} /> - Regular greeting + Lời chào thông thường </label> <label> <input type="radio" - checked={value === 'Hello and welcome'} - onChange={e => onChange('Hello and welcome')} + checked={value === 'Xin chào và chào mừng'} + onChange={e => onChange('Xin chào và chào mừng')} /> - Enthusiastic greeting + Lời chào nhiệt tình </label> </> ); @@ -203,13 +203,13 @@ label { </Sandpack> -If you set a state variable to its current value, React will skip re-rendering your component even without `memo`. You may still see your component function being called an extra time, but the result will be discarded. +Nếu bạn đặt một biến trạng thái thành giá trị hiện tại của nó, React sẽ bỏ qua việc kết xuất lại thành phần của bạn ngay cả khi không có `memo`. Bạn vẫn có thể thấy hàm thành phần của bạn được gọi thêm một lần, nhưng kết quả sẽ bị loại bỏ. --- -### Updating a memoized component using a context {/*updating-a-memoized-component-using-a-context*/} +### Cập nhật một thành phần đã ghi nhớ bằng cách sử dụng một ngữ cảnh {/*updating-a-memoized-component-using-a-context*/} -Even when a component is memoized, it will still re-render when a context that it's using changes. Memoization only has to do with props that are passed to the component from its parent. +Ngay cả khi một thành phần được ghi nhớ, nó vẫn sẽ kết xuất lại khi một ngữ cảnh mà nó đang sử dụng thay đổi. Hoạt động ghi nhớ chỉ liên quan đến các đạo cụ được truyền cho thành phần từ thành phần cha của nó. <Sandpack> @@ -228,7 +228,7 @@ export default function MyApp() { return ( <ThemeContext.Provider value={theme}> <button onClick={handleClick}> - Switch theme + Chuyển đổi chủ đề </button> <Greeting name="Taylor" /> </ThemeContext.Provider> @@ -236,10 +236,10 @@ export default function MyApp() { } const Greeting = memo(function Greeting({ name }) { - console.log("Greeting was rendered at", new Date().toLocaleTimeString()); + console.log("Greeting đã được kết xuất vào lúc", new Date().toLocaleTimeString()); const theme = useContext(ThemeContext); return ( - <h3 className={theme}>Hello, {name}!</h3> + <h3 className={theme}>Xin chào, {name}!</h3> ); }); ``` @@ -263,16 +263,15 @@ label { </Sandpack> -To make your component re-render only when a _part_ of some context changes, split your component in two. Read what you need from the context in the outer component, and pass it down to a memoized child as a prop. +Để làm cho thành phần của bạn chỉ kết xuất lại khi một _phần_ của một số ngữ cảnh thay đổi, hãy chia thành phần của bạn thành hai. Đọc những gì bạn cần từ ngữ cảnh trong thành phần bên ngoài và truyền nó xuống một thành phần con đã ghi nhớ dưới dạng một đạo cụ. --- -### Minimizing props changes {/*minimizing-props-changes*/} +### Giảm thiểu các thay đổi đạo cụ {/*minimizing-props-changes*/} -When you use `memo`, your component re-renders whenever any prop is not *shallowly equal* to what it was previously. This means that React compares every prop in your component with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. Note that `Object.is(3, 3)` is `true`, but `Object.is({}, {})` is `false`. +Khi bạn sử dụng `memo`, thành phần của bạn kết xuất lại bất cứ khi nào bất kỳ đạo cụ nào không *bằng nhau một cách nông cạn* với những gì nó đã có trước đó. Điều này có nghĩa là React so sánh mọi đạo cụ trong thành phần của bạn với giá trị trước đó của nó bằng cách sử dụng so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Lưu ý rằng `Object.is(3, 3)` là `true`, nhưng `Object.is({}, {})` là `false`. - -To get the most out of `memo`, minimize the times that the props change. For example, if the prop is an object, prevent the parent component from re-creating that object every time by using [`useMemo`:](/reference/react/useMemo) +Để tận dụng tối đa `memo`, hãy giảm thiểu số lần các đạo cụ thay đổi. Ví dụ: nếu đạo cụ là một đối tượng, hãy ngăn thành phần cha tạo lại đối tượng đó mỗi lần bằng cách sử dụng [`useMemo`:](/reference/react/useMemo) ```js {5-8} function Page() { @@ -292,7 +291,7 @@ const Profile = memo(function Profile({ person }) { }); ``` -A better way to minimize props changes is to make sure the component accepts the minimum necessary information in its props. For example, it could accept individual values instead of a whole object: +Một cách tốt hơn để giảm thiểu các thay đổi đạo cụ là đảm bảo thành phần chấp nhận thông tin cần thiết tối thiểu trong các đạo cụ của nó. Ví dụ: nó có thể chấp nhận các giá trị riêng lẻ thay vì toàn bộ một đối tượng: ```js {4,7} function Page() { @@ -306,7 +305,7 @@ const Profile = memo(function Profile({ name, age }) { }); ``` -Even individual values can sometimes be projected to ones that change less frequently. For example, here a component accepts a boolean indicating the presence of a value rather than the value itself: +Ngay cả các giá trị riêng lẻ đôi khi có thể được chiếu thành các giá trị thay đổi ít thường xuyên hơn. Ví dụ: ở đây một thành phần chấp nhận một boolean cho biết sự hiện diện của một giá trị thay vì chính giá trị đó: ```js {3} function GroupsLanding({ person }) { @@ -319,13 +318,13 @@ const CallToAction = memo(function CallToAction({ hasGroups }) { }); ``` -When you need to pass a function to memoized component, either declare it outside your component so that it never changes, or [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) to cache its definition between re-renders. +Khi bạn cần truyền một hàm cho thành phần đã ghi nhớ, hãy khai báo nó bên ngoài thành phần của bạn để nó không bao giờ thay đổi hoặc [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) để lưu trữ định nghĩa của nó giữa các lần kết xuất lại. --- -### Specifying a custom comparison function {/*specifying-a-custom-comparison-function*/} +### Chỉ định một hàm so sánh tùy chỉnh {/*specifying-a-custom-comparison-function*/} -In rare cases it may be infeasible to minimize the props changes of a memoized component. In that case, you can provide a custom comparison function, which React will use to compare the old and new props instead of using shallow equality. This function is passed as a second argument to `memo`. It should return `true` only if the new props would result in the same output as the old props; otherwise it should return `false`. +Trong một số trường hợp hiếm hoi, có thể không khả thi để giảm thiểu các thay đổi đạo cụ của một thành phần đã ghi nhớ. Trong trường hợp đó, bạn có thể cung cấp một hàm so sánh tùy chỉnh, mà React sẽ sử dụng để so sánh các đạo cụ cũ và mới thay vì sử dụng sự bằng nhau nông cạn. Hàm này được truyền dưới dạng đối số thứ hai cho `memo`. Nó sẽ trả về `true` chỉ khi các đạo cụ mới sẽ dẫn đến cùng một đầu ra như các đạo cụ cũ; nếu không, nó sẽ trả về `false`. ```js {3} const Chart = memo(function Chart({ dataPoints }) { @@ -343,21 +342,21 @@ function arePropsEqual(oldProps, newProps) { } ``` -If you do this, use the Performance panel in your browser developer tools to make sure that your comparison function is actually faster than re-rendering the component. You might be surprised. +Nếu bạn làm điều này, hãy sử dụng bảng điều khiển Performance trong các công cụ dành cho nhà phát triển của trình duyệt của bạn để đảm bảo rằng hàm so sánh của bạn thực sự nhanh hơn việc kết xuất lại thành phần. Bạn có thể ngạc nhiên đấy. -When you do performance measurements, make sure that React is running in the production mode. +Khi bạn thực hiện các phép đo hiệu suất, hãy đảm bảo rằng React đang chạy ở chế độ sản xuất. <Pitfall> -If you provide a custom `arePropsEqual` implementation, **you must compare every prop, including functions.** Functions often [close over](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) the props and state of parent components. If you return `true` when `oldProps.onClick !== newProps.onClick`, your component will keep "seeing" the props and state from a previous render inside its `onClick` handler, leading to very confusing bugs. +Nếu bạn cung cấp một triển khai `arePropsEqual` tùy chỉnh, **bạn phải so sánh mọi đạo cụ, bao gồm cả các hàm.** Các hàm thường [đóng trên](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) các đạo cụ và trạng thái của các thành phần cha. Nếu bạn trả về `true` khi `oldProps.onClick !== newProps.onClick`, thành phần của bạn sẽ tiếp tục "nhìn thấy" các đạo cụ và trạng thái từ một lần kết xuất trước đó bên trong trình xử lý `onClick` của nó, dẫn đến các lỗi rất khó hiểu. -Avoid doing deep equality checks inside `arePropsEqual` unless you are 100% sure that the data structure you're working with has a known limited depth. **Deep equality checks can become incredibly slow** and can freeze your app for many seconds if someone changes the data structure later. +Tránh thực hiện các kiểm tra tính bằng nhau sâu bên trong `arePropsEqual` trừ khi bạn chắc chắn 100% rằng cấu trúc dữ liệu mà bạn đang làm việc có độ sâu giới hạn đã biết. **Các kiểm tra tính bằng nhau sâu có thể trở nên cực kỳ chậm** và có thể đóng băng ứng dụng của bạn trong nhiều giây nếu ai đó thay đổi cấu trúc dữ liệu sau này. </Pitfall> --- -## Troubleshooting {/*troubleshooting*/} -### My component re-renders when a prop is an object, array, or function {/*my-component-rerenders-when-a-prop-is-an-object-or-array*/} +## Khắc phục sự cố {/*troubleshooting*/} +### Thành phần của tôi kết xuất lại khi một đạo cụ là một đối tượng, mảng hoặc hàm {/*my-component-rerenders-when-a-prop-is-an-object-or-array*/} -React compares old and new props by shallow equality: that is, it considers whether each new prop is reference-equal to the old prop. If you create a new object or array each time the parent is re-rendered, even if the individual elements are each the same, React will still consider it to be changed. Similarly, if you create a new function when rendering the parent component, React will consider it to have changed even if the function has the same definition. To avoid this, [simplify props or memoize props in the parent component](#minimizing-props-changes). +React so sánh các đạo cụ cũ và mới bằng tính bằng nhau nông cạn: nghĩa là, nó xem xét liệu mỗi đạo cụ mới có bằng tham chiếu với đạo cụ cũ hay không. Nếu bạn tạo một đối tượng hoặc mảng mới mỗi khi thành phần cha được kết xuất lại, ngay cả khi các phần tử riêng lẻ đều giống nhau, React vẫn sẽ coi nó là đã thay đổi. Tương tự, nếu bạn tạo một hàm mới khi kết xuất thành phần cha, React sẽ coi nó là đã thay đổi ngay cả khi hàm có cùng định nghĩa. Để tránh điều này, hãy [đơn giản hóa các đạo cụ hoặc ghi nhớ các đạo cụ trong thành phần cha](#minimizing-props-changes). diff --git a/src/content/reference/react/startTransition.md b/src/content/reference/react/startTransition.md index fba28f6d1..f5f4d0ce3 100644 --- a/src/content/reference/react/startTransition.md +++ b/src/content/reference/react/startTransition.md @@ -4,7 +4,7 @@ title: startTransition <Intro> -`startTransition` lets you render a part of the UI in the background. +`startTransition` cho phép bạn hiển thị một phần của UI ở chế độ nền. ```js startTransition(action) @@ -16,11 +16,11 @@ startTransition(action) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `startTransition(action)` {/*starttransition*/} -The `startTransition` function lets you mark a state update as a Transition. +Hàm `startTransition` cho phép bạn đánh dấu một cập nhật trạng thái là một Transition. ```js {7,9} import { startTransition } from 'react'; @@ -37,39 +37,39 @@ function TabContainer() { } ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `action`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). React calls `action` immediately with no parameters and marks all state updates scheduled synchronously during the `action` function call as Transitions. Any async calls awaited in the `action` will be included in the transition, but currently require wrapping any `set` functions after the `await` in an additional `startTransition` (see [Troubleshooting](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators.](/reference/react/useTransition#preventing-unwanted-loading-indicators). +* `action`: Một hàm cập nhật một số trạng thái bằng cách gọi một hoặc nhiều hàm [`set`](/reference/react/useState#setstate). React gọi `action` ngay lập tức mà không có tham số và đánh dấu tất cả các cập nhật trạng thái được lên lịch đồng bộ trong quá trình gọi hàm `action` là Transitions. Bất kỳ lệnh gọi không đồng bộ nào được đợi trong `action` sẽ được bao gồm trong quá trình chuyển đổi, nhưng hiện tại yêu cầu gói bất kỳ hàm `set` nào sau `await` trong một `startTransition` bổ sung (xem [Khắc phục sự cố](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)). Các cập nhật trạng thái được đánh dấu là Transitions sẽ [không chặn](#marking-a-state-update-as-a-non-blocking-transition) và [sẽ không hiển thị các chỉ báo tải không mong muốn.](/reference/react/useTransition#preventing-unwanted-loading-indicators). -#### Returns {/*returns*/} +#### Kết quả trả về {/*returns*/} -`startTransition` does not return anything. +`startTransition` không trả về bất cứ điều gì. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `startTransition` does not provide a way to track whether a Transition is pending. To show a pending indicator while the Transition is ongoing, you need [`useTransition`](/reference/react/useTransition) instead. +* `startTransition` không cung cấp cách nào để theo dõi xem Transition có đang chờ xử lý hay không. Để hiển thị chỉ báo đang chờ xử lý trong khi Transition đang diễn ra, bạn cần [`useTransition`](/reference/react/useTransition) thay thế. -* You can wrap an update into a Transition only if you have access to the `set` function of that state. If you want to start a Transition in response to some prop or a custom Hook return value, try [`useDeferredValue`](/reference/react/useDeferredValue) instead. +* Bạn chỉ có thể gói một bản cập nhật vào Transition nếu bạn có quyền truy cập vào hàm `set` của trạng thái đó. Nếu bạn muốn bắt đầu Transition để đáp ứng một số đạo cụ hoặc giá trị trả về Hook tùy chỉnh, hãy thử [`useDeferredValue`](/reference/react/useDeferredValue) thay thế. -* The function you pass to `startTransition` is called immediately, marking all state updates that happen while it executes as Transitions. If you try to perform state updates in a `setTimeout`, for example, they won't be marked as Transitions. +* Hàm bạn chuyển cho `startTransition` được gọi ngay lập tức, đánh dấu tất cả các cập nhật trạng thái xảy ra trong khi nó thực thi là Transitions. Ví dụ: nếu bạn cố gắng thực hiện cập nhật trạng thái trong `setTimeout`, chúng sẽ không được đánh dấu là Transitions. -* You must wrap any state updates after any async requests in another `startTransition` to mark them as Transitions. This is a known limitation that we will fix in the future (see [Troubleshooting](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)). +* Bạn phải gói bất kỳ cập nhật trạng thái nào sau bất kỳ yêu cầu không đồng bộ nào trong một `startTransition` khác để đánh dấu chúng là Transitions. Đây là một hạn chế đã biết mà chúng tôi sẽ khắc phục trong tương lai (xem [Khắc phục sự cố](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition)). -* A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition, but then start typing into an input while the chart is in the middle of a re-render, React will restart the rendering work on the chart component after handling the input state update. +* Một cập nhật trạng thái được đánh dấu là Transition sẽ bị gián đoạn bởi các cập nhật trạng thái khác. Ví dụ: nếu bạn cập nhật một thành phần biểu đồ bên trong Transition, nhưng sau đó bắt đầu nhập vào một đầu vào trong khi biểu đồ đang trong quá trình kết xuất lại, React sẽ khởi động lại công việc kết xuất trên thành phần biểu đồ sau khi xử lý cập nhật trạng thái đầu vào. -* Transition updates can't be used to control text inputs. +* Không thể sử dụng cập nhật Transition để điều khiển đầu vào văn bản. -* If there are multiple ongoing Transitions, React currently batches them together. This is a limitation that may be removed in a future release. +* Nếu có nhiều Transitions đang diễn ra, React hiện đang gộp chúng lại với nhau. Đây là một hạn chế có thể được loại bỏ trong một bản phát hành trong tương lai. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Marking a state update as a non-blocking Transition {/*marking-a-state-update-as-a-non-blocking-transition*/} +### Đánh dấu một cập nhật trạng thái là một Transition không chặn {/*marking-a-state-update-as-a-non-blocking-transition*/} -You can mark a state update as a *Transition* by wrapping it in a `startTransition` call: +Bạn có thể đánh dấu một bản cập nhật trạng thái là *Transition* bằng cách gói nó trong một lệnh gọi `startTransition`: ```js {7,9} import { startTransition } from 'react'; @@ -86,14 +86,14 @@ function TabContainer() { } ``` -Transitions let you keep the user interface updates responsive even on slow devices. +Transitions cho phép bạn giữ cho giao diện người dùng luôn phản hồi ngay cả trên các thiết bị chậm. -With a Transition, your UI stays responsive in the middle of a re-render. For example, if the user clicks a tab but then change their mind and click another tab, they can do that without waiting for the first re-render to finish. +Với Transition, UI của bạn vẫn phản hồi trong quá trình kết xuất lại. Ví dụ: nếu người dùng nhấp vào một tab nhưng sau đó đổi ý và nhấp vào một tab khác, họ có thể thực hiện việc đó mà không cần chờ quá trình kết xuất lại đầu tiên hoàn tất. <Note> -`startTransition` is very similar to [`useTransition`](/reference/react/useTransition), except that it does not provide the `isPending` flag to track whether a Transition is ongoing. You can call `startTransition` when `useTransition` is not available. For example, `startTransition` works outside components, such as from a data library. +`startTransition` rất giống với [`useTransition`](/reference/react/useTransition), ngoại trừ việc nó không cung cấp cờ `isPending` để theo dõi xem Transition có đang diễn ra hay không. Bạn có thể gọi `startTransition` khi `useTransition` không khả dụng. Ví dụ: `startTransition` hoạt động bên ngoài các thành phần, chẳng hạn như từ một thư viện dữ liệu. -[Learn about Transitions and see examples on the `useTransition` page.](/reference/react/useTransition) +[Tìm hiểu về Transitions và xem các ví dụ trên trang `useTransition`.](/reference/react/useTransition) </Note> diff --git a/src/content/reference/react/use.md b/src/content/reference/react/use.md index 557a71cad..6b46838cb 100644 --- a/src/content/reference/react/use.md +++ b/src/content/reference/react/use.md @@ -4,7 +4,7 @@ title: use <Intro> -`use` is a React API that lets you read the value of a resource like a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or [context](/learn/passing-data-deeply-with-context). +`use` là một API của React cho phép bạn đọc giá trị của một tài nguyên như [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) hoặc [context](/learn/passing-data-deeply-with-context). ```js const value = use(resource); @@ -16,11 +16,11 @@ const value = use(resource); --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `use(resource)` {/*use*/} -Call `use` in your component to read the value of a resource like a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or [context](/learn/passing-data-deeply-with-context). +Gọi `use` trong component của bạn để đọc giá trị của một tài nguyên như [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) hoặc [context](/learn/passing-data-deeply-with-context). ```jsx import { use } from 'react'; @@ -31,33 +31,33 @@ function MessageComponent({ messagePromise }) { // ... ``` -Unlike React Hooks, `use` can be called within loops and conditional statements like `if`. Like React Hooks, the function that calls `use` must be a Component or Hook. +Không giống như React Hooks, `use` có thể được gọi bên trong các vòng lặp và các câu lệnh điều kiện như `if`. Giống như React Hooks, hàm gọi `use` phải là một Component hoặc Hook. -When called with a Promise, the `use` API integrates with [`Suspense`](/reference/react/Suspense) and [error boundaries](/reference/react/Component#catching-rendering-errors-with-an-error-boundary). The component calling `use` *suspends* while the Promise passed to `use` is pending. If the component that calls `use` is wrapped in a Suspense boundary, the fallback will be displayed. Once the Promise is resolved, the Suspense fallback is replaced by the rendered components using the data returned by the `use` API. If the Promise passed to `use` is rejected, the fallback of the nearest Error Boundary will be displayed. +Khi được gọi với một Promise, API `use` tích hợp với [`Suspense`](/reference/react/Suspense) và [error boundaries](/reference/react/Component#catching-rendering-errors-with-an-error-boundary). Component gọi `use` sẽ *tạm dừng* trong khi Promise được truyền cho `use` đang ở trạng thái pending. Nếu component gọi `use` được bao bọc trong một Suspense boundary, fallback sẽ được hiển thị. Khi Promise được resolve, Suspense fallback sẽ được thay thế bằng các component được render bằng dữ liệu trả về bởi API `use`. Nếu Promise được truyền cho `use` bị reject, fallback của Error Boundary gần nhất sẽ được hiển thị. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `resource`: this is the source of the data you want to read a value from. A resource can be a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or a [context](/learn/passing-data-deeply-with-context). +* `resource`: Đây là nguồn dữ liệu mà bạn muốn đọc giá trị. Một resource có thể là một [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) hoặc một [context](/learn/passing-data-deeply-with-context). -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -The `use` API returns the value that was read from the resource like the resolved value of a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or [context](/learn/passing-data-deeply-with-context). +API `use` trả về giá trị được đọc từ resource, chẳng hạn như giá trị đã được resolve của một [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) hoặc [context](/learn/passing-data-deeply-with-context). -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* The `use` API must be called inside a Component or a Hook. -* When fetching data in a [Server Component](/reference/rsc/server-components), prefer `async` and `await` over `use`. `async` and `await` pick up rendering from the point where `await` was invoked, whereas `use` re-renders the component after the data is resolved. -* Prefer creating Promises in [Server Components](/reference/rsc/server-components) and passing them to [Client Components](/reference/rsc/use-client) over creating Promises in Client Components. Promises created in Client Components are recreated on every render. Promises passed from a Server Component to a Client Component are stable across re-renders. [See this example](#streaming-data-from-server-to-client). +* API `use` phải được gọi bên trong một Component hoặc một Hook. +* Khi tìm nạp dữ liệu trong một [Server Component](/reference/rsc/server-components), hãy ưu tiên `async` và `await` hơn `use`. `async` và `await` tiếp tục quá trình render từ điểm mà `await` được gọi, trong khi `use` render lại component sau khi dữ liệu được resolve. +* Ưu tiên tạo Promises trong [Server Components](/reference/rsc/server-components) và truyền chúng cho [Client Components](/reference/rsc/use-client) hơn là tạo Promises trong Client Components. Promises được tạo trong Client Components sẽ được tạo lại trên mỗi lần render. Promises được truyền từ một Server Component sang một Client Component sẽ ổn định trên các lần re-render. [Xem ví dụ này](#streaming-data-from-server-to-client). --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Reading context with `use` {/*reading-context-with-use*/} +### Đọc context với `use` {/*reading-context-with-use*/} -When a [context](/learn/passing-data-deeply-with-context) is passed to `use`, it works similarly to [`useContext`](/reference/react/useContext). While `useContext` must be called at the top level of your component, `use` can be called inside conditionals like `if` and loops like `for`. `use` is preferred over `useContext` because it is more flexible. +Khi một [context](/learn/passing-data-deeply-with-context) được truyền cho `use`, nó hoạt động tương tự như [`useContext`](/reference/react/useContext). Trong khi `useContext` phải được gọi ở cấp cao nhất của component, `use` có thể được gọi bên trong các điều kiện như `if` và các vòng lặp như `for`. `use` được ưu tiên hơn `useContext` vì nó linh hoạt hơn. ```js [[2, 4, "theme"], [1, 4, "ThemeContext"]] import { use } from 'react'; @@ -67,9 +67,9 @@ function Button() { // ... ``` -`use` returns the <CodeStep step={2}>context value</CodeStep> for the <CodeStep step={1}>context</CodeStep> you passed. To determine the context value, React searches the component tree and finds **the closest context provider above** for that particular context. +`use` trả về <CodeStep step={2}>giá trị context</CodeStep> cho <CodeStep step={1}>context</CodeStep> mà bạn đã truyền. Để xác định giá trị context, React tìm kiếm trên cây component và tìm **context provider gần nhất ở phía trên** cho context cụ thể đó. -To pass context to a `Button`, wrap it or one of its parent components into the corresponding context provider. +Để truyền context cho một `Button`, hãy bọc nó hoặc một trong các component cha của nó vào context provider tương ứng. ```js [[1, 3, "ThemeContext"], [2, 3, "\\"dark\\""], [1, 5, "ThemeContext"]] function MyPage() { @@ -85,9 +85,9 @@ function Form() { } ``` -It doesn't matter how many layers of components there are between the provider and the `Button`. When a `Button` *anywhere* inside of `Form` calls `use(ThemeContext)`, it will receive `"dark"` as the value. +Không quan trọng có bao nhiêu lớp component giữa provider và `Button`. Khi một `Button` *ở bất kỳ đâu* bên trong `Form` gọi `use(ThemeContext)`, nó sẽ nhận được `dark` làm giá trị. -Unlike [`useContext`](/reference/react/useContext), <CodeStep step={2}>`use`</CodeStep> can be called in conditionals and loops like <CodeStep step={1}>`if`</CodeStep>. +Không giống như [`useContext`](/reference/react/useContext), <CodeStep step={2}>`use`</CodeStep> có thể được gọi trong các điều kiện và vòng lặp như <CodeStep step={1}>`if`</CodeStep>. ```js [[1, 2, "if"], [2, 3, "use"]] function HorizontalRule({ show }) { @@ -99,11 +99,11 @@ function HorizontalRule({ show }) { } ``` -<CodeStep step={2}>`use`</CodeStep> is called from inside a <CodeStep step={1}>`if`</CodeStep> statement, allowing you to conditionally read values from a Context. +<CodeStep step={2}>`use`</CodeStep> được gọi từ bên trong một câu lệnh <CodeStep step={1}>`if`</CodeStep>, cho phép bạn đọc có điều kiện các giá trị từ một Context. <Pitfall> -Like `useContext`, `use(context)` always looks for the closest context provider *above* the component that calls it. It searches upwards and **does not** consider context providers in the component from which you're calling `use(context)`. +Giống như `useContext`, `use(context)` luôn tìm kiếm context provider gần nhất *ở phía trên* component gọi nó. Nó tìm kiếm lên trên và **không** xem xét các context provider trong component mà bạn đang gọi `use(context)`. </Pitfall> @@ -194,9 +194,9 @@ function Button({ show, children }) { </Sandpack> -### Streaming data from the server to the client {/*streaming-data-from-server-to-client*/} +### Truyền dữ liệu từ server đến client {/*streaming-data-from-server-to-client*/} -Data can be streamed from the server to the client by passing a Promise as a prop from a <CodeStep step={1}>Server Component</CodeStep> to a <CodeStep step={2}>Client Component</CodeStep>. +Dữ liệu có thể được truyền từ server đến client bằng cách truyền một Promise như một prop từ một <CodeStep step={1}>Server Component</CodeStep> đến một <CodeStep step={2}>Client Component</CodeStep>. ```js [[1, 4, "App"], [2, 2, "Message"], [3, 7, "Suspense"], [4, 8, "messagePromise", 30], [4, 5, "messagePromise"]] import { fetchMessage } from './lib.js'; @@ -212,7 +212,7 @@ export default function App() { } ``` -The <CodeStep step={2}>Client Component</CodeStep> then takes <CodeStep step={4}>the Promise it received as a prop</CodeStep> and passes it to the <CodeStep step={5}>`use`</CodeStep> API. This allows the <CodeStep step={2}>Client Component</CodeStep> to read the value from <CodeStep step={4}>the Promise</CodeStep> that was initially created by the Server Component. +<CodeStep step={2}>Client Component</CodeStep> sau đó nhận <CodeStep step={4}>Promise mà nó nhận được như một prop</CodeStep> và truyền nó cho API <CodeStep step={5}>`use`</CodeStep>. Điều này cho phép <CodeStep step={2}>Client Component</CodeStep> đọc giá trị từ <CodeStep step={4}>Promise</CodeStep> ban đầu được tạo bởi Server Component. ```js [[2, 6, "Message"], [4, 6, "messagePromise"], [4, 7, "messagePromise"], [5, 7, "use"]] // message.js @@ -225,7 +225,7 @@ export function Message({ messagePromise }) { return <p>Here is the message: {messageContent}</p>; } ``` -Because <CodeStep step={2}>`Message`</CodeStep> is wrapped in <CodeStep step={3}>[`Suspense`](/reference/react/Suspense)</CodeStep>, the fallback will be displayed until the Promise is resolved. When the Promise is resolved, the value will be read by the <CodeStep step={5}>`use`</CodeStep> API and the <CodeStep step={2}>`Message`</CodeStep> component will replace the Suspense fallback. +Vì <CodeStep step={2}>`Message`</CodeStep> được bao bọc trong <CodeStep step={3}>[`Suspense`](/reference/react/Suspense)</CodeStep>, fallback sẽ được hiển thị cho đến khi Promise được resolve. Khi Promise được resolve, giá trị sẽ được đọc bởi API <CodeStep step={5}>`use`</CodeStep> và component <CodeStep step={2}>`Message`</CodeStep> sẽ thay thế Suspense fallback. <Sandpack> @@ -294,16 +294,16 @@ root.render( <Note> -When passing a Promise from a Server Component to a Client Component, its resolved value must be serializable to pass between server and client. Data types like functions aren't serializable and cannot be the resolved value of such a Promise. +Khi truyền một Promise từ một Server Component đến một Client Component, giá trị đã được resolve của nó phải có thể tuần tự hóa để truyền giữa server và client. Các kiểu dữ liệu như functions không thể tuần tự hóa và không thể là giá trị đã được resolve của một Promise như vậy. </Note> <DeepDive> -#### Should I resolve a Promise in a Server or Client Component? {/*resolve-promise-in-server-or-client-component*/} +#### Tôi nên resolve một Promise trong Server Component hay Client Component? {/*resolve-promise-in-server-or-client-component*/} -A Promise can be passed from a Server Component to a Client Component and resolved in the Client Component with the `use` API. You can also resolve the Promise in a Server Component with `await` and pass the required data to the Client Component as a prop. +Một Promise có thể được truyền từ một Server Component đến một Client Component và được resolve trong Client Component với API `use`. Bạn cũng có thể resolve Promise trong một Server Component với `await` và truyền dữ liệu cần thiết cho Client Component như một prop. ```js export default async function App() { @@ -312,24 +312,24 @@ export default async function App() { } ``` -But using `await` in a [Server Component](/reference/react/components#server-components) will block its rendering until the `await` statement is finished. Passing a Promise from a Server Component to a Client Component prevents the Promise from blocking the rendering of the Server Component. +Nhưng sử dụng `await` trong một [Server Component](/reference/react/components#server-components) sẽ chặn quá trình render của nó cho đến khi câu lệnh `await` kết thúc. Truyền một Promise từ một Server Component đến một Client Component ngăn Promise chặn quá trình render của Server Component. </DeepDive> -### Dealing with rejected Promises {/*dealing-with-rejected-promises*/} +### Xử lý các Promise bị reject {/*dealing-with-rejected-promises*/} -In some cases a Promise passed to `use` could be rejected. You can handle rejected Promises by either: +Trong một số trường hợp, một Promise được truyền cho `use` có thể bị reject. Bạn có thể xử lý các Promise bị reject bằng một trong hai cách: -1. [Displaying an error to users with an error boundary.](#displaying-an-error-to-users-with-error-boundary) -2. [Providing an alternative value with `Promise.catch`](#providing-an-alternative-value-with-promise-catch) +1. [Hiển thị lỗi cho người dùng bằng error boundary.](#displaying-an-error-to-users-with-error-boundary) +2. [Cung cấp một giá trị thay thế với `Promise.catch`](#providing-an-alternative-value-with-promise-catch) <Pitfall> -`use` cannot be called in a try-catch block. Instead of a try-catch block [wrap your component in an Error Boundary](#displaying-an-error-to-users-with-error-boundary), or [provide an alternative value to use with the Promise's `.catch` method](#providing-an-alternative-value-with-promise-catch). +Không thể gọi `use` trong một khối try-catch. Thay vì một khối try-catch, [hãy bọc component của bạn trong một Error Boundary](#displaying-an-error-to-users-with-error-boundary), hoặc [cung cấp một giá trị thay thế cho use với phương thức `.catch` của Promise](#providing-an-alternative-value-with-promise-catch). </Pitfall> -#### Displaying an error to users with an error boundary {/*displaying-an-error-to-users-with-error-boundary*/} +#### Hiển thị lỗi cho người dùng bằng error boundary {/*displaying-an-error-to-users-with-error-boundary*/} -If you'd like to display an error to your users when a Promise is rejected, you can use an [error boundary](/reference/react/Component#catching-rendering-errors-with-an-error-boundary). To use an error boundary, wrap the component where you are calling the `use` API in an error boundary. If the Promise passed to `use` is rejected the fallback for the error boundary will be displayed. +Nếu bạn muốn hiển thị lỗi cho người dùng khi một Promise bị reject, bạn có thể sử dụng một [error boundary](/reference/react/Component#catching-rendering-errors-with-an-error-boundary). Để sử dụng một error boundary, hãy bọc component nơi bạn đang gọi API `use` trong một error boundary. Nếu Promise được truyền cho `use` bị reject, fallback cho error boundary sẽ được hiển thị. <Sandpack> @@ -410,9 +410,9 @@ root.render( ``` </Sandpack> -#### Providing an alternative value with `Promise.catch` {/*providing-an-alternative-value-with-promise-catch*/} +#### Cung cấp một giá trị thay thế với `Promise.catch` {/*providing-an-alternative-value-with-promise-catch*/} -If you'd like to provide an alternative value when the Promise passed to `use` is rejected you can use the Promise's <CodeStep step={1}>[`catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch)</CodeStep> method. +Nếu bạn muốn cung cấp một giá trị thay thế khi Promise được truyền cho `use` bị reject, bạn có thể sử dụng phương thức <CodeStep step={1}>[`catch`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch)</CodeStep> của Promise. ```js [[1, 6, "catch"],[2, 7, "return"]] import { Message } from './message.js'; @@ -432,31 +432,31 @@ export default function App() { } ``` -To use the Promise's <CodeStep step={1}>`catch`</CodeStep> method, call <CodeStep step={1}>`catch`</CodeStep> on the Promise object. <CodeStep step={1}>`catch`</CodeStep> takes a single argument: a function that takes an error message as an argument. Whatever is <CodeStep step={2}>returned</CodeStep> by the function passed to <CodeStep step={1}>`catch`</CodeStep> will be used as the resolved value of the Promise. +Để sử dụng phương thức <CodeStep step={1}>`catch`</CodeStep> của Promise, hãy gọi <CodeStep step={1}>`catch`</CodeStep> trên đối tượng Promise. <CodeStep step={1}>`catch`</CodeStep> nhận một đối số duy nhất: một hàm nhận một thông báo lỗi làm đối số. Bất cứ điều gì được <CodeStep step={2}>trả về</CodeStep> bởi hàm được truyền cho <CodeStep step={1}>`catch`</CodeStep> sẽ được sử dụng làm giá trị đã được resolve của Promise. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} ### "Suspense Exception: This is not a real error!" {/*suspense-exception-error*/} -You are either calling `use` outside of a React Component or Hook function, or calling `use` in a try–catch block. If you are calling `use` inside a try–catch block, wrap your component in an error boundary, or call the Promise's `catch` to catch the error and resolve the Promise with another value. [See these examples](#dealing-with-rejected-promises). +Bạn đang gọi `use` bên ngoài một React Component hoặc hàm Hook, hoặc gọi `use` trong một khối try-catch. Nếu bạn đang gọi `use` bên trong một khối try-catch, hãy bọc component của bạn trong một error boundary, hoặc gọi `catch` của Promise để bắt lỗi và resolve Promise với một giá trị khác. [Xem các ví dụ này](#dealing-with-rejected-promises). -If you are calling `use` outside a React Component or Hook function, move the `use` call to a React Component or Hook function. +Nếu bạn đang gọi `use` bên ngoài một React Component hoặc hàm Hook, hãy di chuyển lệnh gọi `use` đến một React Component hoặc hàm Hook. ```jsx function MessageComponent({messagePromise}) { function download() { - // ❌ the function calling `use` is not a Component or Hook + // ❌ hàm gọi `use` không phải là một Component hoặc Hook const message = use(messagePromise); // ... ``` -Instead, call `use` outside any component closures, where the function that calls `use` is a Component or Hook. +Thay vào đó, hãy gọi `use` bên ngoài bất kỳ closure component nào, nơi hàm gọi `use` là một Component hoặc Hook. ```jsx function MessageComponent({messagePromise}) { - // ✅ `use` is being called from a component. + // ✅ `use` đang được gọi từ một component. const message = use(messagePromise); // ... ``` diff --git a/src/content/reference/react/useActionState.md b/src/content/reference/react/useActionState.md index f83f6bdc7..ba0f2b152 100644 --- a/src/content/reference/react/useActionState.md +++ b/src/content/reference/react/useActionState.md @@ -4,7 +4,7 @@ title: useActionState <Intro> -`useActionState` is a Hook that allows you to update state based on the result of a form action. +`useActionState` là một Hook cho phép bạn cập nhật trạng thái dựa trên kết quả của một hành động biểu mẫu. ```js const [state, formAction, isPending] = useActionState(fn, initialState, permalink?); @@ -14,22 +14,21 @@ const [state, formAction, isPending] = useActionState(fn, initialState, permalin <Note> -In earlier React Canary versions, this API was part of React DOM and called `useFormState`. +Trong các phiên bản React Canary trước đây, API này là một phần của React DOM và được gọi là `useFormState`. </Note> - <InlineToc /> --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useActionState(action, initialState, permalink?)` {/*useactionstate*/} {/* TODO T164397693: link to actions documentation once it exists */} -Call `useActionState` at the top level of your component to create component state that is updated [when a form action is invoked](/reference/react-dom/components/form). You pass `useActionState` an existing form action function as well as an initial state, and it returns a new action that you use in your form, along with the latest form state and whether the Action is still pending. The latest form state is also passed to the function that you provided. +Gọi `useActionState` ở cấp cao nhất của component để tạo trạng thái component được cập nhật [khi một hành động biểu mẫu được gọi](/reference/react-dom/components/form). Bạn chuyển cho `useActionState` một hàm hành động biểu mẫu hiện có cũng như một trạng thái ban đầu, và nó trả về một hành động mới mà bạn sử dụng trong biểu mẫu của mình, cùng với trạng thái biểu mẫu mới nhất và liệu Hành động có còn đang chờ xử lý hay không. Trạng thái biểu mẫu mới nhất cũng được chuyển đến hàm mà bạn đã cung cấp. ```js import { useActionState } from "react"; @@ -49,40 +48,40 @@ function StatefulForm({}) { } ``` -The form state is the value returned by the action when the form was last submitted. If the form has not yet been submitted, it is the initial state that you pass. +Trạng thái biểu mẫu là giá trị được trả về bởi hành động khi biểu mẫu được gửi lần cuối. Nếu biểu mẫu chưa được gửi, đó là trạng thái ban đầu mà bạn chuyển vào. -If used with a Server Function, `useActionState` allows the server's response from submitting the form to be shown even before hydration has completed. +Nếu được sử dụng với Server Function, `useActionState` cho phép hiển thị phản hồi của máy chủ từ việc gửi biểu mẫu ngay cả trước khi quá trình hydration hoàn tất. -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `fn`: The function to be called when the form is submitted or button pressed. When the function is called, it will receive the previous state of the form (initially the `initialState` that you pass, subsequently its previous return value) as its initial argument, followed by the arguments that a form action normally receives. -* `initialState`: The value you want the state to be initially. It can be any serializable value. This argument is ignored after the action is first invoked. -* **optional** `permalink`: A string containing the unique page URL that this form modifies. For use on pages with dynamic content (eg: feeds) in conjunction with progressive enhancement: if `fn` is a [server function](/reference/rsc/server-functions) and the form is submitted before the JavaScript bundle loads, the browser will navigate to the specified permalink URL, rather than the current page's URL. Ensure that the same form component is rendered on the destination page (including the same action `fn` and `permalink`) so that React knows how to pass the state through. Once the form has been hydrated, this parameter has no effect. +* `fn`: Hàm sẽ được gọi khi biểu mẫu được gửi hoặc nút được nhấn. Khi hàm được gọi, nó sẽ nhận trạng thái trước đó của biểu mẫu (ban đầu là `initialState` mà bạn chuyển vào, sau đó là giá trị trả về trước đó của nó) làm đối số ban đầu, tiếp theo là các đối số mà một hành động biểu mẫu thường nhận được. +* `initialState`: Giá trị bạn muốn trạng thái ban đầu là. Nó có thể là bất kỳ giá trị tuần tự hóa nào. Đối số này bị bỏ qua sau khi hành động được gọi lần đầu tiên. +* **tùy chọn** `permalink`: Một chuỗi chứa URL trang duy nhất mà biểu mẫu này sửa đổi. Để sử dụng trên các trang có nội dung động (ví dụ: nguồn cấp dữ liệu) kết hợp với cải tiến lũy tiến: nếu `fn` là một [hàm máy chủ](/reference/rsc/server-functions) và biểu mẫu được gửi trước khi tải gói JavaScript, trình duyệt sẽ điều hướng đến URL permalink được chỉ định, thay vì URL của trang hiện tại. Đảm bảo rằng cùng một component biểu mẫu được hiển thị trên trang đích (bao gồm cùng một hành động `fn` và `permalink`) để React biết cách truyền trạng thái qua. Sau khi biểu mẫu đã được hydrate, tham số này không có hiệu lực. {/* TODO T164397693: link to serializable values docs once it exists */} -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`useActionState` returns an array with the following values: +`useActionState` trả về một mảng với các giá trị sau: -1. The current state. During the first render, it will match the `initialState` you have passed. After the action is invoked, it will match the value returned by the action. -2. A new action that you can pass as the `action` prop to your `form` component or `formAction` prop to any `button` component within the form. The action can also be called manually within [`startTransition`](/reference/react/startTransition). -3. The `isPending` flag that tells you whether there is a pending Transition. +1. Trạng thái hiện tại. Trong lần hiển thị đầu tiên, nó sẽ khớp với `initialState` mà bạn đã chuyển vào. Sau khi hành động được gọi, nó sẽ khớp với giá trị được trả về bởi hành động. +2. Một hành động mới mà bạn có thể chuyển làm prop `action` cho component `form` của bạn hoặc prop `formAction` cho bất kỳ component `button` nào trong biểu mẫu. Hành động cũng có thể được gọi thủ công trong [`startTransition`](/reference/react/startTransition). +3. Cờ `isPending` cho bạn biết liệu có Transition đang chờ xử lý hay không. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* When used with a framework that supports React Server Components, `useActionState` lets you make forms interactive before JavaScript has executed on the client. When used without Server Components, it is equivalent to component local state. -* The function passed to `useActionState` receives an extra argument, the previous or initial state, as its first argument. This makes its signature different than if it were used directly as a form action without using `useActionState`. +* Khi được sử dụng với một framework hỗ trợ React Server Components, `useActionState` cho phép bạn làm cho biểu mẫu tương tác trước khi JavaScript được thực thi trên máy khách. Khi được sử dụng mà không có Server Components, nó tương đương với trạng thái cục bộ của component. +* Hàm được chuyển cho `useActionState` nhận một đối số bổ sung, trạng thái trước đó hoặc trạng thái ban đầu, làm đối số đầu tiên của nó. Điều này làm cho chữ ký của nó khác với khi nó được sử dụng trực tiếp làm một hành động biểu mẫu mà không sử dụng `useActionState`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Using information returned by a form action {/*using-information-returned-by-a-form-action*/} +### Sử dụng thông tin được trả về bởi một hành động biểu mẫu {/*using-information-returned-by-a-form-action*/} -Call `useActionState` at the top level of your component to access the return value of an action from the last time a form was submitted. +Gọi `useActionState` ở cấp cao nhất của component để truy cập giá trị trả về của một hành động từ lần cuối cùng biểu mẫu được gửi. ```js [[1, 5, "state"], [2, 5, "formAction"], [3, 5, "action"], [4, 5, "null"], [2, 8, "formAction"]] import { useActionState } from 'react'; @@ -99,15 +98,15 @@ function MyComponent() { } ``` -`useActionState` returns an array with the following items: +`useActionState` trả về một mảng với các mục sau: -1. The <CodeStep step={1}>current state</CodeStep> of the form, which is initially set to the <CodeStep step={4}>initial state</CodeStep> you provided, and after the form is submitted is set to the return value of the <CodeStep step={3}>action</CodeStep> you provided. -2. A <CodeStep step={2}>new action</CodeStep> that you pass to `<form>` as its `action` prop or call manually within `startTransition`. -3. A <CodeStep step={1}>pending state</CodeStep> that you can utilise while your action is processing. +1. <CodeStep step={1}>Trạng thái hiện tại</CodeStep> của biểu mẫu, ban đầu được đặt thành <CodeStep step={4}>trạng thái ban đầu</CodeStep> mà bạn đã cung cấp, và sau khi biểu mẫu được gửi, nó được đặt thành giá trị trả về của <CodeStep step={3}>hành động</CodeStep> mà bạn đã cung cấp. +2. Một <CodeStep step={2}>hành động mới</CodeStep> mà bạn chuyển cho `<form>` làm prop `action` của nó hoặc gọi thủ công trong `startTransition`. +3. Một <CodeStep step={1}>trạng thái đang chờ xử lý</CodeStep> mà bạn có thể sử dụng trong khi hành động của bạn đang xử lý. -When the form is submitted, the <CodeStep step={3}>action</CodeStep> function that you provided will be called. Its return value will become the new <CodeStep step={1}>current state</CodeStep> of the form. +Khi biểu mẫu được gửi, hàm <CodeStep step={3}>hành động</CodeStep> mà bạn đã cung cấp sẽ được gọi. Giá trị trả về của nó sẽ trở thành <CodeStep step={1}>trạng thái hiện tại</CodeStep> mới của biểu mẫu. -The <CodeStep step={3}>action</CodeStep> that you provide will also receive a new first argument, namely the <CodeStep step={1}>current state</CodeStep> of the form. The first time the form is submitted, this will be the <CodeStep step={4}>initial state</CodeStep> you provided, while with subsequent submissions, it will be the return value from the last time the action was called. The rest of the arguments are the same as if `useActionState` had not been used. +<CodeStep step={3}>Hành động</CodeStep> mà bạn cung cấp cũng sẽ nhận được một đối số đầu tiên mới, cụ thể là <CodeStep step={1}>trạng thái hiện tại</CodeStep> của biểu mẫu. Lần đầu tiên biểu mẫu được gửi, đây sẽ là <CodeStep step={4}>trạng thái ban đầu</CodeStep> mà bạn đã cung cấp, trong khi với các lần gửi tiếp theo, nó sẽ là giá trị trả về từ lần cuối cùng hành động được gọi. Các đối số còn lại giống như khi `useActionState` chưa được sử dụng. ```js [[3, 1, "action"], [1, 1, "currentState"]] function action(currentState, formData) { @@ -116,11 +115,11 @@ function action(currentState, formData) { } ``` -<Recipes titleText="Display information after submitting a form" titleId="display-information-after-submitting-a-form"> +<Recipes titleText="Hiển thị thông tin sau khi gửi biểu mẫu" titleId="display-information-after-submitting-a-form"> -#### Display form errors {/*display-form-errors*/} +#### Hiển thị lỗi biểu mẫu {/*display-form-errors*/} -To display messages such as an error message or toast that's returned by a Server Function, wrap the action in a call to `useActionState`. +Để hiển thị các thông báo như thông báo lỗi hoặc toast được trả về bởi một Server Function, hãy bọc hành động trong một lệnh gọi đến `useActionState`. <Sandpack> @@ -134,8 +133,8 @@ function AddToCartForm({itemID, itemTitle}) { <form action={formAction}> <h2>{itemTitle}</h2> <input type="hidden" name="itemID" value={itemID} /> - <button type="submit">Add to Cart</button> - {isPending ? "Loading..." : message} + <button type="submit">Thêm vào giỏ hàng</button> + {isPending ? "Đang tải..." : message} </form> ); } @@ -156,13 +155,13 @@ export default function App() { export async function addToCart(prevState, queryData) { const itemID = queryData.get('itemID'); if (itemID === "1") { - return "Added to cart"; + return "Đã thêm vào giỏ hàng"; } else { - // Add a fake delay to make waiting noticeable. + // Thêm một độ trễ giả để làm cho việc chờ đợi trở nên đáng chú ý. await new Promise(resolve => { setTimeout(resolve, 2000); }); - return "Couldn't add to cart: the item is sold out."; + return "Không thể thêm vào giỏ hàng: mặt hàng đã được bán hết."; } } ``` @@ -182,9 +181,9 @@ form button { <Solution /> -#### Display structured information after submitting a form {/*display-structured-information-after-submitting-a-form*/} +#### Hiển thị thông tin có cấu trúc sau khi gửi biểu mẫu {/*display-structured-information-after-submitting-a-form*/} -The return value from a Server Function can be any serializable value. For example, it could be an object that includes a boolean indicating whether the action was successful, an error message, or updated information. +Giá trị trả về từ một Server Function có thể là bất kỳ giá trị tuần tự hóa nào. Ví dụ: nó có thể là một đối tượng bao gồm một boolean cho biết liệu hành động có thành công hay không, một thông báo lỗi hoặc thông tin được cập nhật. <Sandpack> @@ -198,15 +197,15 @@ function AddToCartForm({itemID, itemTitle}) { <form action={formAction}> <h2>{itemTitle}</h2> <input type="hidden" name="itemID" value={itemID} /> - <button type="submit">Add to Cart</button> + <button type="submit">Thêm vào giỏ hàng</button> {formState?.success && <div className="toast"> - Added to cart! Your cart now has {formState.cartSize} items. + Đã thêm vào giỏ hàng! Giỏ hàng của bạn hiện có {formState.cartSize} mặt hàng. </div> } {formState?.success === false && <div className="error"> - Failed to add to cart: {formState.message} + Không thể thêm vào giỏ hàng: {formState.message} </div> } </form> @@ -236,7 +235,7 @@ export async function addToCart(prevState, queryData) { } else { return { success: false, - message: "The item is sold out.", + message: "Mặt hàng đã được bán hết.", }; } } @@ -259,11 +258,11 @@ form button { </Recipes> -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My action can no longer read the submitted form data {/*my-action-can-no-longer-read-the-submitted-form-data*/} +### Hành động của tôi không còn có thể đọc dữ liệu biểu mẫu đã gửi {/*my-action-can-no-longer-read-the-submitted-form-data*/} -When you wrap an action with `useActionState`, it gets an extra argument *as its first argument*. The submitted form data is therefore its *second* argument instead of its first as it would usually be. The new first argument that gets added is the current state of the form. +Khi bạn bọc một hành động bằng `useActionState`, nó sẽ nhận được một đối số bổ sung *làm đối số đầu tiên của nó*. Do đó, dữ liệu biểu mẫu đã gửi là đối số *thứ hai* của nó thay vì đối số đầu tiên như bình thường. Đối số đầu tiên mới được thêm vào là trạng thái hiện tại của biểu mẫu. ```js function action(currentState, formData) { diff --git a/src/content/reference/react/useCallback.md b/src/content/reference/react/useCallback.md index abcd474df..c1e2c05d3 100644 --- a/src/content/reference/react/useCallback.md +++ b/src/content/reference/react/useCallback.md @@ -4,7 +4,7 @@ title: useCallback <Intro> -`useCallback` is a React Hook that lets you cache a function definition between re-renders. +`useCallback` là một React Hook cho phép bạn lưu trữ định nghĩa hàm giữa các lần render lại. ```js const cachedFn = useCallback(fn, dependencies) @@ -16,11 +16,11 @@ const cachedFn = useCallback(fn, dependencies) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useCallback(fn, dependencies)` {/*usecallback*/} -Call `useCallback` at the top level of your component to cache a function definition between re-renders: +Gọi `useCallback` ở cấp cao nhất của component để lưu trữ định nghĩa hàm giữa các lần render lại: ```js {4,9} import { useCallback } from 'react'; @@ -34,34 +34,33 @@ export default function ProductPage({ productId, referrer, theme }) { }, [productId, referrer]); ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `fn`: The function value that you want to cache. It can take any arguments and return any values. React will return (not call!) your function back to you during the initial render. On next renders, React will give you the same function again if the `dependencies` have not changed since the last render. Otherwise, it will give you the function that you have passed during the current render, and store it in case it can be reused later. React will not call your function. The function is returned to you so you can decide when and whether to call it. +* `fn`: Giá trị hàm bạn muốn lưu trữ. Nó có thể nhận bất kỳ đối số nào và trả về bất kỳ giá trị nào. React sẽ trả về (không gọi!) hàm của bạn trong lần render ban đầu. Trong các lần render tiếp theo, React sẽ cung cấp lại cho bạn cùng một hàm nếu `dependencies` không thay đổi kể từ lần render cuối cùng. Nếu không, nó sẽ cung cấp cho bạn hàm mà bạn đã truyền trong lần render hiện tại và lưu trữ nó trong trường hợp nó có thể được sử dụng lại sau này. React sẽ không gọi hàm của bạn. Hàm được trả lại cho bạn để bạn có thể quyết định khi nào và có nên gọi nó hay không. +* `dependencies`: Danh sách tất cả các giá trị phản ứng được tham chiếu bên trong mã `fn`. Các giá trị phản ứng bao gồm props, state và tất cả các biến và hàm được khai báo trực tiếp bên trong phần thân component của bạn. Nếu trình kiểm tra lỗi của bạn được [cấu hình cho React](/learn/editor-setup#linting), nó sẽ xác minh rằng mọi giá trị phản ứng được chỉ định chính xác là một dependency. Danh sách các dependency phải có một số lượng mục không đổi và được viết nội tuyến như `[dep1, dep2, dep3]`. React sẽ so sánh từng dependency với giá trị trước đó của nó bằng cách sử dụng thuật toán so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). -* `dependencies`: The list of all reactive values referenced inside of the `fn` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison algorithm. +#### Giá trị trả về {/*returns*/} -#### Returns {/*returns*/} +Trong lần render ban đầu, `useCallback` trả về hàm `fn` mà bạn đã truyền. -On the initial render, `useCallback` returns the `fn` function you have passed. +Trong các lần render tiếp theo, nó sẽ trả về một hàm `fn` đã được lưu trữ từ lần render cuối cùng (nếu các dependency không thay đổi) hoặc trả về hàm `fn` mà bạn đã truyền trong lần render này. -During subsequent renders, it will either return an already stored `fn` function from the last render (if the dependencies haven't changed), or return the `fn` function you have passed during this render. +#### Lưu ý {/*caveats*/} -#### Caveats {/*caveats*/} - -* `useCallback` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. -* React **will not throw away the cached function unless there is a specific reason to do that.** For example, in development, React throws away the cache when you edit the file of your component. Both in development and in production, React will throw away the cache if your component suspends during the initial mount. In the future, React may add more features that take advantage of throwing away the cache--for example, if React adds built-in support for virtualized lists in the future, it would make sense to throw away the cache for items that scroll out of the virtualized table viewport. This should match your expectations if you rely on `useCallback` as a performance optimization. Otherwise, a [state variable](/reference/react/useState#im-trying-to-set-state-to-a-function-but-it-gets-called-instead) or a [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) may be more appropriate. +* `useCallback` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc Hook của riêng bạn. Bạn không thể gọi nó bên trong vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển state vào đó. +* React **sẽ không loại bỏ hàm đã lưu trữ trừ khi có một lý do cụ thể để làm điều đó.** Ví dụ: trong quá trình phát triển, React sẽ loại bỏ bộ nhớ cache khi bạn chỉnh sửa tệp của component. Cả trong quá trình phát triển và sản xuất, React sẽ loại bỏ bộ nhớ cache nếu component của bạn tạm ngưng trong quá trình mount ban đầu. Trong tương lai, React có thể thêm nhiều tính năng hơn tận dụng việc loại bỏ bộ nhớ cache--ví dụ: nếu React thêm hỗ trợ tích hợp cho danh sách ảo hóa trong tương lai, thì việc loại bỏ bộ nhớ cache cho các mục cuộn ra khỏi khung nhìn của bảng ảo hóa sẽ hợp lý. Điều này sẽ phù hợp với mong đợi của bạn nếu bạn dựa vào `useCallback` như một tối ưu hóa hiệu suất. Nếu không, một [biến state](/reference/react/useState#im-trying-to-set-state-to-a-function-but-it-gets-called-instead) hoặc một [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) có thể phù hợp hơn. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Skipping re-rendering of components {/*skipping-re-rendering-of-components*/} +### Bỏ qua việc render lại các component {/*skipping-re-rendering-of-components*/} -When you optimize rendering performance, you will sometimes need to cache the functions that you pass to child components. Let's first look at the syntax for how to do this, and then see in which cases it's useful. +Khi bạn tối ưu hóa hiệu suất render, đôi khi bạn sẽ cần lưu trữ các hàm mà bạn truyền cho các component con. Trước tiên, hãy xem cú pháp để làm điều này như thế nào, và sau đó xem trong những trường hợp nào nó hữu ích. -To cache a function between re-renders of your component, wrap its definition into the `useCallback` Hook: +Để lưu trữ một hàm giữa các lần render lại của component, hãy bọc định nghĩa của nó vào Hook `useCallback`: ```js [[3, 4, "handleSubmit"], [2, 9, "[productId, referrer]"]] import { useCallback } from 'react'; @@ -76,20 +75,20 @@ function ProductPage({ productId, referrer, theme }) { // ... ``` -You need to pass two things to `useCallback`: +Bạn cần truyền hai thứ cho `useCallback`: -1. A function definition that you want to cache between re-renders. -2. A <CodeStep step={2}>list of dependencies</CodeStep> including every value within your component that's used inside your function. +1. Một định nghĩa hàm mà bạn muốn lưu trữ giữa các lần render lại. +2. Một <CodeStep step={2}>danh sách các dependency</CodeStep> bao gồm mọi giá trị bên trong component của bạn được sử dụng bên trong hàm của bạn. -On the initial render, the <CodeStep step={3}>returned function</CodeStep> you'll get from `useCallback` will be the function you passed. +Trong lần render ban đầu, <CodeStep step={3}>hàm được trả về</CodeStep> mà bạn sẽ nhận được từ `useCallback` sẽ là hàm bạn đã truyền. -On the following renders, React will compare the <CodeStep step={2}>dependencies</CodeStep> with the dependencies you passed during the previous render. If none of the dependencies have changed (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useCallback` will return the same function as before. Otherwise, `useCallback` will return the function you passed on *this* render. +Trong các lần render tiếp theo, React sẽ so sánh <CodeStep step={2}>các dependency</CodeStep> với các dependency bạn đã truyền trong lần render trước. Nếu không có dependency nào thay đổi (so sánh với [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useCallback` sẽ trả về cùng một hàm như trước. Nếu không, `useCallback` sẽ trả về hàm bạn đã truyền trong lần render *này*. -In other words, `useCallback` caches a function between re-renders until its dependencies change. +Nói cách khác, `useCallback` lưu trữ một hàm giữa các lần render lại cho đến khi các dependency của nó thay đổi. -**Let's walk through an example to see when this is useful.** +**Hãy xem qua một ví dụ để xem khi nào điều này hữu ích.** -Say you're passing a `handleSubmit` function down from the `ProductPage` to the `ShippingForm` component: +Giả sử bạn đang truyền một hàm `handleSubmit` từ `ProductPage` xuống component `ShippingForm`: ```js {5} function ProductPage({ productId, referrer, theme }) { @@ -101,9 +100,9 @@ function ProductPage({ productId, referrer, theme }) { ); ``` -You've noticed that toggling the `theme` prop freezes the app for a moment, but if you remove `<ShippingForm />` from your JSX, it feels fast. This tells you that it's worth trying to optimize the `ShippingForm` component. +Bạn nhận thấy rằng việc chuyển đổi prop `theme` làm đóng băng ứng dụng trong một khoảnh khắc, nhưng nếu bạn xóa `<ShippingForm />` khỏi JSX của mình, nó sẽ cảm thấy nhanh. Điều này cho bạn biết rằng bạn nên thử tối ưu hóa component `ShippingForm`. -**By default, when a component re-renders, React re-renders all of its children recursively.** This is why, when `ProductPage` re-renders with a different `theme`, the `ShippingForm` component *also* re-renders. This is fine for components that don't require much calculation to re-render. But if you verified a re-render is slow, you can tell `ShippingForm` to skip re-rendering when its props are the same as on last render by wrapping it in [`memo`:](/reference/react/memo) +**Theo mặc định, khi một component render lại, React sẽ render lại tất cả các component con của nó một cách đệ quy.** Đây là lý do tại sao, khi `ProductPage` render lại với một `theme` khác, component `ShippingForm` *cũng* render lại. Điều này là tốt cho các component không yêu cầu nhiều tính toán để render lại. Nhưng nếu bạn đã xác minh rằng việc render lại chậm, bạn có thể yêu cầu `ShippingForm` bỏ qua việc render lại khi các props của nó giống như trong lần render cuối cùng bằng cách bọc nó trong [`memo`:](/reference/react/memo) ```js {3,5} import { memo } from 'react'; @@ -113,11 +112,11 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { }); ``` -**With this change, `ShippingForm` will skip re-rendering if all of its props are the *same* as on the last render.** This is when caching a function becomes important! Let's say you defined `handleSubmit` without `useCallback`: +**Với thay đổi này, `ShippingForm` sẽ bỏ qua việc render lại nếu tất cả các props của nó *giống* như trong lần render cuối cùng.** Đây là khi việc lưu trữ một hàm trở nên quan trọng! Giả sử bạn đã định nghĩa `handleSubmit` mà không có `useCallback`: ```js {2,3,8,12-13} function ProductPage({ productId, referrer, theme }) { - // Every time the theme changes, this will be a different function... + // Mỗi khi theme thay đổi, đây sẽ là một hàm khác... function handleSubmit(orderDetails) { post('/product/' + productId + '/buy', { referrer, @@ -127,47 +126,47 @@ function ProductPage({ productId, referrer, theme }) { return ( <div className={theme}> - {/* ... so ShippingForm's props will never be the same, and it will re-render every time */} + {/* ... vì vậy các props của ShippingForm sẽ không bao giờ giống nhau và nó sẽ render lại mỗi lần */} <ShippingForm onSubmit={handleSubmit} /> </div> ); } ``` -**In JavaScript, a `function () {}` or `() => {}` always creates a _different_ function,** similar to how the `{}` object literal always creates a new object. Normally, this wouldn't be a problem, but it means that `ShippingForm` props will never be the same, and your [`memo`](/reference/react/memo) optimization won't work. This is where `useCallback` comes in handy: +**Trong JavaScript, một `function () {}` hoặc `() => {}` luôn tạo ra một hàm _khác_,** tương tự như cách literal đối tượng `{}` luôn tạo ra một đối tượng mới. Thông thường, điều này sẽ không phải là một vấn đề, nhưng nó có nghĩa là các props của `ShippingForm` sẽ không bao giờ giống nhau và tối ưu hóa [`memo`](/reference/react/memo) của bạn sẽ không hoạt động. Đây là nơi `useCallback` sẽ hữu ích: ```js {2,3,8,12-13} function ProductPage({ productId, referrer, theme }) { - // Tell React to cache your function between re-renders... + // Yêu cầu React lưu trữ hàm của bạn giữa các lần render lại... const handleSubmit = useCallback((orderDetails) => { post('/product/' + productId + '/buy', { referrer, orderDetails, }); - }, [productId, referrer]); // ...so as long as these dependencies don't change... + }, [productId, referrer]); // ...miễn là các dependency này không thay đổi... return ( <div className={theme}> - {/* ...ShippingForm will receive the same props and can skip re-rendering */} + {/* ...ShippingForm sẽ nhận được các props giống nhau và có thể bỏ qua việc render lại */} <ShippingForm onSubmit={handleSubmit} /> </div> ); } ``` -**By wrapping `handleSubmit` in `useCallback`, you ensure that it's the *same* function between the re-renders** (until dependencies change). You don't *have to* wrap a function in `useCallback` unless you do it for some specific reason. In this example, the reason is that you pass it to a component wrapped in [`memo`,](/reference/react/memo) and this lets it skip re-rendering. There are other reasons you might need `useCallback` which are described further on this page. +**Bằng cách bọc `handleSubmit` trong `useCallback`, bạn đảm bảo rằng nó là hàm *giống nhau* giữa các lần render lại** (cho đến khi các dependency thay đổi). Bạn không *phải* bọc một hàm trong `useCallback` trừ khi bạn làm điều đó vì một lý do cụ thể nào đó. Trong ví dụ này, lý do là bạn truyền nó cho một component được bọc trong [`memo`,](/reference/react/memo) và điều này cho phép nó bỏ qua việc render lại. Có những lý do khác bạn có thể cần `useCallback` được mô tả thêm trên trang này. <Note> -**You should only rely on `useCallback` as a performance optimization.** If your code doesn't work without it, find the underlying problem and fix it first. Then you may add `useCallback` back. +**Bạn chỉ nên dựa vào `useCallback` như một tối ưu hóa hiệu suất.** Nếu mã của bạn không hoạt động nếu không có nó, hãy tìm vấn đề cơ bản và khắc phục nó trước. Sau đó, bạn có thể thêm lại `useCallback`. </Note> <DeepDive> -#### How is useCallback related to useMemo? {/*how-is-usecallback-related-to-usememo*/} +#### useCallback liên quan đến useMemo như thế nào? {/*how-is-usecallback-related-to-usememo*/} -You will often see [`useMemo`](/reference/react/useMemo) alongside `useCallback`. They are both useful when you're trying to optimize a child component. They let you [memoize](https://en.wikipedia.org/wiki/Memoization) (or, in other words, cache) something you're passing down: +Bạn sẽ thường thấy [`useMemo`](/reference/react/useMemo) cùng với `useCallback`. Cả hai đều hữu ích khi bạn đang cố gắng tối ưu hóa một component con. Chúng cho phép bạn [ghi nhớ](https://en.wikipedia.org/wiki/Memoization) (hay nói cách khác, lưu trữ) một cái gì đó bạn đang truyền xuống: ```js {6-8,10-15,19} import { useMemo, useCallback } from 'react'; @@ -175,11 +174,11 @@ import { useMemo, useCallback } from 'react'; function ProductPage({ productId, referrer }) { const product = useData('/product/' + productId); - const requirements = useMemo(() => { // Calls your function and caches its result + const requirements = useMemo(() => { // Gọi hàm của bạn và lưu trữ kết quả của nó return computeRequirements(product); }, [product]); - const handleSubmit = useCallback((orderDetails) => { // Caches your function itself + const handleSubmit = useCallback((orderDetails) => { // Lưu trữ chính hàm của bạn post('/product/' + productId + '/buy', { referrer, orderDetails, @@ -194,60 +193,60 @@ function ProductPage({ productId, referrer }) { } ``` -The difference is in *what* they're letting you cache: +Sự khác biệt là ở *những gì* chúng cho phép bạn lưu trữ: -* **[`useMemo`](/reference/react/useMemo) caches the *result* of calling your function.** In this example, it caches the result of calling `computeRequirements(product)` so that it doesn't change unless `product` has changed. This lets you pass the `requirements` object down without unnecessarily re-rendering `ShippingForm`. When necessary, React will call the function you've passed during rendering to calculate the result. -* **`useCallback` caches *the function itself.*** Unlike `useMemo`, it does not call the function you provide. Instead, it caches the function you provided so that `handleSubmit` *itself* doesn't change unless `productId` or `referrer` has changed. This lets you pass the `handleSubmit` function down without unnecessarily re-rendering `ShippingForm`. Your code won't run until the user submits the form. +* **[`useMemo`](/reference/react/useMemo) lưu trữ *kết quả* của việc gọi hàm của bạn.** Trong ví dụ này, nó lưu trữ kết quả của việc gọi `computeRequirements(product)` để nó không thay đổi trừ khi `product` đã thay đổi. Điều này cho phép bạn truyền đối tượng `requirements` xuống mà không cần render lại `ShippingForm` một cách không cần thiết. Khi cần thiết, React sẽ gọi hàm bạn đã truyền trong quá trình render để tính toán kết quả. +* **`useCallback` lưu trữ *chính hàm*.** Không giống như `useMemo`, nó không gọi hàm bạn cung cấp. Thay vào đó, nó lưu trữ hàm bạn đã cung cấp để bản thân `handleSubmit` không thay đổi trừ khi `productId` hoặc `referrer` đã thay đổi. Điều này cho phép bạn truyền hàm `handleSubmit` xuống mà không cần render lại `ShippingForm` một cách không cần thiết. Mã của bạn sẽ không chạy cho đến khi người dùng gửi biểu mẫu. -If you're already familiar with [`useMemo`,](/reference/react/useMemo) you might find it helpful to think of `useCallback` as this: +Nếu bạn đã quen thuộc với [`useMemo`,](/reference/react/useMemo) bạn có thể thấy hữu ích khi nghĩ về `useCallback` như sau: ```js -// Simplified implementation (inside React) +// Triển khai đơn giản hóa (bên trong React) function useCallback(fn, dependencies) { return useMemo(() => fn, dependencies); } ``` -[Read more about the difference between `useMemo` and `useCallback`.](/reference/react/useMemo#memoizing-a-function) +[Đọc thêm về sự khác biệt giữa `useMemo` và `useCallback`.](/reference/react/useMemo#memoizing-a-function) </DeepDive> <DeepDive> -#### Should you add useCallback everywhere? {/*should-you-add-usecallback-everywhere*/} +#### Bạn có nên thêm useCallback ở mọi nơi không? {/*should-you-add-usecallback-everywhere*/} -If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful. +Nếu ứng dụng của bạn giống như trang web này và hầu hết các tương tác đều thô (như thay thế một trang hoặc toàn bộ một phần), thì việc ghi nhớ thường là không cần thiết. Mặt khác, nếu ứng dụng của bạn giống một trình chỉnh sửa bản vẽ hơn và hầu hết các tương tác đều chi tiết (như di chuyển hình dạng), thì bạn có thể thấy việc ghi nhớ rất hữu ích. -Caching a function with `useCallback` is only valuable in a few cases: +Việc lưu trữ một hàm bằng `useCallback` chỉ có giá trị trong một vài trường hợp: -- You pass it as a prop to a component wrapped in [`memo`.](/reference/react/memo) You want to skip re-rendering if the value hasn't changed. Memoization lets your component re-render only if dependencies changed. -- The function you're passing is later used as a dependency of some Hook. For example, another function wrapped in `useCallback` depends on it, or you depend on this function from [`useEffect.`](/reference/react/useEffect) +* Bạn truyền nó như một prop cho một component được bọc trong [`memo`.](/reference/react/memo) Bạn muốn bỏ qua việc render lại nếu giá trị không thay đổi. Việc ghi nhớ cho phép component của bạn chỉ render lại nếu các dependency thay đổi. +* Hàm bạn đang truyền sau này được sử dụng làm dependency của một số Hook. Ví dụ: một hàm khác được bọc trong `useCallback` phụ thuộc vào nó hoặc bạn phụ thuộc vào hàm này từ [`useEffect.`](/reference/react/useEffect) -There is no benefit to wrapping a function in `useCallback` in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside is that code becomes less readable. Also, not all memoization is effective: a single value that's "always new" is enough to break memoization for an entire component. +Không có lợi ích gì khi bọc một hàm trong `useCallback` trong các trường hợp khác. Cũng không có hại đáng kể nào khi làm điều đó, vì vậy một số nhóm chọn không nghĩ về các trường hợp riêng lẻ và ghi nhớ càng nhiều càng tốt. Nhược điểm là mã trở nên khó đọc hơn. Ngoài ra, không phải tất cả các ghi nhớ đều hiệu quả: một giá trị duy nhất "luôn mới" là đủ để phá vỡ việc ghi nhớ cho toàn bộ component. -Note that `useCallback` does not prevent *creating* the function. You're always creating a function (and that's fine!), but React ignores it and gives you back a cached function if nothing changed. +Lưu ý rằng `useCallback` không ngăn chặn việc *tạo* hàm. Bạn luôn tạo một hàm (và điều đó là tốt!), nhưng React bỏ qua nó và trả lại cho bạn một hàm đã lưu trữ nếu không có gì thay đổi. -**In practice, you can make a lot of memoization unnecessary by following a few principles:** +**Trong thực tế, bạn có thể làm cho rất nhiều ghi nhớ trở nên không cần thiết bằng cách tuân theo một vài nguyên tắc:** -1. When a component visually wraps other components, let it [accept JSX as children.](/learn/passing-props-to-a-component#passing-jsx-as-children) Then, if the wrapper component updates its own state, React knows that its children don't need to re-render. -1. Prefer local state and don't [lift state up](/learn/sharing-state-between-components) any further than necessary. Don't keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library. -1. Keep your [rendering logic pure.](/learn/keeping-components-pure) If re-rendering a component causes a problem or produces some noticeable visual artifact, it's a bug in your component! Fix the bug instead of adding memoization. -1. Avoid [unnecessary Effects that update state.](/learn/you-might-not-need-an-effect) Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over. -1. Try to [remove unnecessary dependencies from your Effects.](/learn/removing-effect-dependencies) For example, instead of memoization, it's often simpler to move some object or a function inside an Effect or outside the component. +1. Khi một component bao bọc trực quan các component khác, hãy để nó [chấp nhận JSX làm children.](/learn/passing-props-to-a-component#passing-jsx-as-children) Sau đó, nếu component bao bọc cập nhật state của chính nó, React biết rằng các component con của nó không cần render lại. +2. Ưu tiên state cục bộ và không [nâng state lên](/learn/sharing-state-between-components) xa hơn mức cần thiết. Không giữ state tạm thời như biểu mẫu và việc một mục có được di chuột hay không ở đầu cây của bạn hoặc trong một thư viện state toàn cục. +3. Giữ cho [logic render của bạn thuần túy.](/learn/keeping-components-pure) Nếu việc render lại một component gây ra sự cố hoặc tạo ra một tạo tác trực quan đáng chú ý nào đó, thì đó là một lỗi trong component của bạn! Sửa lỗi thay vì thêm ghi nhớ. +4. Tránh [các Effect không cần thiết cập nhật state.](/learn/you-might-not-need-an-effect) Hầu hết các vấn đề về hiệu suất trong các ứng dụng React là do chuỗi các bản cập nhật bắt nguồn từ các Effect khiến các component của bạn render đi render lại. +5. Cố gắng [xóa các dependency không cần thiết khỏi Effect của bạn.](/learn/removing-effect-dependencies) Ví dụ: thay vì ghi nhớ, thường đơn giản hơn là di chuyển một số đối tượng hoặc một hàm bên trong một Effect hoặc bên ngoài component. -If a specific interaction still feels laggy, [use the React Developer Tools profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) to see which components benefit the most from memoization, and add memoization where needed. These principles make your components easier to debug and understand, so it's good to follow them in any case. In long term, we're researching [doing memoization automatically](https://www.youtube.com/watch?v=lGEMwh32soc) to solve this once and for all. +Nếu một tương tác cụ thể vẫn cảm thấy chậm, [hãy sử dụng trình cấu hình React Developer Tools](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) để xem những component nào được hưởng lợi nhiều nhất từ việc ghi nhớ và thêm ghi nhớ khi cần thiết. Các nguyên tắc này giúp các component của bạn dễ gỡ lỗi và hiểu hơn, vì vậy tốt nhất là tuân theo chúng trong mọi trường hợp. Về lâu dài, chúng tôi đang nghiên cứu [thực hiện ghi nhớ tự động](https://www.youtube.com/watch?v=lGEMwh32soc) để giải quyết vấn đề này một lần và mãi mãi. </DeepDive> -<Recipes titleText="The difference between useCallback and declaring a function directly" titleId="examples-rerendering"> +<Recipes titleText="Sự khác biệt giữa useCallback và khai báo trực tiếp một hàm" titleId="examples-rerendering"> -#### Skipping re-rendering with `useCallback` and `memo` {/*skipping-re-rendering-with-usecallback-and-memo*/} +#### Bỏ qua việc render lại với `useCallback` và `memo` {/*skipping-re-rendering-with-usecallback-and-memo*/} -In this example, the `ShippingForm` component is **artificially slowed down** so that you can see what happens when a React component you're rendering is genuinely slow. Try incrementing the counter and toggling the theme. +Trong ví dụ này, component `ShippingForm` bị **làm chậm một cách giả tạo** để bạn có thể thấy điều gì xảy ra khi một component React mà bạn đang render thực sự chậm. Hãy thử tăng bộ đếm và chuyển đổi chủ đề. -Incrementing the counter feels slow because it forces the slowed down `ShippingForm` to re-render. That's expected because the counter has changed, and so you need to reflect the user's new choice on the screen. +Việc tăng bộ đếm có cảm giác chậm vì nó buộc `ShippingForm` bị làm chậm phải render lại. Điều đó được mong đợi vì bộ đếm đã thay đổi và do đó bạn cần phản ánh lựa chọn mới của người dùng trên màn hình. -Next, try toggling the theme. **Thanks to `useCallback` together with [`memo`](/reference/react/memo), it’s fast despite the artificial slowdown!** `ShippingForm` skipped re-rendering because the `handleSubmit` function has not changed. The `handleSubmit` function has not changed because both `productId` and `referrer` (your `useCallback` dependencies) haven't changed since last render. +Tiếp theo, hãy thử chuyển đổi chủ đề. **Nhờ `useCallback` cùng với [`memo`](/reference/react/memo), nó nhanh chóng mặc dù bị làm chậm một cách giả tạo!** `ShippingForm` đã bỏ qua việc render lại vì hàm `handleSubmit` không thay đổi. Hàm `handleSubmit` không thay đổi vì cả `productId` và `referrer` (các dependency `useCallback` của bạn) đều không thay đổi kể từ lần render cuối cùng. <Sandpack> @@ -383,11 +382,11 @@ button[type="button"] { <Solution /> -#### Always re-rendering a component {/*always-re-rendering-a-component*/} +#### Luôn luôn render lại một component {/*always-re-rendering-a-component*/} -In this example, the `ShippingForm` implementation is also **artificially slowed down** so that you can see what happens when some React component you're rendering is genuinely slow. Try incrementing the counter and toggling the theme. +Trong ví dụ này, việc triển khai `ShippingForm` cũng bị **làm chậm một cách giả tạo** để bạn có thể thấy điều gì xảy ra khi một component React mà bạn đang render thực sự chậm. Hãy thử tăng bộ đếm và chuyển đổi chủ đề. -Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useCallback` call in this version,** so `handleSubmit` is always a new function, and the slowed down `ShippingForm` component can't skip re-rendering. +Không giống như trong ví dụ trước, việc chuyển đổi chủ đề bây giờ cũng chậm! Điều này là do **không có lệnh gọi `useCallback` trong phiên bản này,** vì vậy `handleSubmit` luôn là một hàm mới và component `ShippingForm` bị chậm không thể bỏ qua việc render lại. <Sandpack> @@ -405,7 +404,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Dark mode + Chế độ tối </label> <hr /> <ProductPage @@ -437,7 +436,7 @@ export default function ProductPage({ productId, referrer, theme }) { } function post(url, data) { - // Imagine this sends a request... + // Hãy tưởng tượng điều này gửi một yêu cầu... console.log('POST /' + url); console.log(data); } @@ -449,10 +448,10 @@ import { memo, useState } from 'react'; const ShippingForm = memo(function ShippingForm({ onSubmit }) { const [count, setCount] = useState(1); - console.log('[ARTIFICIALLY SLOW] Rendering <ShippingForm />'); + console.log('[CHẬM MỘT CÁCH GIẢ TẠO] Rendering <ShippingForm />'); let startTime = performance.now(); while (performance.now() - startTime < 500) { - // Do nothing for 500 ms to emulate extremely slow code + // Không làm gì trong 500 ms để mô phỏng mã cực kỳ chậm } function handleSubmit(e) { @@ -467,26 +466,26 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { return ( <form onSubmit={handleSubmit}> - <p><b>Note: <code>ShippingForm</code> is artificially slowed down!</b></p> + <p><b>Lưu ý: <code>ShippingForm</code> bị làm chậm một cách giả tạo!</b></p> <label> - Number of items: + Số lượng sản phẩm: <button type="button" onClick={() => setCount(count - 1)}>–</button> {count} <button type="button" onClick={() => setCount(count + 1)}>+</button> </label> <label> - Street: + Đường: <input name="street" /> </label> <label> - City: + Thành phố: <input name="city" /> </label> <label> - Postal code: + Mã bưu điện: <input name="zipCode" /> </label> - <button type="submit">Submit</button> + <button type="submit">Gửi</button> </form> ); }); @@ -521,7 +520,7 @@ button[type="button"] { </Sandpack> -However, here is the same code **with the artificial slowdown removed.** Does the lack of `useCallback` feel noticeable or not? +Tuy nhiên, đây là cùng một mã **với độ chậm nhân tạo đã được loại bỏ.** Việc thiếu `useCallback` có cảm thấy đáng chú ý hay không? <Sandpack> @@ -539,7 +538,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Dark mode + Chế độ tối </label> <hr /> <ProductPage @@ -571,7 +570,7 @@ export default function ProductPage({ productId, referrer, theme }) { } function post(url, data) { - // Imagine this sends a request... + // Hãy tưởng tượng điều này gửi một yêu cầu... console.log('POST /' + url); console.log(data); } @@ -598,24 +597,24 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { return ( <form onSubmit={handleSubmit}> <label> - Number of items: + Số lượng sản phẩm: <button type="button" onClick={() => setCount(count - 1)}>–</button> {count} <button type="button" onClick={() => setCount(count + 1)}>+</button> </label> <label> - Street: + Đường: <input name="street" /> </label> <label> - City: + Thành phố: <input name="city" /> </label> <label> - Postal code: + Mã bưu điện: <input name="zipCode" /> </label> - <button type="submit">Submit</button> + <button type="submit">Gửi</button> </form> ); }); @@ -650,9 +649,9 @@ button[type="button"] { </Sandpack> -Quite often, code without memoization works fine. If your interactions are fast enough, you don't need memoization. +Thông thường, mã không có memoization vẫn hoạt động tốt. Nếu các tương tác của bạn đủ nhanh, bạn không cần memoization. -Keep in mind that you need to run React in production mode, disable [React Developer Tools](/learn/react-developer-tools), and use devices similar to the ones your app's users have in order to get a realistic sense of what's actually slowing down your app. +Hãy nhớ rằng bạn cần chạy React ở chế độ production, tắt [React Developer Tools](/learn/react-developer-tools) và sử dụng các thiết bị tương tự như những thiết bị mà người dùng ứng dụng của bạn có để có được cảm giác thực tế về những gì thực sự làm chậm ứng dụng của bạn. <Solution /> @@ -660,11 +659,11 @@ Keep in mind that you need to run React in production mode, disable [React Devel --- -### Updating state from a memoized callback {/*updating-state-from-a-memoized-callback*/} +### Cập nhật state từ một callback đã memo {/*updating-state-from-a-memoized-callback*/} -Sometimes, you might need to update state based on previous state from a memoized callback. +Đôi khi, bạn có thể cần cập nhật state dựa trên state trước đó từ một callback đã memo. -This `handleAddTodo` function specifies `todos` as a dependency because it computes the next todos from it: +Hàm `handleAddTodo` này chỉ định `todos` làm dependency vì nó tính toán các todos tiếp theo từ nó: ```js {6,7} function TodoList() { @@ -677,7 +676,7 @@ function TodoList() { // ... ``` -You'll usually want memoized functions to have as few dependencies as possible. When you read some state only to calculate the next state, you can remove that dependency by passing an [updater function](/reference/react/useState#updating-state-based-on-the-previous-state) instead: +Bạn thường muốn các hàm đã memo có càng ít dependency càng tốt. Khi bạn chỉ đọc một số state để tính toán state tiếp theo, bạn có thể loại bỏ dependency đó bằng cách truyền một [hàm cập nhật](/reference/react/useState#updating-state-based-on-the-previous-state) thay thế: ```js {6,7} function TodoList() { @@ -686,17 +685,17 @@ function TodoList() { const handleAddTodo = useCallback((text) => { const newTodo = { id: nextId++, text }; setTodos(todos => [...todos, newTodo]); - }, []); // ✅ No need for the todos dependency + }, []); // ✅ Không cần dependency todos // ... ``` -Here, instead of making `todos` a dependency and reading it inside, you pass an instruction about *how* to update the state (`todos => [...todos, newTodo]`) to React. [Read more about updater functions.](/reference/react/useState#updating-state-based-on-the-previous-state) +Ở đây, thay vì biến `todos` thành một dependency và đọc nó bên trong, bạn truyền một hướng dẫn về *cách* cập nhật state (`todos => [...todos, newTodo]`) cho React. [Đọc thêm về các hàm cập nhật.](/reference/react/useState#updating-state-based-on-the-previous-state) --- -### Preventing an Effect from firing too often {/*preventing-an-effect-from-firing-too-often*/} +### Ngăn chặn một Effect kích hoạt quá thường xuyên {/*preventing-an-effect-from-firing-too-often*/} -Sometimes, you might want to call a function from inside an [Effect:](/learn/synchronizing-with-effects) +Đôi khi, bạn có thể muốn gọi một hàm từ bên trong một [Effect:](/learn/synchronizing-with-effects) ```js {4-9,12} function ChatRoom({ roomId }) { @@ -716,7 +715,7 @@ function ChatRoom({ roomId }) { // ... ``` -This creates a problem. [Every reactive value must be declared as a dependency of your Effect.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) However, if you declare `createOptions` as a dependency, it will cause your Effect to constantly reconnect to the chat room: +Điều này tạo ra một vấn đề. [Mọi giá trị phản ứng phải được khai báo là một dependency của Effect của bạn.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Tuy nhiên, nếu bạn khai báo `createOptions` là một dependency, nó sẽ khiến Effect của bạn liên tục kết nối lại với phòng chat: ```js {6} @@ -725,11 +724,11 @@ This creates a problem. [Every reactive value must be declared as a dependency o const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // 🔴 Problem: This dependency changes on every render + }, [createOptions]); // 🔴 Vấn đề: Dependency này thay đổi trên mỗi lần render // ... ``` -To solve this, you can wrap the function you need to call from an Effect into `useCallback`: +Để giải quyết vấn đề này, bạn có thể bọc hàm bạn cần gọi từ một Effect vào `useCallback`: ```js {4-9,16} function ChatRoom({ roomId }) { @@ -740,25 +739,25 @@ function ChatRoom({ roomId }) { serverUrl: 'https://localhost:1234', roomId: roomId }; - }, [roomId]); // ✅ Only changes when roomId changes + }, [roomId]); // ✅ Chỉ thay đổi khi roomId thay đổi useEffect(() => { const options = createOptions(); const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // ✅ Only changes when createOptions changes + }, [createOptions]); // ✅ Chỉ thay đổi khi createOptions thay đổi // ... ``` -This ensures that the `createOptions` function is the same between re-renders if the `roomId` is the same. **However, it's even better to remove the need for a function dependency.** Move your function *inside* the Effect: +Điều này đảm bảo rằng hàm `createOptions` là giống nhau giữa các lần render lại nếu `roomId` là giống nhau. **Tuy nhiên, tốt hơn nữa là loại bỏ sự cần thiết của một dependency hàm.** Di chuyển hàm của bạn *vào bên trong* Effect: ```js {5-10,16} function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { - function createOptions() { // ✅ No need for useCallback or function dependencies! + function createOptions() { // ✅ Không cần useCallback hoặc dependency hàm! return { serverUrl: 'https://localhost:1234', roomId: roomId @@ -769,17 +768,17 @@ function ChatRoom({ roomId }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ Only changes when roomId changes + }, [roomId]); // ✅ Chỉ thay đổi khi roomId thay đổi // ... ``` -Now your code is simpler and doesn't need `useCallback`. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) +Bây giờ mã của bạn đơn giản hơn và không cần `useCallback`. [Tìm hiểu thêm về cách loại bỏ dependency Effect.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) --- -### Optimizing a custom Hook {/*optimizing-a-custom-hook*/} +### Tối ưu hóa một Hook tùy chỉnh {/*optimizing-a-custom-hook*/} -If you're writing a [custom Hook,](/learn/reusing-logic-with-custom-hooks) it's recommended to wrap any functions that it returns into `useCallback`: +Nếu bạn đang viết một [Hook tùy chỉnh,](/learn/reusing-logic-with-custom-hooks) bạn nên bọc bất kỳ hàm nào mà nó trả về vào `useCallback`: ```js {4-6,8-10} function useRouter() { @@ -800,17 +799,17 @@ function useRouter() { } ``` -This ensures that the consumers of your Hook can optimize their own code when needed. +Điều này đảm bảo rằng người dùng Hook của bạn có thể tối ưu hóa mã của riêng họ khi cần. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### Every time my component renders, `useCallback` returns a different function {/*every-time-my-component-renders-usecallback-returns-a-different-function*/} +### Mỗi khi component của tôi render, `useCallback` trả về một hàm khác {/*every-time-my-component-renders-usecallback-returns-a-different-function*/} -Make sure you've specified the dependency array as a second argument! +Hãy chắc chắn rằng bạn đã chỉ định mảng dependency làm đối số thứ hai! -If you forget the dependency array, `useCallback` will return a new function every time: +Nếu bạn quên mảng dependency, `useCallback` sẽ trả về một hàm mới mỗi lần: ```js {7} function ProductPage({ productId, referrer }) { @@ -819,11 +818,11 @@ function ProductPage({ productId, referrer }) { referrer, orderDetails, }); - }); // 🔴 Returns a new function every time: no dependency array + }); // 🔴 Trả về một hàm mới mỗi lần: không có mảng dependency // ... ``` -This is the corrected version passing the dependency array as a second argument: +Đây là phiên bản đã sửa truyền mảng dependency làm đối số thứ hai: ```js {7} function ProductPage({ productId, referrer }) { @@ -832,11 +831,11 @@ function ProductPage({ productId, referrer }) { referrer, orderDetails, }); - }, [productId, referrer]); // ✅ Does not return a new function unnecessarily + }, [productId, referrer]); // ✅ Không trả về một hàm mới một cách không cần thiết // ... ``` -If this doesn't help, then the problem is that at least one of your dependencies is different from the previous render. You can debug this problem by manually logging your dependencies to the console: +Nếu điều này không giúp ích, thì vấn đề là ít nhất một trong các dependency của bạn khác với lần render trước. Bạn có thể gỡ lỗi vấn đề này bằng cách ghi thủ công các dependency của bạn vào console: ```js {5} const handleSubmit = useCallback((orderDetails) => { @@ -846,28 +845,28 @@ If this doesn't help, then the problem is that at least one of your dependencies console.log([productId, referrer]); ``` -You can then right-click on the arrays from different re-renders in the console and select "Store as a global variable" for both of them. Assuming the first one got saved as `temp1` and the second one got saved as `temp2`, you can then use the browser console to check whether each dependency in both arrays is the same: +Sau đó, bạn có thể nhấp chuột phải vào các mảng từ các lần render lại khác nhau trong console và chọn "Store as a global variable" cho cả hai. Giả sử cái đầu tiên được lưu là `temp1` và cái thứ hai được lưu là `temp2`, sau đó bạn có thể sử dụng console của trình duyệt để kiểm tra xem mỗi dependency trong cả hai mảng có giống nhau hay không: ```js -Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays? -Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays? -Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ... +Object.is(temp1[0], temp2[0]); // Dependency đầu tiên có giống nhau giữa các mảng không? +Object.is(temp1[1], temp2[1]); // Dependency thứ hai có giống nhau giữa các mảng không? +Object.is(temp1[2], temp2[2]); // ... và cứ thế cho mọi dependency ... ``` -When you find which dependency is breaking memoization, either find a way to remove it, or [memoize it as well.](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) +Khi bạn tìm thấy dependency nào đang phá vỡ memoization, hãy tìm cách loại bỏ nó hoặc [memoize nó luôn.](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) --- -### I need to call `useCallback` for each list item in a loop, but it's not allowed {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/} +### Tôi cần gọi `useCallback` cho mỗi mục danh sách trong một vòng lặp, nhưng nó không được phép {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/} -Suppose the `Chart` component is wrapped in [`memo`](/reference/react/memo). You want to skip re-rendering every `Chart` in the list when the `ReportList` component re-renders. However, you can't call `useCallback` in a loop: +Giả sử component `Chart` được bọc trong [`memo`](/reference/react/memo). Bạn muốn bỏ qua việc render lại mọi `Chart` trong danh sách khi component `ReportList` render lại. Tuy nhiên, bạn không thể gọi `useCallback` trong một vòng lặp: ```js {5-14} function ReportList({ items }) { return ( <article> {items.map(item => { - // 🔴 You can't call useCallback in a loop like this: + // 🔴 Bạn không thể gọi useCallback trong một vòng lặp như thế này: const handleClick = useCallback(() => { sendReport(item) }, [item]); @@ -883,7 +882,7 @@ function ReportList({ items }) { } ``` -Instead, extract a component for an individual item, and put `useCallback` there: +Thay vào đó, hãy trích xuất một component cho một mục riêng lẻ và đặt `useCallback` ở đó: ```js {5,12-21} function ReportList({ items }) { @@ -897,7 +896,7 @@ function ReportList({ items }) { } function Report({ item }) { - // ✅ Call useCallback at the top level: + // ✅ Gọi useCallback ở cấp cao nhất: const handleClick = useCallback(() => { sendReport(item) }, [item]); @@ -910,7 +909,7 @@ function Report({ item }) { } ``` -Alternatively, you could remove `useCallback` in the last snippet and instead wrap `Report` itself in [`memo`.](/reference/react/memo) If the `item` prop does not change, `Report` will skip re-rendering, so `Chart` will skip re-rendering too: +Ngoài ra, bạn có thể loại bỏ `useCallback` trong đoạn mã cuối cùng và thay vào đó bọc chính `Report` trong [`memo`.](/reference/react/memo) Nếu prop `item` không thay đổi, `Report` sẽ bỏ qua việc render lại, vì vậy `Chart` cũng sẽ bỏ qua việc render lại: ```js {5,6-8,15} function ReportList({ items }) { diff --git a/src/content/reference/react/useContext.md b/src/content/reference/react/useContext.md index ce06e7035..5d18949a2 100644 --- a/src/content/reference/react/useContext.md +++ b/src/content/reference/react/useContext.md @@ -1,10 +1,9 @@ --- title: useContext --- - <Intro> -`useContext` is a React Hook that lets you read and subscribe to [context](/learn/passing-data-deeply-with-context) from your component. +`useContext` là một React Hook cho phép bạn đọc và đăng ký [context](/learn/passing-data-deeply-with-context) từ component của bạn. ```js const value = useContext(SomeContext) @@ -16,11 +15,11 @@ const value = useContext(SomeContext) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useContext(SomeContext)` {/*usecontext*/} -Call `useContext` at the top level of your component to read and subscribe to [context.](/learn/passing-data-deeply-with-context) +Gọi `useContext` ở cấp cao nhất của component để đọc và đăng ký [context.](/learn/passing-data-deeply-with-context) ```js import { useContext } from 'react'; @@ -30,30 +29,29 @@ function MyComponent() { // ... ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `SomeContext`: The context that you've previously created with [`createContext`](/reference/react/createContext). The context itself does not hold the information, it only represents the kind of information you can provide or read from components. +* `SomeContext`: Context mà bạn đã tạo trước đó bằng [`createContext`](/reference/react/createContext). Bản thân context không chứa thông tin, nó chỉ đại diện cho loại thông tin bạn có thể cung cấp hoặc đọc từ các component. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`useContext` returns the context value for the calling component. It is determined as the `value` passed to the closest `SomeContext.Provider` above the calling component in the tree. If there is no such provider, then the returned value will be the `defaultValue` you have passed to [`createContext`](/reference/react/createContext) for that context. The returned value is always up-to-date. React automatically re-renders components that read some context if it changes. +`useContext` trả về giá trị context cho component gọi. Nó được xác định là `value` được truyền cho `SomeContext.Provider` gần nhất ở trên component gọi trong cây. Nếu không có provider nào như vậy, thì giá trị trả về sẽ là `defaultValue` mà bạn đã truyền cho [`createContext`](/reference/react/createContext) cho context đó. Giá trị trả về luôn được cập nhật. React tự động render lại các component đọc một số context nếu nó thay đổi. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useContext()` call in a component is not affected by providers returned from the *same* component. The corresponding `<Context.Provider>` **needs to be *above*** the component doing the `useContext()` call. -* React **automatically re-renders** all the children that use a particular context starting from the provider that receives a different `value`. The previous and the next values are compared with the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. Skipping re-renders with [`memo`](/reference/react/memo) does not prevent the children receiving fresh context values. -* If your build system produces duplicates modules in the output (which can happen with symlinks), this can break context. Passing something via context only works if `SomeContext` that you use to provide context and `SomeContext` that you use to read it are ***exactly* the same object**, as determined by a `===` comparison. +* Lệnh gọi `useContext()` trong một component không bị ảnh hưởng bởi các provider được trả về từ *cùng* component đó. `<Context.Provider>` tương ứng **cần phải ở *trên*** component thực hiện lệnh gọi `useContext()`. +* React **tự động render lại** tất cả các phần tử con sử dụng một context cụ thể bắt đầu từ provider nhận được một `value` khác. Các giá trị trước và tiếp theo được so sánh bằng phép so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Việc bỏ qua render lại bằng [`memo`](/reference/react/memo) không ngăn các phần tử con nhận các giá trị context mới. +* Nếu hệ thống build của bạn tạo ra các module trùng lặp trong đầu ra (điều này có thể xảy ra với các symlink), điều này có thể phá vỡ context. Việc truyền một thứ gì đó qua context chỉ hoạt động nếu `SomeContext` mà bạn sử dụng để cung cấp context và `SomeContext` mà bạn sử dụng để đọc nó là ***chính xác* cùng một đối tượng**, như được xác định bằng phép so sánh `===`. --- -## Usage {/*usage*/} - +## Cách sử dụng {/*usage*/} -### Passing data deeply into the tree {/*passing-data-deeply-into-the-tree*/} +### Truyền dữ liệu sâu vào cây {/*passing-data-deeply-into-the-tree*/} -Call `useContext` at the top level of your component to read and subscribe to [context.](/learn/passing-data-deeply-with-context) +Gọi `useContext` ở cấp cao nhất của component để đọc và đăng ký [context.](/learn/passing-data-deeply-with-context) ```js [[2, 4, "theme"], [1, 4, "ThemeContext"]] import { useContext } from 'react'; @@ -63,9 +61,9 @@ function Button() { // ... ``` -`useContext` returns the <CodeStep step={2}>context value</CodeStep> for the <CodeStep step={1}>context</CodeStep> you passed. To determine the context value, React searches the component tree and finds **the closest context provider above** for that particular context. +`useContext` trả về <CodeStep step={2}>giá trị context</CodeStep> cho <CodeStep step={1}>context</CodeStep> mà bạn đã truyền. Để xác định giá trị context, React tìm kiếm cây component và tìm **provider context gần nhất ở trên** cho context cụ thể đó. -To pass context to a `Button`, wrap it or one of its parent components into the corresponding context provider: +Để truyền context cho một `Button`, hãy bọc nó hoặc một trong các component cha của nó vào provider context tương ứng: ```js [[1, 3, "ThemeContext"], [2, 3, "\\"dark\\""], [1, 5, "ThemeContext"]] function MyPage() { @@ -81,11 +79,11 @@ function Form() { } ``` -It doesn't matter how many layers of components there are between the provider and the `Button`. When a `Button` *anywhere* inside of `Form` calls `useContext(ThemeContext)`, it will receive `"dark"` as the value. +Không quan trọng có bao nhiêu lớp component giữa provider và `Button`. Khi một `Button` *ở bất kỳ đâu* bên trong `Form` gọi `useContext(ThemeContext)`, nó sẽ nhận được `"dark"` làm giá trị. <Pitfall> -`useContext()` always looks for the closest provider *above* the component that calls it. It searches upwards and **does not** consider providers in the component from which you're calling `useContext()`. +`useContext()` luôn tìm kiếm provider gần nhất *ở trên* component gọi nó. Nó tìm kiếm lên trên và **không** xem xét các provider trong component mà từ đó bạn đang gọi `useContext()`. </Pitfall> @@ -175,9 +173,9 @@ function Button({ children }) { --- -### Updating data passed via context {/*updating-data-passed-via-context*/} +### Cập nhật dữ liệu được truyền qua context {/*updating-data-passed-via-context*/} -Often, you'll want the context to change over time. To update context, combine it with [state.](/reference/react/useState) Declare a state variable in the parent component, and pass the current state down as the <CodeStep step={2}>context value</CodeStep> to the provider. +Thông thường, bạn sẽ muốn context thay đổi theo thời gian. Để cập nhật context, hãy kết hợp nó với [state.](/reference/react/useState) Khai báo một biến state trong component cha và truyền state hiện tại xuống dưới dạng <CodeStep step={2}>giá trị context</CodeStep> cho provider. ```js {2} [[1, 4, "ThemeContext"], [2, 4, "theme"], [1, 11, "ThemeContext"]] function MyPage() { @@ -195,13 +193,13 @@ function MyPage() { } ``` -Now any `Button` inside of the provider will receive the current `theme` value. If you call `setTheme` to update the `theme` value that you pass to the provider, all `Button` components will re-render with the new `'light'` value. +Bây giờ bất kỳ `Button` nào bên trong provider sẽ nhận được giá trị `theme` hiện tại. Nếu bạn gọi `setTheme` để cập nhật giá trị `theme` mà bạn truyền cho provider, tất cả các component `Button` sẽ render lại với giá trị `'light'` mới. -<Recipes titleText="Examples of updating context" titleId="examples-basic"> +<Recipes titleText="Ví dụ về cập nhật context" titleId="examples-basic"> -#### Updating a value via context {/*updating-a-value-via-context*/} +#### Cập nhật một giá trị thông qua context {/*updating-a-value-via-context*/} -In this example, the `MyApp` component holds a state variable which is then passed to the `ThemeContext` provider. Checking the "Dark mode" checkbox updates the state. Changing the provided value re-renders all the components using that context. +Trong ví dụ này, component `MyApp` giữ một biến state sau đó được truyền cho provider `ThemeContext`. Việc đánh dấu vào ô "Dark mode" sẽ cập nhật state. Thay đổi giá trị được cung cấp sẽ render lại tất cả các component sử dụng context đó. <Sandpack> @@ -299,13 +297,13 @@ function Button({ children }) { </Sandpack> -Note that `value="dark"` passes the `"dark"` string, but `value={theme}` passes the value of the JavaScript `theme` variable with [JSX curly braces.](/learn/javascript-in-jsx-with-curly-braces) Curly braces also let you pass context values that aren't strings. +Lưu ý rằng `value="dark"` truyền chuỗi `"dark"`, nhưng `value={theme}` truyền giá trị của biến JavaScript `theme` với [dấu ngoặc nhọn JSX.](/learn/javascript-in-jsx-with-curly-braces) Dấu ngoặc nhọn cũng cho phép bạn truyền các giá trị context không phải là chuỗi. <Solution /> -#### Updating an object via context {/*updating-an-object-via-context*/} +#### Cập nhật một đối tượng thông qua context {/*updating-an-object-via-context*/} -In this example, there is a `currentUser` state variable which holds an object. You combine `{ currentUser, setCurrentUser }` into a single object and pass it down through the context inside the `value={}`. This lets any component below, such as `LoginButton`, read both `currentUser` and `setCurrentUser`, and then call `setCurrentUser` when needed. +Trong ví dụ này, có một biến state `currentUser` giữ một đối tượng. Bạn kết hợp `{ currentUser, setCurrentUser }` thành một đối tượng duy nhất và truyền nó xuống thông qua context bên trong `value={}`. Điều này cho phép bất kỳ component nào bên dưới, chẳng hạn như `LoginButton`, đọc cả `currentUser` và `setCurrentUser`, và sau đó gọi `setCurrentUser` khi cần. <Sandpack> @@ -395,9 +393,9 @@ label { <Solution /> -#### Multiple contexts {/*multiple-contexts*/} +#### Nhiều context {/*multiple-contexts*/} -In this example, there are two independent contexts. `ThemeContext` provides the current theme, which is a string, while `CurrentUserContext` holds the object representing the current user. +Trong ví dụ này, có hai context độc lập. `ThemeContext` cung cấp theme hiện tại, là một chuỗi, trong khi `CurrentUserContext` giữ đối tượng đại diện cho người dùng hiện tại. <Sandpack> @@ -562,9 +560,10 @@ label { <Solution /> -#### Extracting providers to a component {/*extracting-providers-to-a-component*/} +#### Trích xuất các provider vào một component {/*extracting-providers-to-a-component*/} + +Khi ứng dụng của bạn phát triển, bạn có thể sẽ có một "kim tự tháp" các context gần gốc ứng dụng của bạn hơn. Không có gì sai với điều đó. Tuy nhiên, nếu bạn không thích việc lồng nhau về mặt thẩm mỹ, bạn có thể trích xuất các provider vào một component duy nhất. Trong ví dụ này, `MyProviders` ẩn "hệ thống ống nước" và render các phần tử con được truyền cho nó bên trong các provider cần thiết. Lưu ý rằng state `theme` và `setTheme` là cần thiết trong chính `MyApp`, vì vậy `MyApp` vẫn sở hữu phần state đó. -As your app grows, it is expected that you'll have a "pyramid" of contexts closer to the root of your app. There is nothing wrong with that. However, if you dislike the nesting aesthetically, you can extract the providers into a single component. In this example, `MyProviders` hides the "plumbing" and renders the children passed to it inside the necessary providers. Note that the `theme` and `setTheme` state is needed in `MyApp` itself, so `MyApp` still owns that piece of the state. <Sandpack> @@ -736,12 +735,11 @@ label { </Sandpack> <Solution /> +#### Mở rộng với context và reducer {/*scaling-up-with-context-and-a-reducer*/} -#### Scaling up with context and a reducer {/*scaling-up-with-context-and-a-reducer*/} - -In larger apps, it is common to combine context with a [reducer](/reference/react/useReducer) to extract the logic related to some state out of components. In this example, all the "wiring" is hidden in the `TasksContext.js`, which contains a reducer and two separate contexts. +Trong các ứng dụng lớn hơn, việc kết hợp context với [reducer](/reference/react/useReducer) để tách logic liên quan đến một số trạng thái ra khỏi các component là điều phổ biến. Trong ví dụ này, tất cả các "dây" được ẩn trong `TasksContext.js`, chứa một reducer và hai context riêng biệt. -Read a [full walkthrough](/learn/scaling-up-with-reducer-and-context) of this example. +Đọc [hướng dẫn đầy đủ](/learn/scaling-up-with-reducer-and-context) về ví dụ này. <Sandpack> @@ -753,7 +751,7 @@ import { TasksProvider } from './TasksContext.js'; export default function TaskApp() { return ( <TasksProvider> - <h1>Day off in Kyoto</h1> + <h1>Ngày nghỉ ở Kyoto</h1> <AddTask /> <TaskList /> </TasksProvider> @@ -947,25 +945,25 @@ ul, li { margin: 0; padding: 0; } --- -### Specifying a fallback default value {/*specifying-a-fallback-default-value*/} +### Chỉ định giá trị mặc định dự phòng {/*specifying-a-fallback-default-value*/} -If React can't find any providers of that particular <CodeStep step={1}>context</CodeStep> in the parent tree, the context value returned by `useContext()` will be equal to the <CodeStep step={3}>default value</CodeStep> that you specified when you [created that context](/reference/react/createContext): +Nếu React không thể tìm thấy bất kỳ provider nào của <CodeStep step={1}>context</CodeStep> cụ thể đó trong cây cha, giá trị context được trả về bởi `useContext()` sẽ bằng với <CodeStep step={3}>giá trị mặc định</CodeStep> mà bạn đã chỉ định khi bạn [tạo context đó](/reference/react/createContext): ```js [[1, 1, "ThemeContext"], [3, 1, "null"]] const ThemeContext = createContext(null); ``` -The default value **never changes**. If you want to update context, use it with state as [described above.](#updating-data-passed-via-context) +Giá trị mặc định **không bao giờ thay đổi**. Nếu bạn muốn cập nhật context, hãy sử dụng nó với state như [đã mô tả ở trên.](#updating-data-passed-via-context) -Often, instead of `null`, there is some more meaningful value you can use as a default, for example: +Thông thường, thay vì `null`, có một số giá trị ý nghĩa hơn mà bạn có thể sử dụng làm mặc định, ví dụ: ```js [[1, 1, "ThemeContext"], [3, 1, "light"]] const ThemeContext = createContext('light'); ``` -This way, if you accidentally render some component without a corresponding provider, it won't break. This also helps your components work well in a test environment without setting up a lot of providers in the tests. +Bằng cách này, nếu bạn vô tình render một số component mà không có provider tương ứng, nó sẽ không bị hỏng. Điều này cũng giúp các component của bạn hoạt động tốt trong môi trường thử nghiệm mà không cần thiết lập nhiều provider trong các thử nghiệm. -In the example below, the "Toggle theme" button is always light because it's **outside any theme context provider** and the default context theme value is `'light'`. Try editing the default theme to be `'dark'`. +Trong ví dụ dưới đây, nút "Toggle theme" luôn có màu sáng vì nó **nằm ngoài bất kỳ theme context provider nào** và giá trị theme context mặc định là `'light'`. Hãy thử chỉnh sửa theme mặc định thành `'dark'`. <Sandpack> @@ -1062,9 +1060,9 @@ function Button({ children, onClick }) { --- -### Overriding context for a part of the tree {/*overriding-context-for-a-part-of-the-tree*/} +### Ghi đè context cho một phần của cây {/*overriding-context-for-a-part-of-the-tree*/} -You can override the context for a part of the tree by wrapping that part in a provider with a different value. +Bạn có thể ghi đè context cho một phần của cây bằng cách bọc phần đó trong một provider với một giá trị khác. ```js {3,5} <ThemeContext.Provider value="dark"> @@ -1076,13 +1074,13 @@ You can override the context for a part of the tree by wrapping that part in a p </ThemeContext.Provider> ``` -You can nest and override providers as many times as you need. +Bạn có thể lồng và ghi đè provider bao nhiêu lần tùy thích. -<Recipes titleText="Examples of overriding context"> +<Recipes titleText="Ví dụ về ghi đè context"> -#### Overriding a theme {/*overriding-a-theme*/} +#### Ghi đè một theme {/*overriding-a-theme*/} -Here, the button *inside* the `Footer` receives a different context value (`"light"`) than the buttons outside (`"dark"`). +Ở đây, nút *bên trong* `Footer` nhận một giá trị context khác (`"light"`) so với các nút bên ngoài (`"dark"`). <Sandpack> @@ -1186,11 +1184,11 @@ footer { <Solution /> -#### Automatically nested headings {/*automatically-nested-headings*/} +#### Tự động lồng các tiêu đề {/*automatically-nested-headings*/} -You can "accumulate" information when you nest context providers. In this example, the `Section` component keeps track of the `LevelContext` which specifies the depth of the section nesting. It reads the `LevelContext` from the parent section, and provides the `LevelContext` number increased by one to its children. As a result, the `Heading` component can automatically decide which of the `<h1>`, `<h2>`, `<h3>`, ..., tags to use based on how many `Section` components it is nested inside of. +Bạn có thể "tích lũy" thông tin khi bạn lồng các context provider. Trong ví dụ này, component `Section` theo dõi `LevelContext` chỉ định độ sâu của việc lồng các section. Nó đọc `LevelContext` từ section cha và cung cấp số `LevelContext` tăng thêm một cho các phần tử con của nó. Do đó, component `Heading` có thể tự động quyết định sử dụng thẻ `<h1>`, `<h2>`, `<h3>`, ..., nào dựa trên số lượng component `Section` mà nó được lồng bên trong. -Read a [detailed walkthrough](/learn/passing-data-deeply-with-context) of this example. +Đọc [hướng dẫn chi tiết](/learn/passing-data-deeply-with-context) về ví dụ này. <Sandpack> @@ -1288,9 +1286,9 @@ export const LevelContext = createContext(0); --- -### Optimizing re-renders when passing objects and functions {/*optimizing-re-renders-when-passing-objects-and-functions*/} +### Tối ưu hóa việc render lại khi truyền các đối tượng và hàm {/*optimizing-re-renders-when-passing-objects-and-functions*/} -You can pass any values via context, including objects and functions. +Bạn có thể truyền bất kỳ giá trị nào qua context, bao gồm cả đối tượng và hàm. ```js [[2, 10, "{ currentUser, login }"]] function MyApp() { @@ -1309,9 +1307,9 @@ function MyApp() { } ``` -Here, the <CodeStep step={2}>context value</CodeStep> is a JavaScript object with two properties, one of which is a function. Whenever `MyApp` re-renders (for example, on a route update), this will be a *different* object pointing at a *different* function, so React will also have to re-render all components deep in the tree that call `useContext(AuthContext)`. +Ở đây, <CodeStep step={2}>giá trị context</CodeStep> là một đối tượng JavaScript với hai thuộc tính, một trong số đó là một hàm. Bất cứ khi nào `MyApp` render lại (ví dụ: khi cập nhật route), đây sẽ là một đối tượng *khác* trỏ đến một hàm *khác*, vì vậy React cũng sẽ phải render lại tất cả các component sâu trong cây gọi `useContext(AuthContext)`. -In smaller apps, this is not a problem. However, there is no need to re-render them if the underlying data, like `currentUser`, has not changed. To help React take advantage of that fact, you may wrap the `login` function with [`useCallback`](/reference/react/useCallback) and wrap the object creation into [`useMemo`](/reference/react/useMemo). This is a performance optimization: +Trong các ứng dụng nhỏ hơn, đây không phải là vấn đề. Tuy nhiên, không cần thiết phải render lại chúng nếu dữ liệu cơ bản, như `currentUser`, không thay đổi. Để giúp React tận dụng lợi thế đó, bạn có thể bọc hàm `login` bằng [`useCallback`](/reference/react/useCallback) và bọc việc tạo đối tượng vào [`useMemo`](/reference/react/useMemo). Đây là một tối ưu hóa hiệu suất: ```js {6,9,11,14,17} import { useCallback, useMemo } from 'react'; @@ -1337,51 +1335,51 @@ function MyApp() { } ``` -As a result of this change, even if `MyApp` needs to re-render, the components calling `useContext(AuthContext)` won't need to re-render unless `currentUser` has changed. +Do thay đổi này, ngay cả khi `MyApp` cần render lại, các component gọi `useContext(AuthContext)` sẽ không cần render lại trừ khi `currentUser` đã thay đổi. -Read more about [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) and [`useCallback`.](/reference/react/useCallback#skipping-re-rendering-of-components) +Đọc thêm về [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) và [`useCallback`.](/reference/react/useCallback#skipping-re-rendering-of-components) --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My component doesn't see the value from my provider {/*my-component-doesnt-see-the-value-from-my-provider*/} +### Component của tôi không thấy giá trị từ provider của tôi {/*my-component-doesnt-see-the-value-from-my-provider*/} -There are a few common ways that this can happen: +Có một vài cách phổ biến mà điều này có thể xảy ra: -1. You're rendering `<SomeContext.Provider>` in the same component (or below) as where you're calling `useContext()`. Move `<SomeContext.Provider>` *above and outside* the component calling `useContext()`. -2. You may have forgotten to wrap your component with `<SomeContext.Provider>`, or you might have put it in a different part of the tree than you thought. Check whether the hierarchy is right using [React DevTools.](/learn/react-developer-tools) -3. You might be running into some build issue with your tooling that causes `SomeContext` as seen from the providing component and `SomeContext` as seen by the reading component to be two different objects. This can happen if you use symlinks, for example. You can verify this by assigning them to globals like `window.SomeContext1` and `window.SomeContext2` and then checking whether `window.SomeContext1 === window.SomeContext2` in the console. If they're not the same, fix that issue on the build tool level. +1. Bạn đang render `<SomeContext.Provider>` trong cùng một component (hoặc bên dưới) với nơi bạn đang gọi `useContext()`. Di chuyển `<SomeContext.Provider>` *lên trên và ra ngoài* component gọi `useContext()`. +2. Bạn có thể đã quên bọc component của mình bằng `<SomeContext.Provider>`, hoặc bạn có thể đã đặt nó ở một phần khác của cây so với những gì bạn nghĩ. Kiểm tra xem hệ thống phân cấp có đúng không bằng cách sử dụng [React DevTools.](/learn/react-developer-tools) +3. Bạn có thể đang gặp phải một số sự cố build với công cụ của mình khiến `SomeContext` như được thấy từ component cung cấp và `SomeContext` như được thấy bởi component đọc là hai đối tượng khác nhau. Điều này có thể xảy ra nếu bạn sử dụng symlink, chẳng hạn. Bạn có thể xác minh điều này bằng cách gán chúng cho các biến toàn cục như `window.SomeContext1` và `window.SomeContext2` và sau đó kiểm tra xem `window.SomeContext1 === window.SomeContext2` trong console hay không. Nếu chúng không giống nhau, hãy khắc phục sự cố đó ở cấp độ công cụ build. -### I am always getting `undefined` from my context although the default value is different {/*i-am-always-getting-undefined-from-my-context-although-the-default-value-is-different*/} +### Tôi luôn nhận được `undefined` từ context của mình mặc dù giá trị mặc định khác {/*i-am-always-getting-undefined-from-my-context-although-the-default-value-is-different*/} -You might have a provider without a `value` in the tree: +Bạn có thể có một provider không có `value` trong cây: ```js {1,2} -// 🚩 Doesn't work: no value prop +// 🚩 Không hoạt động: không có prop value <ThemeContext.Provider> <Button /> </ThemeContext.Provider> ``` -If you forget to specify `value`, it's like passing `value={undefined}`. +Nếu bạn quên chỉ định `value`, nó giống như truyền `value={undefined}`. -You may have also mistakingly used a different prop name by mistake: +Bạn cũng có thể đã nhầm lẫn sử dụng một tên prop khác do nhầm lẫn: ```js {1,2} -// 🚩 Doesn't work: prop should be called "value" +// 🚩 Không hoạt động: prop phải được gọi là "value" <ThemeContext.Provider theme={theme}> <Button /> </ThemeContext.Provider> ``` -In both of these cases you should see a warning from React in the console. To fix them, call the prop `value`: +Trong cả hai trường hợp này, bạn sẽ thấy cảnh báo từ React trong console. Để khắc phục chúng, hãy gọi prop là `value`: ```js {1,2} -// ✅ Passing the value prop +// ✅ Truyền prop value <ThemeContext.Provider value={theme}> <Button /> </ThemeContext.Provider> ``` -Note that the [default value from your `createContext(defaultValue)` call](#specifying-a-fallback-default-value) is only used **if there is no matching provider above at all.** If there is a `<SomeContext.Provider value={undefined}>` component somewhere in the parent tree, the component calling `useContext(SomeContext)` *will* receive `undefined` as the context value. +Lưu ý rằng [giá trị mặc định từ lệnh gọi `createContext(defaultValue)` của bạn](#specifying-a-fallback-default-value) chỉ được sử dụng **nếu hoàn toàn không có provider phù hợp nào ở trên.** Nếu có một component `<SomeContext.Provider value={undefined}>` ở đâu đó trong cây cha, component gọi `useContext(SomeContext)` *sẽ* nhận `undefined` làm giá trị context. diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index ba183d3aa..962e47cee 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -4,7 +4,7 @@ title: useEffect <Intro> -`useEffect` is a React Hook that lets you [synchronize a component with an external system.](/learn/synchronizing-with-effects) +`useEffect` là một React Hook cho phép bạn [đồng bộ hóa một component với một hệ thống bên ngoài.](/learn/synchronizing-with-effects) ```js useEffect(setup, dependencies?) @@ -16,11 +16,11 @@ useEffect(setup, dependencies?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useEffect(setup, dependencies?)` {/*useeffect*/} -Call `useEffect` at the top level of your component to declare an Effect: +Gọi `useEffect` ở cấp cao nhất của component để khai báo một Effect: ```js import { useState, useEffect } from 'react'; @@ -40,45 +40,45 @@ function ChatRoom({ roomId }) { } ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `setup`: The function with your Effect's logic. Your setup function may also optionally return a *cleanup* function. When your component is added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. After your component is removed from the DOM, React will run your cleanup function. +* `setup`: Hàm chứa logic Effect của bạn. Hàm thiết lập của bạn cũng có thể trả về một hàm *dọn dẹp* (cleanup) tùy chọn. Khi component của bạn được thêm vào DOM, React sẽ chạy hàm thiết lập của bạn. Sau mỗi lần re-render với các dependency đã thay đổi, React sẽ chạy hàm dọn dẹp (nếu bạn cung cấp) với các giá trị cũ, và sau đó chạy hàm thiết lập của bạn với các giá trị mới. Sau khi component của bạn bị xóa khỏi DOM, React sẽ chạy hàm dọn dẹp của bạn. -* **optional** `dependencies`: The list of all reactive values referenced inside of the `setup` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. If you omit this argument, your Effect will re-run after every re-render of the component. [See the difference between passing an array of dependencies, an empty array, and no dependencies at all.](#examples-dependencies) +* **tùy chọn** `dependencies`: Danh sách tất cả các giá trị reactive được tham chiếu bên trong code `setup`. Các giá trị reactive bao gồm props, state và tất cả các biến và hàm được khai báo trực tiếp bên trong phần thân component của bạn. Nếu trình lint của bạn được [cấu hình cho React](/learn/editor-setup#linting), nó sẽ xác minh rằng mọi giá trị reactive được chỉ định chính xác là một dependency. Danh sách các dependency phải có một số lượng mục không đổi và được viết nội tuyến như `[dep1, dep2, dep3]`. React sẽ so sánh từng dependency với giá trị trước đó của nó bằng cách sử dụng so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Nếu bạn bỏ qua đối số này, Effect của bạn sẽ chạy lại sau mỗi lần re-render của component. [Xem sự khác biệt giữa việc truyền một mảng dependency, một mảng trống và không có dependency nào cả.](#examples-dependencies) -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`useEffect` returns `undefined`. +`useEffect` trả về `undefined`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useEffect` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. +* `useEffect` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc Hook của riêng bạn. Bạn không thể gọi nó bên trong các vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển state vào đó. -* If you're **not trying to synchronize with some external system,** [you probably don't need an Effect.](/learn/you-might-not-need-an-effect) +* Nếu bạn **không cố gắng đồng bộ hóa với một số hệ thống bên ngoài,** [có lẽ bạn không cần một Effect.](/learn/you-might-not-need-an-effect) -* When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, [implement the cleanup function.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +* Khi Strict Mode được bật, React sẽ **chạy thêm một chu kỳ thiết lập + dọn dẹp chỉ dành cho quá trình phát triển** trước khi thiết lập thực tế đầu tiên. Đây là một bài kiểm tra áp lực để đảm bảo rằng logic dọn dẹp của bạn "phản ánh" logic thiết lập của bạn và nó dừng hoặc hoàn tác bất cứ điều gì mà thiết lập đang làm. Nếu điều này gây ra sự cố, [hãy triển khai hàm dọn dẹp.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) -* If some of your dependencies are objects or functions defined inside the component, there is a risk that they will **cause the Effect to re-run more often than needed.** To fix this, remove unnecessary [object](#removing-unnecessary-object-dependencies) and [function](#removing-unnecessary-function-dependencies) dependencies. You can also [extract state updates](#updating-state-based-on-previous-state-from-an-effect) and [non-reactive logic](#reading-the-latest-props-and-state-from-an-effect) outside of your Effect. +* Nếu một số dependency của bạn là các đối tượng hoặc hàm được xác định bên trong component, có một rủi ro là chúng sẽ **khiến Effect chạy lại thường xuyên hơn mức cần thiết.** Để khắc phục điều này, hãy loại bỏ các dependency [đối tượng](#removing-unnecessary-object-dependencies) và [hàm](#removing-unnecessary-function-dependencies) không cần thiết. Bạn cũng có thể [trích xuất các bản cập nhật state](#updating-state-based-on-previous-state-from-an-effect) và [logic không reactive](#reading-the-latest-props-and-state-from-an-effect) ra khỏi Effect của bạn. -* If your Effect wasn't caused by an interaction (like a click), React will generally let the browser **paint the updated screen first before running your Effect.** If your Effect is doing something visual (for example, positioning a tooltip), and the delay is noticeable (for example, it flickers), replace `useEffect` with [`useLayoutEffect`.](/reference/react/useLayoutEffect) +* Nếu Effect của bạn không phải do một tương tác (như một cú nhấp chuột) gây ra, React thường sẽ cho phép trình duyệt **vẽ màn hình được cập nhật trước khi chạy Effect của bạn.** Nếu Effect của bạn đang làm một cái gì đó trực quan (ví dụ: định vị một tooltip) và độ trễ là đáng chú ý (ví dụ: nó nhấp nháy), hãy thay thế `useEffect` bằng [`useLayoutEffect`.](/reference/react/useLayoutEffect) -* If your Effect is caused by an interaction (like a click), **React may run your Effect before the browser paints the updated screen**. This ensures that the result of the Effect can be observed by the event system. Usually, this works as expected. However, if you must defer the work until after paint, such as an `alert()`, you can use `setTimeout`. See [reactwg/react-18/128](https://github.com/reactwg/react-18/discussions/128) for more information. +* Nếu Effect của bạn là do một tương tác (như một cú nhấp chuột) gây ra, **React có thể chạy Effect của bạn trước khi trình duyệt vẽ màn hình được cập nhật**. Điều này đảm bảo rằng kết quả của Effect có thể được quan sát bởi hệ thống sự kiện. Thông thường, điều này hoạt động như mong đợi. Tuy nhiên, nếu bạn phải trì hoãn công việc cho đến sau khi vẽ, chẳng hạn như một `alert()`, bạn có thể sử dụng `setTimeout`. Xem [reactwg/react-18/128](https://github.com/reactwg/react-18/discussions/128) để biết thêm thông tin. -* Even if your Effect was caused by an interaction (like a click), **React may allow the browser to repaint the screen before processing the state updates inside your Effect.** Usually, this works as expected. However, if you must block the browser from repainting the screen, you need to replace `useEffect` with [`useLayoutEffect`.](/reference/react/useLayoutEffect) +* Ngay cả khi Effect của bạn là do một tương tác (như một cú nhấp chuột) gây ra, **React có thể cho phép trình duyệt vẽ lại màn hình trước khi xử lý các bản cập nhật state bên trong Effect của bạn.** Thông thường, điều này hoạt động như mong đợi. Tuy nhiên, nếu bạn phải chặn trình duyệt vẽ lại màn hình, bạn cần thay thế `useEffect` bằng [`useLayoutEffect`.](/reference/react/useLayoutEffect) -* Effects **only run on the client.** They don't run during server rendering. +* Các Effect **chỉ chạy trên client.** Chúng không chạy trong quá trình server rendering. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Connecting to an external system {/*connecting-to-an-external-system*/} +### Kết nối với một hệ thống bên ngoài {/*connecting-to-an-external-system*/} -Some components need to stay connected to the network, some browser API, or a third-party library, while they are displayed on the page. These systems aren't controlled by React, so they are called *external.* +Một số component cần duy trì kết nối với mạng, một số API của trình duyệt hoặc một thư viện của bên thứ ba, trong khi chúng được hiển thị trên trang. Các hệ thống này không được React kiểm soát, vì vậy chúng được gọi là *bên ngoài*. -To [connect your component to some external system,](/learn/synchronizing-with-effects) call `useEffect` at the top level of your component: +Để [kết nối component của bạn với một số hệ thống bên ngoài,](/learn/synchronizing-with-effects) hãy gọi `useEffect` ở cấp cao nhất của component của bạn: ```js [[1, 8, "const connection = createConnection(serverUrl, roomId);"], [1, 9, "connection.connect();"], [2, 11, "connection.disconnect();"], [3, 13, "[serverUrl, roomId]"]] import { useState, useEffect } from 'react'; @@ -88,55 +88,55 @@ function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useEffect(() => { - const connection = createConnection(serverUrl, roomId); + const connection = createConnection(serverUrl, roomId); connection.connect(); - return () => { + return () => { connection.disconnect(); - }; + }; }, [serverUrl, roomId]); // ... } ``` -You need to pass two arguments to `useEffect`: +Bạn cần truyền hai đối số cho `useEffect`: -1. A *setup function* with <CodeStep step={1}>setup code</CodeStep> that connects to that system. - - It should return a *cleanup function* with <CodeStep step={2}>cleanup code</CodeStep> that disconnects from that system. -2. A <CodeStep step={3}>list of dependencies</CodeStep> including every value from your component used inside of those functions. +1. Một *hàm thiết lập* với <CodeStep step={1}>code thiết lập</CodeStep> kết nối với hệ thống đó. + - Nó sẽ trả về một *hàm dọn dẹp* với <CodeStep step={2}>code dọn dẹp</CodeStep> ngắt kết nối khỏi hệ thống đó. +2. Một <CodeStep step={3}>danh sách các dependency</CodeStep> bao gồm mọi giá trị từ component của bạn được sử dụng bên trong các hàm đó. -**React calls your setup and cleanup functions whenever it's necessary, which may happen multiple times:** +**React gọi các hàm thiết lập và dọn dẹp của bạn bất cứ khi nào cần thiết, điều này có thể xảy ra nhiều lần:** -1. Your <CodeStep step={1}>setup code</CodeStep> runs when your component is added to the page *(mounts)*. -2. After every re-render of your component where the <CodeStep step={3}>dependencies</CodeStep> have changed: - - First, your <CodeStep step={2}>cleanup code</CodeStep> runs with the old props and state. - - Then, your <CodeStep step={1}>setup code</CodeStep> runs with the new props and state. -3. Your <CodeStep step={2}>cleanup code</CodeStep> runs one final time after your component is removed from the page *(unmounts).* +1. <CodeStep step={1}>Code thiết lập</CodeStep> của bạn chạy khi component của bạn được thêm vào trang *(mounts)*. +2. Sau mỗi lần re-render của component của bạn, nơi <CodeStep step={3}>các dependency</CodeStep> đã thay đổi: + - Đầu tiên, <CodeStep step={2}>code dọn dẹp</CodeStep> của bạn chạy với các props và state cũ. + - Sau đó, <CodeStep step={1}>code thiết lập</CodeStep> của bạn chạy với các props và state mới. +3. <CodeStep step={2}>Code dọn dẹp</CodeStep> của bạn chạy lần cuối cùng sau khi component của bạn bị xóa khỏi trang *(unmounts).* -**Let's illustrate this sequence for the example above.** +**Hãy minh họa chuỗi này cho ví dụ trên.** -When the `ChatRoom` component above gets added to the page, it will connect to the chat room with the initial `serverUrl` and `roomId`. If either `serverUrl` or `roomId` change as a result of a re-render (say, if the user picks a different chat room in a dropdown), your Effect will *disconnect from the previous room, and connect to the next one.* When the `ChatRoom` component is removed from the page, your Effect will disconnect one last time. +Khi component `ChatRoom` ở trên được thêm vào trang, nó sẽ kết nối với phòng chat với `serverUrl` và `roomId` ban đầu. Nếu `serverUrl` hoặc `roomId` thay đổi do kết quả của một re-render (ví dụ: nếu người dùng chọn một phòng chat khác trong một dropdown), Effect của bạn sẽ *ngắt kết nối khỏi phòng trước đó và kết nối với phòng tiếp theo.* Khi component `ChatRoom` bị xóa khỏi trang, Effect của bạn sẽ ngắt kết nối lần cuối cùng. -**To [help you find bugs,](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) in development React runs <CodeStep step={1}>setup</CodeStep> and <CodeStep step={2}>cleanup</CodeStep> one extra time before the <CodeStep step={1}>setup</CodeStep>.** This is a stress-test that verifies your Effect's logic is implemented correctly. If this causes visible issues, your cleanup function is missing some logic. The cleanup function should stop or undo whatever the setup function was doing. The rule of thumb is that the user shouldn't be able to distinguish between the setup being called once (as in production) and a *setup* → *cleanup* → *setup* sequence (as in development). [See common solutions.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +**Để [giúp bạn tìm lỗi,](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) trong quá trình phát triển, React chạy <CodeStep step={1}>thiết lập</CodeStep> và <CodeStep step={2}>dọn dẹp</CodeStep> thêm một lần trước <CodeStep step={1}>thiết lập</CodeStep>.** Đây là một bài kiểm tra áp lực để xác minh logic Effect của bạn được triển khai chính xác. Nếu điều này gây ra các vấn đề có thể nhìn thấy, thì hàm dọn dẹp của bạn đang thiếu một số logic. Hàm dọn dẹp sẽ dừng hoặc hoàn tác bất cứ điều gì mà hàm thiết lập đã làm. Nguyên tắc chung là người dùng không thể phân biệt giữa việc thiết lập được gọi một lần (như trong production) và một chuỗi *thiết lập* → *dọn dẹp* → *thiết lập* (như trong quá trình phát triển). [Xem các giải pháp phổ biến.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) -**Try to [write every Effect as an independent process](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) and [think about a single setup/cleanup cycle at a time.](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)** It shouldn't matter whether your component is mounting, updating, or unmounting. When your cleanup logic correctly "mirrors" the setup logic, your Effect is resilient to running setup and cleanup as often as needed. +**Cố gắng [viết mọi Effect như một quy trình độc lập](/learn/lifecycle-of-reactive-effects#each-effect-represents-a-separate-synchronization-process) và [suy nghĩ về một chu kỳ thiết lập/dọn dẹp duy nhất tại một thời điểm.](/learn/lifecycle-of-reactive-effects#thinking-from-the-effects-perspective)** Không quan trọng component của bạn đang mounting, updating hay unmounting. Khi logic dọn dẹp của bạn "phản ánh" chính xác logic thiết lập, Effect của bạn có khả năng phục hồi để chạy thiết lập và dọn dẹp thường xuyên khi cần thiết. <Note> -An Effect lets you [keep your component synchronized](/learn/synchronizing-with-effects) with some external system (like a chat service). Here, *external system* means any piece of code that's not controlled by React, such as: +Một Effect cho phép bạn [giữ cho component của bạn được đồng bộ hóa](/learn/synchronizing-with-effects) với một số hệ thống bên ngoài (như một dịch vụ chat). Ở đây, *hệ thống bên ngoài* có nghĩa là bất kỳ đoạn code nào không được React kiểm soát, chẳng hạn như: -* A timer managed with <CodeStep step={1}>[`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)</CodeStep> and <CodeStep step={2}>[`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)</CodeStep>. -* An event subscription using <CodeStep step={1}>[`window.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)</CodeStep> and <CodeStep step={2}>[`window.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)</CodeStep>. -* A third-party animation library with an API like <CodeStep step={1}>`animation.start()`</CodeStep> and <CodeStep step={2}>`animation.reset()`</CodeStep>. +* Một bộ hẹn giờ được quản lý bằng <CodeStep step={1}>[`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)</CodeStep> và <CodeStep step={2}>[`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval)</CodeStep>. +* Một đăng ký sự kiện sử dụng <CodeStep step={1}>[`window.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)</CodeStep> và <CodeStep step={2}>[`window.removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)</CodeStep>. +* Một thư viện hoạt ảnh của bên thứ ba với một API như <CodeStep step={1}>`animation.start()`</CodeStep> và <CodeStep step={2}>`animation.reset()`</CodeStep>. -**If you're not connecting to any external system, [you probably don't need an Effect.](/learn/you-might-not-need-an-effect)** +**Nếu bạn không kết nối với bất kỳ hệ thống bên ngoài nào, [có lẽ bạn không cần một Effect.](/learn/you-might-not-need-an-effect)** </Note> -<Recipes titleText="Examples of connecting to an external system" titleId="examples-connecting"> +<Recipes titleText="Ví dụ về kết nối với một hệ thống bên ngoài" titleId="examples-connecting"> -#### Connecting to a chat server {/*connecting-to-a-chat-server*/} +#### Kết nối với một máy chủ chat {/*connecting-to-a-chat-server*/} -In this example, the `ChatRoom` component uses an Effect to stay connected to an external system defined in `chat.js`. Press "Open chat" to make the `ChatRoom` component appear. This sandbox runs in development mode, so there is an extra connect-and-disconnect cycle, as [explained here.](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) Try changing the `roomId` and `serverUrl` using the dropdown and the input, and see how the Effect re-connects to the chat. Press "Close chat" to see the Effect disconnect one last time. +Trong ví dụ này, component `ChatRoom` sử dụng một Effect để duy trì kết nối với một hệ thống bên ngoài được xác định trong `chat.js`. Nhấn "Open chat" để làm cho component `ChatRoom` xuất hiện. Sandbox này chạy ở chế độ phát triển, vì vậy có một chu kỳ kết nối và ngắt kết nối bổ sung, như [đã giải thích ở đây.](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) Hãy thử thay đổi `roomId` và `serverUrl` bằng cách sử dụng dropdown và input, và xem Effect kết nối lại với chat như thế nào. Nhấn "Close chat" để xem Effect ngắt kết nối lần cuối cùng. <Sandpack> @@ -164,7 +164,7 @@ function ChatRoom({ roomId }) { onChange={e => setServerUrl(e.target.value)} /> </label> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> </> ); } @@ -175,7 +175,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -186,7 +186,7 @@ export default function App() { </select> </label> <button onClick={() => setShow(!show)}> - {show ? 'Close chat' : 'Open chat'} + {show ? 'Đóng chat' : 'Mở chat'} </button> {show && <hr />} {show && <ChatRoom roomId={roomId} />} @@ -197,13 +197,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -218,9 +218,9 @@ button { margin-left: 10px; } <Solution /> -#### Listening to a global browser event {/*listening-to-a-global-browser-event*/} +#### Lắng nghe một sự kiện trình duyệt toàn cục {/*listening-to-a-global-browser-event*/} -In this example, the external system is the browser DOM itself. Normally, you'd specify event listeners with JSX, but you can't listen to the global [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object this way. An Effect lets you connect to the `window` object and listen to its events. Listening to the `pointermove` event lets you track the cursor (or finger) position and update the red dot to move with it. +Trong ví dụ này, hệ thống bên ngoài là chính DOM của trình duyệt. Thông thường, bạn sẽ chỉ định các trình lắng nghe sự kiện bằng JSX, nhưng bạn không thể lắng nghe đối tượng [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) toàn cục theo cách này. Một Effect cho phép bạn kết nối với đối tượng `window` và lắng nghe các sự kiện của nó. Lắng nghe sự kiện `pointermove` cho phép bạn theo dõi vị trí con trỏ (hoặc ngón tay) và cập nhật dấu chấm màu đỏ để di chuyển cùng với nó. <Sandpack> @@ -267,9 +267,9 @@ body { <Solution /> -#### Triggering an animation {/*triggering-an-animation*/} +#### Kích hoạt một hoạt ảnh {/*triggering-an-animation*/} -In this example, the external system is the animation library in `animation.js`. It provides a JavaScript class called `FadeInAnimation` that takes a DOM node as an argument and exposes `start()` and `stop()` methods to control the animation. This component [uses a ref](/learn/manipulating-the-dom-with-refs) to access the underlying DOM node. The Effect reads the DOM node from the ref and automatically starts the animation for that node when the component appears. +Trong ví dụ này, hệ thống bên ngoài là thư viện hoạt ảnh trong `animation.js`. Nó cung cấp một class JavaScript có tên là `FadeInAnimation` lấy một DOM node làm đối số và hiển thị các phương thức `start()` và `stop()` để điều khiển hoạt ảnh. Component này [sử dụng một ref](/learn/manipulating-the-dom-with-refs) để truy cập DOM node cơ bản. Effect đọc DOM node từ ref và tự động bắt đầu hoạt ảnh cho node đó khi component xuất hiện. <Sandpack> @@ -300,7 +300,7 @@ function Welcome() { backgroundImage: 'radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%)' }} > - Welcome + Chào mừng </h1> ); } @@ -310,7 +310,7 @@ export default function App() { return ( <> <button onClick={() => setShow(!show)}> - {show ? 'Remove' : 'Show'} + {show ? 'Gỡ bỏ' : 'Hiển thị'} </button> <hr /> {show && <Welcome />} @@ -366,9 +366,9 @@ html, body { min-height: 300px; } <Solution /> -#### Controlling a modal dialog {/*controlling-a-modal-dialog*/} +#### Điều khiển một hộp thoại phương thức {/*controlling-a-modal-dialog*/} -In this example, the external system is the browser DOM. The `ModalDialog` component renders a [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element. It uses an Effect to synchronize the `isOpen` prop to the [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) and [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close) method calls. +Trong ví dụ này, hệ thống bên ngoài là DOM của trình duyệt. Component `ModalDialog` render một phần tử [`<dialog>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog). Nó sử dụng một Effect để đồng bộ hóa prop `isOpen` với các lệnh gọi phương thức [`showModal()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) và [`close()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/close). <Sandpack> @@ -381,14 +381,14 @@ export default function App() { return ( <> <button onClick={() => setShow(true)}> - Open dialog + Mở hộp thoại </button> <ModalDialog isOpen={show}> - Hello there! + Xin chào! <br /> <button onClick={() => { setShow(false); - }}>Close</button> + }}>Đóng</button> </ModalDialog> </> ); @@ -426,9 +426,9 @@ body { <Solution /> -#### Tracking element visibility {/*tracking-element-visibility*/} +#### Theo dõi khả năng hiển thị của phần tử {/*tracking-element-visibility*/} -In this example, the external system is again the browser DOM. The `App` component displays a long list, then a `Box` component, and then another long list. Scroll the list down. Notice that when all of the `Box` component is fully visible in the viewport, the background color changes to black. To implement this, the `Box` component uses an Effect to manage an [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). This browser API notifies you when the DOM element is visible in the viewport. +Trong ví dụ này, hệ thống bên ngoài lại là DOM của trình duyệt. Component `App` hiển thị một danh sách dài, sau đó là một component `Box` và sau đó là một danh sách dài khác. Cuộn danh sách xuống. Lưu ý rằng khi tất cả component `Box` hoàn toàn hiển thị trong khung nhìn, màu nền sẽ thay đổi thành màu đen. Để triển khai điều này, component `Box` sử dụng một Effect để quản lý một [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). API trình duyệt này thông báo cho bạn khi phần tử DOM hiển thị trong khung nhìn. <Sandpack> @@ -450,7 +450,7 @@ export default function App() { function LongSection() { const items = []; for (let i = 0; i < 50; i++) { - items.push(<li key={i}>Item #{i} (keep scrolling)</li>); + items.push(<li key={i}>Mục #{i} (tiếp tục cuộn)</li>); } return <ul>{items}</ul> } @@ -502,11 +502,11 @@ export default function Box() { --- -### Wrapping Effects in custom Hooks {/*wrapping-effects-in-custom-hooks*/} +### Gói các Effect trong các Hook tùy chỉnh {/*wrapping-effects-in-custom-hooks*/} -Effects are an ["escape hatch":](/learn/escape-hatches) you use them when you need to "step outside React" and when there is no better built-in solution for your use case. If you find yourself often needing to manually write Effects, it's usually a sign that you need to extract some [custom Hooks](/learn/reusing-logic-with-custom-hooks) for common behaviors your components rely on. +Các Effect là một ["lối thoát hiểm":](/learn/escape-hatches) bạn sử dụng chúng khi bạn cần "bước ra ngoài React" và khi không có giải pháp tích hợp tốt hơn cho trường hợp sử dụng của bạn. Nếu bạn thấy mình thường xuyên cần viết Effect theo cách thủ công, thì đó thường là một dấu hiệu cho thấy bạn cần trích xuất một số [Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks) cho các hành vi phổ biến mà các component của bạn dựa vào. -For example, this `useChatRoom` custom Hook "hides" the logic of your Effect behind a more declarative API: +Ví dụ: Hook tùy chỉnh `useChatRoom` này "ẩn" logic của Effect của bạn đằng sau một API khai báo hơn: ```js {1,11} function useChatRoom({ serverUrl, roomId }) { @@ -522,7 +522,7 @@ function useChatRoom({ serverUrl, roomId }) { } ``` -Then you can use it from any component like this: +Sau đó, bạn có thể sử dụng nó từ bất kỳ component nào như thế này: ```js {4-7} function ChatRoom({ roomId }) { @@ -535,15 +535,15 @@ function ChatRoom({ roomId }) { // ... ``` -There are also many excellent custom Hooks for every purpose available in the React ecosystem. +Ngoài ra còn có nhiều Hook tùy chỉnh tuyệt vời cho mọi mục đích có sẵn trong hệ sinh thái React. -[Learn more about wrapping Effects in custom Hooks.](/learn/reusing-logic-with-custom-hooks) +[Tìm hiểu thêm về việc gói các Effect trong các Hook tùy chỉnh.](/learn/reusing-logic-with-custom-hooks) -<Recipes titleText="Examples of wrapping Effects in custom Hooks" titleId="examples-custom-hooks"> +<Recipes titleText="Ví dụ về việc gói các Effect trong các Hook tùy chỉnh" titleId="examples-custom-hooks"> -#### Custom `useChatRoom` Hook {/*custom-usechatroom-hook*/} +#### Hook `useChatRoom` tùy chỉnh {/*custom-usechatroom-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is extracted to a custom Hook. +Ví dụ này giống hệt với một trong những [ví dụ trước đó,](#examples-connecting) nhưng logic được trích xuất sang một Hook tùy chỉnh. <Sandpack> @@ -568,7 +568,7 @@ function ChatRoom({ roomId }) { onChange={e => setServerUrl(e.target.value)} /> </label> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> </> ); } @@ -579,7 +579,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -590,7 +590,7 @@ export default function App() { </select> </label> <button onClick={() => setShow(!show)}> - {show ? 'Close chat' : 'Open chat'} + {show ? 'Đóng chat' : 'Mở chat'} </button> {show && <hr />} {show && <ChatRoom roomId={roomId} />} @@ -616,13 +616,13 @@ export function useChatRoom({ serverUrl, roomId }) { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -637,9 +637,9 @@ button { margin-left: 10px; } <Solution /> -#### Custom `useWindowListener` Hook {/*custom-usewindowlistener-hook*/} +#### Hook `useWindowListener` tùy chỉnh {/*custom-usewindowlistener-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is extracted to a custom Hook. +Ví dụ này giống hệt với một trong những [ví dụ trước đó,](#examples-connecting) nhưng logic được trích xuất sang một Hook tùy chỉnh. <Sandpack> @@ -694,9 +694,9 @@ body { <Solution /> -#### Custom `useIntersectionObserver` Hook {/*custom-useintersectionobserver-hook*/} +#### Hook `useIntersectionObserver` tùy chỉnh {/*custom-useintersectionobserver-hook*/} -This example is identical to one of the [earlier examples,](#examples-connecting) but the logic is partially extracted to a custom Hook. +Ví dụ này giống hệt với một trong những [ví dụ trước đó,](#examples-connecting) nhưng logic được trích xuất một phần sang một Hook tùy chỉnh. <Sandpack> @@ -718,7 +718,7 @@ export default function App() { function LongSection() { const items = []; for (let i = 0; i < 50; i++) { - items.push(<li key={i}>Item #{i} (keep scrolling)</li>); + items.push(<li key={i}>Mục #{i} (tiếp tục cuộn)</li>); } return <ul>{items}</ul> } @@ -786,11 +786,11 @@ export function useIntersectionObserver(ref) { --- -### Controlling a non-React widget {/*controlling-a-non-react-widget*/} +### Kiểm soát một widget không phải React {/*controlling-a-non-react-widget*/} -Sometimes, you want to keep an external system synchronized to some prop or state of your component. +Đôi khi, bạn muốn giữ một hệ thống bên ngoài được đồng bộ hóa với một số prop hoặc state của component của bạn. -For example, if you have a third-party map widget or a video player component written without React, you can use an Effect to call methods on it that make its state match the current state of your React component. This Effect creates an instance of a `MapWidget` class defined in `map-widget.js`. When you change the `zoomLevel` prop of the `Map` component, the Effect calls the `setZoom()` on the class instance to keep it synchronized: +Ví dụ: nếu bạn có một widget bản đồ của bên thứ ba hoặc một component trình phát video được viết mà không cần React, bạn có thể sử dụng Effect để gọi các phương thức trên nó để làm cho state của nó khớp với state hiện tại của component React của bạn. Effect này tạo một instance của một class `MapWidget` được định nghĩa trong `map-widget.js`. Khi bạn thay đổi prop `zoomLevel` của component `Map`, Effect sẽ gọi `setZoom()` trên instance class để giữ cho nó được đồng bộ hóa: <Sandpack> @@ -890,15 +890,15 @@ button { margin: 5px; } </Sandpack> -In this example, a cleanup function is not needed because the `MapWidget` class manages only the DOM node that was passed to it. After the `Map` React component is removed from the tree, both the DOM node and the `MapWidget` class instance will be automatically garbage-collected by the browser JavaScript engine. +Trong ví dụ này, một hàm dọn dẹp là không cần thiết vì class `MapWidget` chỉ quản lý DOM node đã được truyền cho nó. Sau khi component `Map` React bị xóa khỏi cây, cả DOM node và instance class `MapWidget` sẽ tự động được thu gom rác bởi engine JavaScript của trình duyệt. --- -### Fetching data with Effects {/*fetching-data-with-effects*/} +### Tìm nạp dữ liệu với Effects {/*fetching-data-with-effects*/} -You can use an Effect to fetch data for your component. Note that [if you use a framework,](/learn/start-a-new-react-project#production-grade-react-frameworks) using your framework's data fetching mechanism will be a lot more efficient than writing Effects manually. +Bạn có thể sử dụng Effect để tìm nạp dữ liệu cho component của bạn. Lưu ý rằng [nếu bạn sử dụng một framework,](/learn/start-a-new-react-project#production-grade-react-frameworks) việc sử dụng cơ chế tìm nạp dữ liệu tích hợp của framework của bạn sẽ hiệu quả hơn nhiều so với việc viết Effects thủ công. -If you want to fetch data from an Effect manually, your code might look like this: +Nếu bạn muốn tìm nạp dữ liệu từ một Effect thủ công, code của bạn có thể trông như thế này: ```js import { useState, useEffect } from 'react'; @@ -924,7 +924,7 @@ export default function Page() { // ... ``` -Note the `ignore` variable which is initialized to `false`, and is set to `true` during cleanup. This ensures [your code doesn't suffer from "race conditions":](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) network responses may arrive in a different order than you sent them. +Lưu ý biến `ignore` được khởi tạo thành `false` và được đặt thành `true` trong quá trình dọn dẹp. Điều này đảm bảo [code của bạn không bị "race conditions":](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) các phản hồi mạng có thể đến theo một thứ tự khác với thứ tự bạn đã gửi chúng. <Sandpack> @@ -977,7 +977,7 @@ export async function fetchBio(person) { </Sandpack> -You can also rewrite using the [`async` / `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) syntax, but you still need to provide a cleanup function: +Bạn cũng có thể viết lại bằng cú pháp [`async` / `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), nhưng bạn vẫn cần cung cấp một hàm dọn dẹp: <Sandpack> @@ -1033,50 +1033,50 @@ export async function fetchBio(person) { </Sandpack> -Writing data fetching directly in Effects gets repetitive and makes it difficult to add optimizations like caching and server rendering later. [It's easier to use a custom Hook--either your own or maintained by the community.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) +Việc viết code tìm nạp dữ liệu trực tiếp trong Effects trở nên lặp đi lặp lại và gây khó khăn cho việc thêm các tối ưu hóa như caching và server rendering sau này. [Sẽ dễ dàng hơn khi sử dụng một Hook tùy chỉnh--hoặc của riêng bạn hoặc được duy trì bởi cộng đồng.](/learn/reusing-logic-with-custom-hooks#when-to-use-custom-hooks) <DeepDive> -#### What are good alternatives to data fetching in Effects? {/*what-are-good-alternatives-to-data-fetching-in-effects*/} +#### Những lựa chọn thay thế tốt cho việc tìm nạp dữ liệu trong Effects là gì? {/*what-are-good-alternatives-to-data-fetching-in-effects*/} -Writing `fetch` calls inside Effects is a [popular way to fetch data](https://www.robinwieruch.de/react-hooks-fetch-data/), especially in fully client-side apps. This is, however, a very manual approach and it has significant downsides: +Việc viết các lệnh gọi `fetch` bên trong Effects là một [cách phổ biến để tìm nạp dữ liệu](https://www.robinwieruch.de/react-hooks-fetch-data/), đặc biệt là trong các ứng dụng hoàn toàn phía client. Tuy nhiên, đây là một cách tiếp cận rất thủ công và nó có những nhược điểm đáng kể: -- **Effects don't run on the server.** This means that the initial server-rendered HTML will only include a loading state with no data. The client computer will have to download all JavaScript and render your app only to discover that now it needs to load the data. This is not very efficient. -- **Fetching directly in Effects makes it easy to create "network waterfalls".** You render the parent component, it fetches some data, renders the child components, and then they start fetching their data. If the network is not very fast, this is significantly slower than fetching all data in parallel. -- **Fetching directly in Effects usually means you don't preload or cache data.** For example, if the component unmounts and then mounts again, it would have to fetch the data again. -- **It's not very ergonomic.** There's quite a bit of boilerplate code involved when writing `fetch` calls in a way that doesn't suffer from bugs like [race conditions.](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) +- **Effects không chạy trên server.** Điều này có nghĩa là HTML được render ban đầu phía server sẽ chỉ bao gồm một state loading mà không có dữ liệu. Máy tính của client sẽ phải tải xuống tất cả JavaScript và render ứng dụng của bạn chỉ để phát hiện ra rằng bây giờ nó cần tải dữ liệu. Điều này không hiệu quả lắm. +- **Việc tìm nạp trực tiếp trong Effects giúp dễ dàng tạo ra "network waterfalls".** Bạn render component cha, nó tìm nạp một số dữ liệu, render các component con, và sau đó chúng bắt đầu tìm nạp dữ liệu của chúng. Nếu mạng không nhanh lắm, điều này sẽ chậm hơn đáng kể so với việc tìm nạp tất cả dữ liệu song song. +- **Việc tìm nạp trực tiếp trong Effects thường có nghĩa là bạn không preload hoặc cache dữ liệu.** Ví dụ: nếu component unmount và sau đó mount lại, nó sẽ phải tìm nạp lại dữ liệu. +- **Nó không được tiện dụng lắm.** Có khá nhiều code boilerplate liên quan khi viết các lệnh gọi `fetch` theo cách không bị các lỗi như [race conditions.](https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect) -This list of downsides is not specific to React. It applies to fetching data on mount with any library. Like with routing, data fetching is not trivial to do well, so we recommend the following approaches: +Danh sách các nhược điểm này không dành riêng cho React. Nó áp dụng cho việc tìm nạp dữ liệu trên mount với bất kỳ thư viện nào. Giống như với routing, việc tìm nạp dữ liệu không phải là điều tầm thường để thực hiện tốt, vì vậy chúng tôi khuyên bạn nên sử dụng các cách tiếp cận sau: -- **If you use a [framework](/learn/start-a-new-react-project#production-grade-react-frameworks), use its built-in data fetching mechanism.** Modern React frameworks have integrated data fetching mechanisms that are efficient and don't suffer from the above pitfalls. -- **Otherwise, consider using or building a client-side cache.** Popular open source solutions include [React Query](https://tanstack.com/query/latest/), [useSWR](https://swr.vercel.app/), and [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) You can build your own solution too, in which case you would use Effects under the hood but also add logic for deduplicating requests, caching responses, and avoiding network waterfalls (by preloading data or hoisting data requirements to routes). +- **Nếu bạn sử dụng một [framework](/learn/start-a-new-react-project#production-grade-react-frameworks), hãy sử dụng cơ chế tìm nạp dữ liệu tích hợp của nó.** Các framework React hiện đại có các cơ chế tìm nạp dữ liệu tích hợp hiệu quả và không mắc phải những cạm bẫy trên. +- **Nếu không, hãy cân nhắc sử dụng hoặc xây dựng một cache phía client.** Các giải pháp mã nguồn mở phổ biến bao gồm [React Query](https://tanstack.com/query/latest/), [useSWR](https://swr.vercel.app/), và [React Router 6.4+.](https://beta.reactrouter.com/en/main/start/overview) Bạn cũng có thể xây dựng giải pháp của riêng mình, trong trường hợp đó, bạn sẽ sử dụng Effects bên dưới nhưng cũng thêm logic để loại bỏ các yêu cầu trùng lặp, caching các phản hồi và tránh network waterfalls (bằng cách preload dữ liệu hoặc nâng các yêu cầu dữ liệu lên các route). -You can continue fetching data directly in Effects if neither of these approaches suit you. +Bạn có thể tiếp tục tìm nạp dữ liệu trực tiếp trong Effects nếu không có cách tiếp cận nào trong số này phù hợp với bạn. </DeepDive> --- -### Specifying reactive dependencies {/*specifying-reactive-dependencies*/} +### Chỉ định các dependency reactive {/*specifying-reactive-dependencies*/} -**Notice that you can't "choose" the dependencies of your Effect.** Every <CodeStep step={2}>reactive value</CodeStep> used by your Effect's code must be declared as a dependency. Your Effect's dependency list is determined by the surrounding code: +**Lưu ý rằng bạn không thể "chọn" các dependency của Effect của bạn.** Mọi <CodeStep step={2}>giá trị reactive</CodeStep> được sử dụng bởi code Effect của bạn phải được khai báo là một dependency. Danh sách dependency của Effect của bạn được xác định bởi code xung quanh: ```js [[2, 1, "roomId"], [2, 2, "serverUrl"], [2, 5, "serverUrl"], [2, 5, "roomId"], [2, 8, "serverUrl"], [2, 8, "roomId"]] -function ChatRoom({ roomId }) { // This is a reactive value - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too +function ChatRoom({ roomId }) { // Đây là một giá trị reactive + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // Đây cũng là một giá trị reactive useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values + const connection = createConnection(serverUrl, roomId); // Effect này đọc các giá trị reactive này connection.connect(); return () => connection.disconnect(); - }, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect + }, [serverUrl, roomId]); // ✅ Vì vậy, bạn phải chỉ định chúng làm dependency của Effect của bạn // ... } ``` -If either `serverUrl` or `roomId` change, your Effect will reconnect to the chat using the new values. +Nếu `serverUrl` hoặc `roomId` thay đổi, Effect của bạn sẽ kết nối lại với chat bằng các giá trị mới. -**[Reactive values](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) include props and all variables and functions declared directly inside of your component.** Since `roomId` and `serverUrl` are reactive values, you can't remove them from the dependencies. If you try to omit them and [your linter is correctly configured for React,](/learn/editor-setup#linting) the linter will flag this as a mistake you need to fix: +**[Các giá trị reactive](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) bao gồm các prop và tất cả các biến và hàm được khai báo trực tiếp bên trong component của bạn.** Vì `roomId` và `serverUrl` là các giá trị reactive, bạn không thể xóa chúng khỏi các dependency. Nếu bạn cố gắng bỏ qua chúng và [linter của bạn được định cấu hình chính xác cho React,](/learn/editor-setup#linting) linter sẽ gắn cờ điều này là một lỗi bạn cần sửa: ```js {8} function ChatRoom({ roomId }) { @@ -1086,65 +1086,65 @@ function ChatRoom({ roomId }) { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // 🔴 React Hook useEffect has missing dependencies: 'roomId' and 'serverUrl' + }, []); // 🔴 React Hook useEffect có các dependency bị thiếu: 'roomId' và 'serverUrl' // ... } ``` -**To remove a dependency, you need to ["prove" to the linter that it *doesn't need* to be a dependency.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)** For example, you can move `serverUrl` out of your component to prove that it's not reactive and won't change on re-renders: +**Để xóa một dependency, bạn cần phải ["chứng minh" cho linter rằng nó *không cần* phải là một dependency.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies)** Ví dụ: bạn có thể di chuyển `serverUrl` ra khỏi component của bạn để chứng minh rằng nó không reactive và sẽ không thay đổi khi re-render: ```js {1,8} -const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore +const serverUrl = 'https://localhost:1234'; // Không còn là một giá trị reactive nữa function ChatRoom({ roomId }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ All dependencies declared + }, [roomId]); // ✅ Tất cả các dependency đã được khai báo // ... } ``` -Now that `serverUrl` is not a reactive value (and can't change on a re-render), it doesn't need to be a dependency. **If your Effect's code doesn't use any reactive values, its dependency list should be empty (`[]`):** +Bây giờ `serverUrl` không phải là một giá trị reactive (và không thể thay đổi khi re-render), nó không cần phải là một dependency. **Nếu code Effect của bạn không sử dụng bất kỳ giá trị reactive nào, danh sách dependency của nó phải trống (`[]`):** ```js {1,2,9} -const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore -const roomId = 'music'; // Not a reactive value anymore +const serverUrl = 'https://localhost:1234'; // Không còn là một giá trị reactive nữa +const roomId = 'music'; // Không còn là một giá trị reactive nữa function ChatRoom() { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // ✅ All dependencies declared + }, []); // ✅ Tất cả các dependency đã được khai báo // ... } ``` -[An Effect with empty dependencies](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) doesn't re-run when any of your component's props or state change. +[Một Effect với các dependency trống](/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means) không chạy lại khi bất kỳ prop hoặc state nào của component của bạn thay đổi. <Pitfall> -If you have an existing codebase, you might have some Effects that suppress the linter like this: +Nếu bạn có một codebase hiện có, bạn có thể có một số Effects bỏ qua linter như thế này: ```js {3-4} useEffect(() => { // ... - // 🔴 Avoid suppressing the linter like this: + // 🔴 Tránh bỏ qua linter như thế này: // eslint-ignore-next-line react-hooks/exhaustive-deps }, []); ``` -**When dependencies don't match the code, there is a high risk of introducing bugs.** By suppressing the linter, you "lie" to React about the values your Effect depends on. [Instead, prove they're unnecessary.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies) +**Khi các dependency không khớp với code, có một nguy cơ cao gây ra các lỗi.** Bằng cách bỏ qua linter, bạn "nói dối" với React về các giá trị mà Effect của bạn phụ thuộc vào. [Thay vào đó, hãy chứng minh rằng chúng là không cần thiết.](/learn/removing-effect-dependencies#removing-unnecessary-dependencies) </Pitfall> -<Recipes titleText="Examples of passing reactive dependencies" titleId="examples-dependencies"> +<Recipes titleText="Ví dụ về việc truyền các dependency reactive" titleId="examples-dependencies"> -#### Passing a dependency array {/*passing-a-dependency-array*/} +#### Truyền một mảng dependency {/*passing-a-dependency-array*/} -If you specify the dependencies, your Effect runs **after the initial render _and_ after re-renders with changed dependencies.** +Nếu bạn chỉ định các dependency, Effect của bạn sẽ chạy **sau lần render ban đầu _và_ sau khi re-render với các dependency đã thay đổi.** ```js {3} useEffect(() => { @@ -1152,7 +1152,7 @@ useEffect(() => { }, [a, b]); // Runs again if a or b are different ``` -In the below example, `serverUrl` and `roomId` are [reactive values,](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) so they both must be specified as dependencies. As a result, selecting a different room in the dropdown or editing the server URL input causes the chat to re-connect. However, since `message` isn't used in the Effect (and so it isn't a dependency), editing the message doesn't re-connect to the chat. +Trong ví dụ bên dưới, `serverUrl` và `roomId` là [các giá trị reactive,](/learn/lifecycle-of-reactive-effects#effects-react-to-reactive-values) vì vậy cả hai phải được chỉ định làm dependency. Do đó, việc chọn một phòng khác trong dropdown hoặc chỉnh sửa input URL của server sẽ khiến chat kết nối lại. Tuy nhiên, vì `message` không được sử dụng trong Effect (và do đó nó không phải là một dependency), việc chỉnh sửa message sẽ không kết nối lại với chat. <Sandpack> @@ -1181,9 +1181,9 @@ function ChatRoom({ roomId }) { onChange={e => setServerUrl(e.target.value)} /> </label> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> <label> - Your message:{' '} + Tin nhắn của bạn:{' '} <input value={message} onChange={e => setMessage(e.target.value)} /> </label> </> @@ -1196,7 +1196,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -1206,7 +1206,7 @@ export default function App() { <option value="music">music</option> </select> <button onClick={() => setShow(!show)}> - {show ? 'Close chat' : 'Open chat'} + {show ? 'Đóng chat' : 'Mở chat'} </button> </label> {show && <hr />} @@ -1218,13 +1218,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -1239,20 +1239,20 @@ button { margin-left: 5px; } <Solution /> -#### Passing an empty dependency array {/*passing-an-empty-dependency-array*/} +#### Truyền một mảng dependency rỗng {/*passing-an-empty-dependency-array*/} -If your Effect truly doesn't use any reactive values, it will only run **after the initial render.** +Nếu Effect của bạn thực sự không sử dụng bất kỳ giá trị reactive nào, nó sẽ chỉ chạy **sau lần render ban đầu.** ```js {3} useEffect(() => { // ... -}, []); // Does not run again (except once in development) +}, []); // Không chạy lại (ngoại trừ một lần trong quá trình phát triển) ``` -**Even with empty dependencies, setup and cleanup will [run one extra time in development](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) to help you find bugs.** +**Ngay cả với các dependency rỗng, việc thiết lập và dọn dẹp sẽ [chạy thêm một lần trong quá trình phát triển](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) để giúp bạn tìm lỗi.** -In this example, both `serverUrl` and `roomId` are hardcoded. Since they're declared outside the component, they are not reactive values, and so they aren't dependencies. The dependency list is empty, so the Effect doesn't re-run on re-renders. +Trong ví dụ này, cả `serverUrl` và `roomId` đều được hardcode. Vì chúng được khai báo bên ngoài component, chúng không phải là các giá trị reactive, và do đó chúng không phải là dependency. Danh sách dependency là rỗng, vì vậy Effect không chạy lại khi re-render. <Sandpack> @@ -1274,9 +1274,9 @@ function ChatRoom() { return ( <> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> <label> - Your message:{' '} + Tin nhắn của bạn:{' '} <input value={message} onChange={e => setMessage(e.target.value)} /> </label> </> @@ -1288,7 +1288,7 @@ export default function App() { return ( <> <button onClick={() => setShow(!show)}> - {show ? 'Close chat' : 'Open chat'} + {show ? 'Đóng chat' : 'Mở chat'} </button> {show && <hr />} {show && <ChatRoom />} @@ -1299,13 +1299,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -1316,17 +1316,17 @@ export function createConnection(serverUrl, roomId) { <Solution /> -#### Passing no dependency array at all {/*passing-no-dependency-array-at-all*/} +#### Không truyền mảng dependency nào cả {/*passing-no-dependency-array-at-all*/} -If you pass no dependency array at all, your Effect runs **after every single render (and re-render)** of your component. +Nếu bạn không truyền mảng dependency nào cả, Effect của bạn sẽ chạy **sau mỗi lần render (và re-render)** của component của bạn. ```js {3} useEffect(() => { // ... -}); // Always runs again +}); // Luôn chạy lại ``` -In this example, the Effect re-runs when you change `serverUrl` and `roomId`, which is sensible. However, it *also* re-runs when you change the `message`, which is probably undesirable. This is why usually you'll specify the dependency array. +Trong ví dụ này, Effect chạy lại khi bạn thay đổi `serverUrl` và `roomId`, điều này là hợp lý. Tuy nhiên, nó *cũng* chạy lại khi bạn thay đổi `message`, điều này có lẽ là không mong muốn. Đây là lý do tại sao bạn thường sẽ chỉ định mảng dependency. <Sandpack> @@ -1344,7 +1344,7 @@ function ChatRoom({ roomId }) { return () => { connection.disconnect(); }; - }); // No dependency array at all + }); // Không có mảng dependency nào cả return ( <> @@ -1355,9 +1355,9 @@ function ChatRoom({ roomId }) { onChange={e => setServerUrl(e.target.value)} /> </label> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> <label> - Your message:{' '} + Tin nhắn của bạn:{' '} <input value={message} onChange={e => setMessage(e.target.value)} /> </label> </> @@ -1370,7 +1370,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -1380,7 +1380,7 @@ export default function App() { <option value="music">music</option> </select> <button onClick={() => setShow(!show)}> - {show ? 'Close chat' : 'Open chat'} + {show ? 'Đóng chat' : 'Mở chat'} </button> </label> {show && <hr />} @@ -1392,13 +1392,13 @@ export default function App() { ```js src/chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -1417,9 +1417,9 @@ button { margin-left: 5px; } --- -### Updating state based on previous state from an Effect {/*updating-state-based-on-previous-state-from-an-effect*/} +### Cập nhật state dựa trên state trước đó từ một Effect {/*updating-state-based-on-previous-state-from-an-effect*/} -When you want to update state based on previous state from an Effect, you might run into a problem: +Khi bạn muốn cập nhật state dựa trên state trước đó từ một Effect, bạn có thể gặp phải một vấn đề: ```js {6,9} function Counter() { @@ -1427,17 +1427,17 @@ function Counter() { useEffect(() => { const intervalId = setInterval(() => { - setCount(count + 1); // You want to increment the counter every second... + setCount(count + 1); // Bạn muốn tăng bộ đếm mỗi giây... }, 1000) return () => clearInterval(intervalId); - }, [count]); // 🚩 ... but specifying `count` as a dependency always resets the interval. + }, [count]); // 🚩 ... nhưng việc chỉ định `count` làm dependency luôn reset interval. // ... } ``` -Since `count` is a reactive value, it must be specified in the list of dependencies. However, that causes the Effect to cleanup and setup again every time the `count` changes. This is not ideal. +Vì `count` là một giá trị reactive, nó phải được chỉ định trong danh sách các dependency. Tuy nhiên, điều đó khiến Effect dọn dẹp và thiết lập lại mỗi khi `count` thay đổi. Điều này không lý tưởng. -To fix this, [pass the `c => c + 1` state updater](/reference/react/useState#updating-state-based-on-the-previous-state) to `setCount`: +Để khắc phục điều này, [truyền hàm cập nhật state `c => c + 1`](/reference/react/useState#updating-state-based-on-the-previous-state) cho `setCount`: <Sandpack> @@ -1449,10 +1449,10 @@ export default function Counter() { useEffect(() => { const intervalId = setInterval(() => { - setCount(c => c + 1); // ✅ Pass a state updater + setCount(c => c + 1); // ✅ Truyền một hàm cập nhật state }, 1000); return () => clearInterval(intervalId); - }, []); // ✅ Now count is not a dependency + }, []); // ✅ Bây giờ count không phải là một dependency return <h1>{count}</h1>; } @@ -1472,14 +1472,14 @@ body { </Sandpack> -Now that you're passing `c => c + 1` instead of `count + 1`, [your Effect no longer needs to depend on `count`.](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state) As a result of this fix, it won't need to cleanup and setup the interval again every time the `count` changes. +Bây giờ bạn đang truyền `c => c + 1` thay vì `count + 1`, [Effect của bạn không còn cần phải phụ thuộc vào `count`.](/learn/removing-effect-dependencies#are-you-reading-some-state-to-calculate-the-next-state) Do kết quả của việc sửa lỗi này, nó sẽ không cần phải dọn dẹp và thiết lập lại interval mỗi khi `count` thay đổi. --- -### Removing unnecessary object dependencies {/*removing-unnecessary-object-dependencies*/} +### Loại bỏ các dependency object không cần thiết {/*removing-unnecessary-object-dependencies*/} -If your Effect depends on an object or a function created during rendering, it might run too often. For example, this Effect re-connects after every render because the `options` object is [different for every render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) +Nếu Effect của bạn phụ thuộc vào một đối tượng hoặc một hàm được tạo trong quá trình rendering, nó có thể chạy quá thường xuyên. Ví dụ: Effect này kết nối lại sau mỗi lần render vì đối tượng `options` [khác nhau cho mỗi lần render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) ```js {6-9,12,15} const serverUrl = 'https://localhost:1234'; @@ -1487,20 +1487,20 @@ const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - const options = { // 🚩 This object is created from scratch on every re-render + const options = { // 🚩 Đối tượng này được tạo mới hoàn toàn sau mỗi lần re-render serverUrl: serverUrl, roomId: roomId }; useEffect(() => { - const connection = createConnection(options); // It's used inside the Effect + const connection = createConnection(options); // Nó được sử dụng bên trong Effect connection.connect(); return () => connection.disconnect(); - }, [options]); // 🚩 As a result, these dependencies are always different on a re-render + }, [options]); // 🚩 Kết quả là, các dependency này luôn khác nhau sau mỗi lần re-render // ... ``` -Avoid using an object created during rendering as a dependency. Instead, create the object inside the Effect: +Tránh sử dụng một đối tượng được tạo trong quá trình rendering làm dependency. Thay vào đó, hãy tạo đối tượng bên trong Effect: <Sandpack> @@ -1525,7 +1525,7 @@ function ChatRoom({ roomId }) { return ( <> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> </> ); @@ -1536,7 +1536,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -1555,13 +1555,13 @@ export default function App() { ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -1574,21 +1574,20 @@ button { margin-left: 10px; } </Sandpack> -Now that you create the `options` object inside the Effect, the Effect itself only depends on the `roomId` string. +Bây giờ bạn tạo đối tượng `options` bên trong Effect, bản thân Effect chỉ phụ thuộc vào chuỗi `roomId`. -With this fix, typing into the input doesn't reconnect the chat. Unlike an object which gets re-created, a string like `roomId` doesn't change unless you set it to another value. [Read more about removing dependencies.](/learn/removing-effect-dependencies) +Với sửa đổi này, việc nhập vào input sẽ không kết nối lại chat. Không giống như một đối tượng được tạo lại, một chuỗi như `roomId` không thay đổi trừ khi bạn gán nó một giá trị khác. [Đọc thêm về loại bỏ dependency.](/learn/removing-effect-dependencies) --- +### Loại bỏ các dependency function không cần thiết {/*removing-unnecessary-function-dependencies*/} -### Removing unnecessary function dependencies {/*removing-unnecessary-function-dependencies*/} - -If your Effect depends on an object or a function created during rendering, it might run too often. For example, this Effect re-connects after every render because the `createOptions` function is [different for every render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) +Nếu Effect của bạn phụ thuộc vào một đối tượng hoặc một hàm được tạo trong quá trình render, nó có thể chạy quá thường xuyên. Ví dụ: Effect này kết nối lại sau mỗi lần render vì hàm `createOptions` [khác nhau sau mỗi lần render:](/learn/removing-effect-dependencies#does-some-reactive-value-change-unintentionally) ```js {4-9,12,16} function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); - function createOptions() { // 🚩 This function is created from scratch on every re-render + function createOptions() { // 🚩 Hàm này được tạo mới hoàn toàn sau mỗi lần re-render return { serverUrl: serverUrl, roomId: roomId @@ -1596,17 +1595,17 @@ function ChatRoom({ roomId }) { } useEffect(() => { - const options = createOptions(); // It's used inside the Effect + const options = createOptions(); // Nó được sử dụng bên trong Effect const connection = createConnection(); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // 🚩 As a result, these dependencies are always different on a re-render + }, [createOptions]); // 🚩 Kết quả là, các dependency này luôn khác nhau sau mỗi lần re-render // ... ``` -By itself, creating a function from scratch on every re-render is not a problem. You don't need to optimize that. However, if you use it as a dependency of your Effect, it will cause your Effect to re-run after every re-render. +Bản thân việc tạo một hàm mới hoàn toàn sau mỗi lần re-render không phải là một vấn đề. Bạn không cần phải tối ưu hóa điều đó. Tuy nhiên, nếu bạn sử dụng nó như một dependency của Effect, nó sẽ khiến Effect của bạn chạy lại sau mỗi lần re-render. -Avoid using a function created during rendering as a dependency. Instead, declare it inside the Effect: +Tránh sử dụng một hàm được tạo trong quá trình render làm dependency. Thay vào đó, hãy khai báo nó bên trong Effect: <Sandpack> @@ -1635,7 +1634,7 @@ function ChatRoom({ roomId }) { return ( <> - <h1>Welcome to the {roomId} room!</h1> + <h1>Chào mừng đến phòng {roomId}!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> </> ); @@ -1646,7 +1645,7 @@ export default function App() { return ( <> <label> - Choose the chat room:{' '} + Chọn phòng chat:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} @@ -1665,13 +1664,13 @@ export default function App() { ```js src/chat.js export function createConnection({ serverUrl, roomId }) { - // A real implementation would actually connect to the server + // Một implementation thực tế sẽ thực sự kết nối đến server return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ Đang kết nối đến phòng "' + roomId + '" tại ' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ Đã ngắt kết nối khỏi phòng "' + roomId + '" tại ' + serverUrl); } }; } @@ -1684,32 +1683,32 @@ button { margin-left: 10px; } </Sandpack> -Now that you define the `createOptions` function inside the Effect, the Effect itself only depends on the `roomId` string. With this fix, typing into the input doesn't reconnect the chat. Unlike a function which gets re-created, a string like `roomId` doesn't change unless you set it to another value. [Read more about removing dependencies.](/learn/removing-effect-dependencies) +Bây giờ bạn đã định nghĩa hàm `createOptions` bên trong Effect, bản thân Effect chỉ phụ thuộc vào chuỗi `roomId`. Với sửa đổi này, việc nhập vào input sẽ không kết nối lại chat. Không giống như một hàm được tạo lại, một chuỗi như `roomId` không thay đổi trừ khi bạn gán nó một giá trị khác. [Đọc thêm về loại bỏ dependency.](/learn/removing-effect-dependencies) --- -### Reading the latest props and state from an Effect {/*reading-the-latest-props-and-state-from-an-effect*/} +### Đọc các props và state mới nhất từ Effect {/*reading-the-latest-props-and-state-from-an-effect*/} <Wip> -This section describes an **experimental API that has not yet been released** in a stable version of React. +Phần này mô tả một **API thử nghiệm chưa được phát hành** trong phiên bản ổn định của React. </Wip> -By default, when you read a reactive value from an Effect, you have to add it as a dependency. This ensures that your Effect "reacts" to every change of that value. For most dependencies, that's the behavior you want. +Theo mặc định, khi bạn đọc một giá trị reactive từ Effect, bạn phải thêm nó làm dependency. Điều này đảm bảo rằng Effect của bạn "phản ứng" với mọi thay đổi của giá trị đó. Đối với hầu hết các dependency, đó là hành vi bạn muốn. -**However, sometimes you'll want to read the *latest* props and state from an Effect without "reacting" to them.** For example, imagine you want to log the number of the items in the shopping cart for every page visit: +**Tuy nhiên, đôi khi bạn sẽ muốn đọc các props và state *mới nhất* từ Effect mà không cần "phản ứng" với chúng.** Ví dụ: hãy tưởng tượng bạn muốn ghi lại số lượng các mặt hàng trong giỏ hàng cho mỗi lần truy cập trang: ```js {3} function Page({ url, shoppingCart }) { useEffect(() => { logVisit(url, shoppingCart.length); - }, [url, shoppingCart]); // ✅ All dependencies declared + }, [url, shoppingCart]); // ✅ Tất cả các dependency đã được khai báo // ... } ``` -**What if you want to log a new page visit after every `url` change, but *not* if only the `shoppingCart` changes?** You can't exclude `shoppingCart` from dependencies without breaking the [reactivity rules.](#specifying-reactive-dependencies) However, you can express that you *don't want* a piece of code to "react" to changes even though it is called from inside an Effect. [Declare an *Effect Event*](/learn/separating-events-from-effects#declaring-an-effect-event) with the [`useEffectEvent`](/reference/react/experimental_useEffectEvent) Hook, and move the code reading `shoppingCart` inside of it: +**Điều gì sẽ xảy ra nếu bạn muốn ghi lại một lượt truy cập trang mới sau mỗi thay đổi `url`, nhưng *không phải* nếu chỉ `shoppingCart` thay đổi?** Bạn không thể loại trừ `shoppingCart` khỏi các dependency mà không phá vỡ [các quy tắc reactive.](#specifying-reactive-dependencies) Tuy nhiên, bạn có thể thể hiện rằng bạn *không muốn* một đoạn code "phản ứng" với các thay đổi mặc dù nó được gọi từ bên trong Effect. [Khai báo một *Effect Event*](/learn/separating-events-from-effects#declaring-an-effect-event) với Hook [`useEffectEvent`](/reference/react/experimental_useEffectEvent) và di chuyển code đọc `shoppingCart` vào bên trong nó: ```js {2-4,7,8} function Page({ url, shoppingCart }) { @@ -1719,23 +1718,22 @@ function Page({ url, shoppingCart }) { useEffect(() => { onVisit(url); - }, [url]); // ✅ All dependencies declared + }, [url]); // ✅ Tất cả các dependency đã được khai báo // ... } ``` -**Effect Events are not reactive and must always be omitted from dependencies of your Effect.** This is what lets you put non-reactive code (where you can read the latest value of some props and state) inside of them. By reading `shoppingCart` inside of `onVisit`, you ensure that `shoppingCart` won't re-run your Effect. - -[Read more about how Effect Events let you separate reactive and non-reactive code.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) +**Effect Events không reactive và phải luôn được bỏ qua khỏi các dependency của Effect của bạn.** Đây là điều cho phép bạn đặt code không reactive (nơi bạn có thể đọc giá trị mới nhất của một số props và state) bên trong chúng. Bằng cách đọc `shoppingCart` bên trong `onVisit`, bạn đảm bảo rằng `shoppingCart` sẽ không chạy lại Effect của bạn. +[Đọc thêm về cách Effect Events cho phép bạn tách code reactive và không reactive.](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) --- -### Displaying different content on the server and the client {/*displaying-different-content-on-the-server-and-the-client*/} +### Hiển thị nội dung khác nhau trên server và client {/*displaying-different-content-on-the-server-and-the-client*/} -If your app uses server rendering (either [directly](/reference/react-dom/server) or via a [framework](/learn/start-a-new-react-project#production-grade-react-frameworks)), your component will render in two different environments. On the server, it will render to produce the initial HTML. On the client, React will run the rendering code again so that it can attach your event handlers to that HTML. This is why, for [hydration](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) to work, your initial render output must be identical on the client and the server. +Nếu ứng dụng của bạn sử dụng server rendering (hoặc [trực tiếp](/reference/react-dom/server) hoặc thông qua một [framework](/learn/start-a-new-react-project#production-grade-react-frameworks)), component của bạn sẽ render trong hai môi trường khác nhau. Trên server, nó sẽ render để tạo ra HTML ban đầu. Trên client, React sẽ chạy lại code rendering để nó có thể đính kèm các trình xử lý sự kiện của bạn vào HTML đó. Đây là lý do tại sao, để [hydration](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) hoạt động, đầu ra render ban đầu của bạn phải giống hệt nhau trên client và server. -In rare cases, you might need to display different content on the client. For example, if your app reads some data from [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), it can't possibly do that on the server. Here is how you could implement this: +Trong một số trường hợp hiếm hoi, bạn có thể cần hiển thị nội dung khác nhau trên client. Ví dụ: nếu ứng dụng của bạn đọc một số dữ liệu từ [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), thì nó không thể thực hiện điều đó trên server. Đây là cách bạn có thể triển khai điều này: ```js function MyComponent() { @@ -1746,44 +1744,44 @@ function MyComponent() { }, []); if (didMount) { - // ... return client-only JSX ... + // ... trả về JSX chỉ dành cho client ... } else { - // ... return initial JSX ... + // ... trả về JSX ban đầu ... } } ``` -While the app is loading, the user will see the initial render output. Then, when it's loaded and hydrated, your Effect will run and set `didMount` to `true`, triggering a re-render. This will switch to the client-only render output. Effects don't run on the server, so this is why `didMount` was `false` during the initial server render. +Trong khi ứng dụng đang tải, người dùng sẽ thấy đầu ra render ban đầu. Sau đó, khi nó được tải và hydrate, Effect của bạn sẽ chạy và đặt `didMount` thành `true`, kích hoạt re-render. Điều này sẽ chuyển sang đầu ra render chỉ dành cho client. Các Effect không chạy trên server, vì vậy đây là lý do tại sao `didMount` là `false` trong quá trình server render ban đầu. -Use this pattern sparingly. Keep in mind that users with a slow connection will see the initial content for quite a bit of time--potentially, many seconds--so you don't want to make jarring changes to your component's appearance. In many cases, you can avoid the need for this by conditionally showing different things with CSS. +Sử dụng pattern này một cách tiết kiệm. Hãy nhớ rằng người dùng có kết nối chậm sẽ thấy nội dung ban đầu trong một khoảng thời gian khá dài - có khả năng là nhiều giây - vì vậy bạn không muốn thực hiện các thay đổi khó chịu đối với giao diện của component. Trong nhiều trường hợp, bạn có thể tránh sự cần thiết của điều này bằng cách hiển thị có điều kiện những thứ khác nhau bằng CSS. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My Effect runs twice when the component mounts {/*my-effect-runs-twice-when-the-component-mounts*/} +### Effect của tôi chạy hai lần khi component được mount {/*my-effect-runs-twice-when-the-component-mounts*/} -When Strict Mode is on, in development, React runs setup and cleanup one extra time before the actual setup. +Khi Strict Mode được bật, trong quá trình phát triển, React sẽ chạy thiết lập và dọn dẹp thêm một lần trước khi thiết lập thực tế. -This is a stress-test that verifies your Effect’s logic is implemented correctly. If this causes visible issues, your cleanup function is missing some logic. The cleanup function should stop or undo whatever the setup function was doing. The rule of thumb is that the user shouldn’t be able to distinguish between the setup being called once (as in production) and a setup → cleanup → setup sequence (as in development). +Đây là một bài kiểm tra áp lực để xác minh logic Effect của bạn được triển khai chính xác. Nếu điều này gây ra các vấn đề có thể nhìn thấy, thì hàm dọn dẹp của bạn đang thiếu một số logic. Hàm dọn dẹp sẽ dừng hoặc hoàn tác bất cứ điều gì mà hàm thiết lập đã làm. Nguyên tắc chung là người dùng không thể phân biệt giữa việc thiết lập được gọi một lần (như trong production) và một chuỗi thiết lập → dọn dẹp → thiết lập (như trong quá trình phát triển). -Read more about [how this helps find bugs](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) and [how to fix your logic.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +Đọc thêm về [cách điều này giúp tìm lỗi](/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed) và [cách sửa logic của bạn.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) --- -### My Effect runs after every re-render {/*my-effect-runs-after-every-re-render*/} +### Effect của tôi chạy sau mỗi lần re-render {/*my-effect-runs-after-every-re-render*/} -First, check that you haven't forgotten to specify the dependency array: +Đầu tiên, hãy kiểm tra xem bạn có quên chỉ định mảng dependency hay không: ```js {3} useEffect(() => { // ... -}); // 🚩 No dependency array: re-runs after every render! +}); // 🚩 Không có mảng dependency: chạy lại sau mỗi lần render! ``` -If you've specified the dependency array but your Effect still re-runs in a loop, it's because one of your dependencies is different on every re-render. +Nếu bạn đã chỉ định mảng dependency nhưng Effect của bạn vẫn chạy lại trong một vòng lặp, thì đó là vì một trong các dependency của bạn khác nhau sau mỗi lần re-render. -You can debug this problem by manually logging your dependencies to the console: +Bạn có thể gỡ lỗi vấn đề này bằng cách ghi thủ công các dependency của bạn vào console: ```js {5} useEffect(() => { @@ -1793,58 +1791,58 @@ You can debug this problem by manually logging your dependencies to the console: console.log([serverUrl, roomId]); ``` -You can then right-click on the arrays from different re-renders in the console and select "Store as a global variable" for both of them. Assuming the first one got saved as `temp1` and the second one got saved as `temp2`, you can then use the browser console to check whether each dependency in both arrays is the same: +Sau đó, bạn có thể nhấp chuột phải vào các mảng từ các re-render khác nhau trong console và chọn "Store as a global variable" cho cả hai. Giả sử cái đầu tiên được lưu dưới dạng `temp1` và cái thứ hai được lưu dưới dạng `temp2`, sau đó bạn có thể sử dụng console của trình duyệt để kiểm tra xem mỗi dependency trong cả hai mảng có giống nhau hay không: ```js -Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays? -Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays? -Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ... +Object.is(temp1[0], temp2[0]); // Dependency đầu tiên có giống nhau giữa các mảng không? +Object.is(temp1[1], temp2[1]); // Dependency thứ hai có giống nhau giữa các mảng không? +Object.is(temp1[2], temp2[2]); // ... và cứ thế cho mọi dependency ... ``` -When you find the dependency that is different on every re-render, you can usually fix it in one of these ways: +Khi bạn tìm thấy dependency khác nhau sau mỗi lần re-render, bạn thường có thể sửa nó theo một trong những cách sau: -- [Updating state based on previous state from an Effect](#updating-state-based-on-previous-state-from-an-effect) -- [Removing unnecessary object dependencies](#removing-unnecessary-object-dependencies) -- [Removing unnecessary function dependencies](#removing-unnecessary-function-dependencies) -- [Reading the latest props and state from an Effect](#reading-the-latest-props-and-state-from-an-effect) +- [Cập nhật state dựa trên state trước đó từ Effect](#updating-state-based-on-previous-state-from-an-effect) +- [Loại bỏ các dependency object không cần thiết](#removing-unnecessary-object-dependencies) +- [Loại bỏ các dependency function không cần thiết](#removing-unnecessary-function-dependencies) +- [Đọc các props và state mới nhất từ Effect](#reading-the-latest-props-and-state-from-an-effect) -As a last resort (if these methods didn't help), wrap its creation with [`useMemo`](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) or [`useCallback`](/reference/react/useCallback#preventing-an-effect-from-firing-too-often) (for functions). +Phương sách cuối cùng (nếu các phương pháp này không giúp ích), hãy bọc việc tạo nó bằng [`useMemo`](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) hoặc [`useCallback`](/reference/react/useCallback#preventing-an-effect-from-firing-too-often) (cho các function). --- -### My Effect keeps re-running in an infinite cycle {/*my-effect-keeps-re-running-in-an-infinite-cycle*/} +### Effect của tôi tiếp tục chạy lại trong một vòng lặp vô hạn {/*my-effect-keeps-re-running-in-an-infinite-cycle*/} -If your Effect runs in an infinite cycle, these two things must be true: +Nếu Effect của bạn chạy trong một vòng lặp vô hạn, thì hai điều này phải đúng: -- Your Effect is updating some state. -- That state leads to a re-render, which causes the Effect's dependencies to change. +- Effect của bạn đang cập nhật một số state. +- State đó dẫn đến re-render, khiến các dependency của Effect thay đổi. -Before you start fixing the problem, ask yourself whether your Effect is connecting to some external system (like DOM, network, a third-party widget, and so on). Why does your Effect need to set state? Does it synchronize with that external system? Or are you trying to manage your application's data flow with it? +Trước khi bạn bắt đầu sửa vấn đề, hãy tự hỏi Effect của bạn có kết nối với một số hệ thống bên ngoài (như DOM, mạng, một widget của bên thứ ba, v.v.) hay không. Tại sao Effect của bạn cần đặt state? Nó có đồng bộ hóa với hệ thống bên ngoài đó không? Hay bạn đang cố gắng quản lý luồng dữ liệu của ứng dụng của mình bằng nó? -If there is no external system, consider whether [removing the Effect altogether](/learn/you-might-not-need-an-effect) would simplify your logic. +Nếu không có hệ thống bên ngoài, hãy xem xét liệu [loại bỏ Effect hoàn toàn](/learn/you-might-not-need-an-effect) có đơn giản hóa logic của bạn hay không. -If you're genuinely synchronizing with some external system, think about why and under what conditions your Effect should update the state. Has something changed that affects your component's visual output? If you need to keep track of some data that isn't used by rendering, a [ref](/reference/react/useRef#referencing-a-value-with-a-ref) (which doesn't trigger re-renders) might be more appropriate. Verify your Effect doesn't update the state (and trigger re-renders) more than needed. +Nếu bạn thực sự đang đồng bộ hóa với một số hệ thống bên ngoài, hãy suy nghĩ về lý do và trong điều kiện nào Effect của bạn sẽ cập nhật state. Có điều gì đó đã thay đổi ảnh hưởng đến đầu ra hình ảnh của component của bạn không? Nếu bạn cần theo dõi một số dữ liệu không được sử dụng bởi rendering, thì [ref](/reference/react/useRef#referencing-a-value-with-a-ref) (không kích hoạt re-render) có thể phù hợp hơn. Xác minh Effect của bạn không cập nhật state (và kích hoạt re-render) nhiều hơn mức cần thiết. -Finally, if your Effect is updating the state at the right time, but there is still a loop, it's because that state update leads to one of the Effect's dependencies changing. [Read how to debug dependency changes.](/reference/react/useEffect#my-effect-runs-after-every-re-render) +Cuối cùng, nếu Effect của bạn đang cập nhật state vào đúng thời điểm, nhưng vẫn còn một vòng lặp, thì đó là vì bản cập nhật state đó dẫn đến một trong các dependency của Effect thay đổi. [Đọc cách gỡ lỗi các thay đổi dependency.](/reference/react/useEffect#my-effect-runs-after-every-re-render) --- -### My cleanup logic runs even though my component didn't unmount {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/} +### Logic dọn dẹp của tôi chạy ngay cả khi component của tôi không unmount {/*my-cleanup-logic-runs-even-though-my-component-didnt-unmount*/} -The cleanup function runs not only during unmount, but before every re-render with changed dependencies. Additionally, in development, React [runs setup+cleanup one extra time immediately after component mounts.](#my-effect-runs-twice-when-the-component-mounts) +Hàm dọn dẹp chạy không chỉ trong quá trình unmount, mà trước mỗi lần re-render với các dependency đã thay đổi. Ngoài ra, trong quá trình phát triển, React [chạy thiết lập + dọn dẹp thêm một lần ngay sau khi component được mount.](#my-effect-runs-twice-when-the-component-mounts) -If you have cleanup code without corresponding setup code, it's usually a code smell: +Nếu bạn có code dọn dẹp mà không có code thiết lập tương ứng, thì đó thường là một dấu hiệu xấu: ```js {2-5} useEffect(() => { - // 🔴 Avoid: Cleanup logic without corresponding setup logic + // 🔴 Tránh: Logic dọn dẹp mà không có logic thiết lập tương ứng return () => { doSomething(); }; }, []); ``` -Your cleanup logic should be "symmetrical" to the setup logic, and should stop or undo whatever setup did: +Logic dọn dẹp của bạn phải "đối xứng" với logic thiết lập và phải dừng hoặc hoàn tác bất cứ điều gì mà thiết lập đã làm: ```js {2-3,5} useEffect(() => { @@ -1856,10 +1854,10 @@ Your cleanup logic should be "symmetrical" to the setup logic, and should stop o }, [serverUrl, roomId]); ``` -[Learn how the Effect lifecycle is different from the component's lifecycle.](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) +[Tìm hiểu cách vòng đời Effect khác với vòng đời của component.](/learn/lifecycle-of-reactive-effects#the-lifecycle-of-an-effect) --- -### My Effect does something visual, and I see a flicker before it runs {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/} +### Effect của tôi làm điều gì đó trực quan và tôi thấy một nhấp nháy trước khi nó chạy {/*my-effect-does-something-visual-and-i-see-a-flicker-before-it-runs*/} -If your Effect must block the browser from [painting the screen,](/learn/render-and-commit#epilogue-browser-paint) replace `useEffect` with [`useLayoutEffect`](/reference/react/useLayoutEffect). Note that **this shouldn't be needed for the vast majority of Effects.** You'll only need this if it's crucial to run your Effect before the browser paint: for example, to measure and position a tooltip before the user sees it. +Nếu Effect của bạn phải chặn trình duyệt [vẽ màn hình,](/learn/render-and-commit#epilogue-browser-paint) hãy thay thế `useEffect` bằng [`useLayoutEffect`](/reference/react/useLayoutEffect). Lưu ý rằng **điều này không cần thiết cho phần lớn các Effect.** Bạn sẽ chỉ cần điều này nếu điều quan trọng là phải chạy Effect của bạn trước khi trình duyệt vẽ: ví dụ: để đo và định vị một tooltip trước khi người dùng nhìn thấy nó. diff --git a/src/content/reference/react/useId.md b/src/content/reference/react/useId.md index a77cf7a72..a215f578d 100644 --- a/src/content/reference/react/useId.md +++ b/src/content/reference/react/useId.md @@ -4,7 +4,7 @@ title: useId <Intro> -`useId` is a React Hook for generating unique IDs that can be passed to accessibility attributes. +`useId` là một React Hook để tạo ID duy nhất có thể được truyền cho các thuộc tính hỗ trợ tiếp cận. ```js const id = useId() @@ -16,11 +16,11 @@ const id = useId() --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useId()` {/*useid*/} -Call `useId` at the top level of your component to generate a unique ID: +Gọi `useId` ở cấp cao nhất của component để tạo một ID duy nhất: ```js import { useId } from 'react'; @@ -30,37 +30,37 @@ function PasswordField() { // ... ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -`useId` does not take any parameters. +`useId` không nhận bất kỳ tham số nào. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`useId` returns a unique ID string associated with this particular `useId` call in this particular component. +`useId` trả về một chuỗi ID duy nhất được liên kết với lệnh gọi `useId` cụ thể này trong component cụ thể này. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useId` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. +* `useId` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc Hook của riêng bạn. Bạn không thể gọi nó bên trong vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển state vào đó. -* `useId` **should not be used to generate keys** in a list. [Keys should be generated from your data.](/learn/rendering-lists#where-to-get-your-key) +* `useId` **không nên được sử dụng để tạo key** trong một danh sách. [Key nên được tạo từ dữ liệu của bạn.](/learn/rendering-lists#where-to-get-your-key) -* `useId` currently cannot be used in [async Server Components](/reference/rsc/server-components#async-components-with-server-components). +* `useId` hiện không thể được sử dụng trong [Server Components không đồng bộ](/reference/rsc/server-components#async-components-with-server-components). --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} <Pitfall> -**Do not call `useId` to generate keys in a list.** [Keys should be generated from your data.](/learn/rendering-lists#where-to-get-your-key) +**Không gọi `useId` để tạo key trong một danh sách.** [Key nên được tạo từ dữ liệu của bạn.](/learn/rendering-lists#where-to-get-your-key) </Pitfall> -### Generating unique IDs for accessibility attributes {/*generating-unique-ids-for-accessibility-attributes*/} +### Tạo ID duy nhất cho các thuộc tính hỗ trợ tiếp cận {/*generating-unique-ids-for-accessibility-attributes*/} -Call `useId` at the top level of your component to generate a unique ID: +Gọi `useId` ở cấp cao nhất của component để tạo một ID duy nhất: ```js [[1, 4, "passwordHintId"]] import { useId } from 'react'; @@ -70,7 +70,7 @@ function PasswordField() { // ... ``` -You can then pass the <CodeStep step={1}>generated ID</CodeStep> to different attributes: +Sau đó, bạn có thể truyền <CodeStep step={1}>ID đã tạo</CodeStep> cho các thuộc tính khác nhau: ```js [[1, 2, "passwordHintId"], [1, 3, "passwordHintId"]] <> @@ -79,11 +79,11 @@ You can then pass the <CodeStep step={1}>generated ID</CodeStep> to different at </> ``` -**Let's walk through an example to see when this is useful.** +**Hãy cùng xem qua một ví dụ để thấy khi nào điều này hữu ích.** -[HTML accessibility attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) like [`aria-describedby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby) let you specify that two tags are related to each other. For example, you can specify that an element (like an input) is described by another element (like a paragraph). +[Các thuộc tính hỗ trợ tiếp cận HTML](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) như [`aria-describedby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby) cho phép bạn chỉ định rằng hai thẻ có liên quan đến nhau. Ví dụ: bạn có thể chỉ định rằng một phần tử (như một input) được mô tả bởi một phần tử khác (như một đoạn văn). -In regular HTML, you would write it like this: +Trong HTML thông thường, bạn sẽ viết nó như thế này: ```html {5,8} <label> @@ -98,7 +98,7 @@ In regular HTML, you would write it like this: </p> ``` -However, hardcoding IDs like this is not a good practice in React. A component may be rendered more than once on the page--but IDs have to be unique! Instead of hardcoding an ID, generate a unique ID with `useId`: +Tuy nhiên, việc mã hóa cứng các ID như thế này không phải là một phương pháp tốt trong React. Một component có thể được render nhiều lần trên trang--nhưng ID phải là duy nhất! Thay vì mã hóa cứng một ID, hãy tạo một ID duy nhất với `useId`: ```js {4,11,14} import { useId } from 'react'; @@ -122,7 +122,7 @@ function PasswordField() { } ``` -Now, even if `PasswordField` appears multiple times on the screen, the generated IDs won't clash. +Bây giờ, ngay cả khi `PasswordField` xuất hiện nhiều lần trên màn hình, các ID được tạo sẽ không xung đột. <Sandpack> @@ -165,33 +165,33 @@ input { margin: 5px; } </Sandpack> -[Watch this video](https://www.youtube.com/watch?v=0dNzNcuEuOo) to see the difference in the user experience with assistive technologies. +[Xem video này](https://www.youtube.com/watch?v=0dNzNcuEuOo) để thấy sự khác biệt trong trải nghiệm người dùng với các công nghệ hỗ trợ. <Pitfall> -With [server rendering](/reference/react-dom/server), **`useId` requires an identical component tree on the server and the client**. If the trees you render on the server and the client don't match exactly, the generated IDs won't match. +Với [server rendering](/reference/react-dom/server), **`useId` yêu cầu một cây component giống hệt nhau trên server và client**. Nếu cây bạn render trên server và client không khớp chính xác, các ID được tạo sẽ không khớp. </Pitfall> <DeepDive> -#### Why is useId better than an incrementing counter? {/*why-is-useid-better-than-an-incrementing-counter*/} +#### Tại sao useId tốt hơn một bộ đếm tăng dần? {/*why-is-useid-better-than-an-incrementing-counter*/} -You might be wondering why `useId` is better than incrementing a global variable like `nextId++`. +Bạn có thể tự hỏi tại sao `useId` tốt hơn việc tăng một biến toàn cục như `nextId++`. -The primary benefit of `useId` is that React ensures that it works with [server rendering.](/reference/react-dom/server) During server rendering, your components generate HTML output. Later, on the client, [hydration](/reference/react-dom/client/hydrateRoot) attaches your event handlers to the generated HTML. For hydration to work, the client output must match the server HTML. +Lợi ích chính của `useId` là React đảm bảo rằng nó hoạt động với [server rendering.](/reference/react-dom/server) Trong quá trình server rendering, các component của bạn tạo ra đầu ra HTML. Sau đó, trên client, [hydration](/reference/react-dom/client/hydrateRoot) đính kèm các trình xử lý sự kiện của bạn vào HTML đã tạo. Để hydration hoạt động, đầu ra của client phải khớp với HTML của server. -This is very difficult to guarantee with an incrementing counter because the order in which the Client Components are hydrated may not match the order in which the server HTML was emitted. By calling `useId`, you ensure that hydration will work, and the output will match between the server and the client. +Điều này rất khó để đảm bảo với một bộ đếm tăng dần vì thứ tự mà Client Components được hydrate có thể không khớp với thứ tự mà HTML của server được phát ra. Bằng cách gọi `useId`, bạn đảm bảo rằng hydration sẽ hoạt động và đầu ra sẽ khớp giữa server và client. -Inside React, `useId` is generated from the "parent path" of the calling component. This is why, if the client and the server tree are the same, the "parent path" will match up regardless of rendering order. +Bên trong React, `useId` được tạo từ "đường dẫn cha" của component gọi. Đây là lý do tại sao, nếu cây client và server giống nhau, thì "đường dẫn cha" sẽ khớp nhau bất kể thứ tự rendering. </DeepDive> --- -### Generating IDs for several related elements {/*generating-ids-for-several-related-elements*/} +### Tạo ID cho một số phần tử liên quan {/*generating-ids-for-several-related-elements*/} -If you need to give IDs to multiple related elements, you can call `useId` to generate a shared prefix for them: +Nếu bạn cần cung cấp ID cho nhiều phần tử liên quan, bạn có thể gọi `useId` để tạo một tiền tố chung cho chúng: <Sandpack> @@ -218,13 +218,13 @@ input { margin: 5px; } </Sandpack> -This lets you avoid calling `useId` for every single element that needs a unique ID. +Điều này cho phép bạn tránh gọi `useId` cho mọi phần tử cần một ID duy nhất. --- -### Specifying a shared prefix for all generated IDs {/*specifying-a-shared-prefix-for-all-generated-ids*/} +### Chỉ định một tiền tố chung cho tất cả các ID được tạo {/*specifying-a-shared-prefix-for-all-generated-ids*/} -If you render multiple independent React applications on a single page, pass `identifierPrefix` as an option to your [`createRoot`](/reference/react-dom/client/createRoot#parameters) or [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) calls. This ensures that the IDs generated by the two different apps never clash because every identifier generated with `useId` will start with the distinct prefix you've specified. +Nếu bạn render nhiều ứng dụng React độc lập trên một trang, hãy truyền `identifierPrefix` làm một tùy chọn cho các lệnh gọi [`createRoot`](/reference/react-dom/client/createRoot#parameters) hoặc [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) của bạn. Điều này đảm bảo rằng các ID được tạo bởi hai ứng dụng khác nhau không bao giờ xung đột vì mọi identifier được tạo bằng `useId` sẽ bắt đầu bằng tiền tố riêng biệt mà bạn đã chỉ định. <Sandpack> @@ -307,9 +307,9 @@ input { margin: 5px; } --- -### Using the same ID prefix on the client and the server {/*using-the-same-id-prefix-on-the-client-and-the-server*/} +### Sử dụng cùng một tiền tố ID trên client và server {/*using-the-same-id-prefix-on-the-client-and-the-server*/} -If you [render multiple independent React apps on the same page](#specifying-a-shared-prefix-for-all-generated-ids), and some of these apps are server-rendered, make sure that the `identifierPrefix` you pass to the [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) call on the client side is the same as the `identifierPrefix` you pass to the [server APIs](/reference/react-dom/server) such as [`renderToPipeableStream`.](/reference/react-dom/server/renderToPipeableStream) +Nếu bạn [render nhiều ứng dụng React độc lập trên cùng một trang](#specifying-a-shared-prefix-for-all-generated-ids), và một số ứng dụng này được render phía server, hãy đảm bảo rằng `identifierPrefix` bạn truyền cho lệnh gọi [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) ở phía client giống với `identifierPrefix` bạn truyền cho [API server](/reference/react-dom/server) như [`renderToPipeableStream`.](/reference/react-dom/server/renderToPipeableStream) ```js // Server @@ -333,4 +333,4 @@ const root = hydrateRoot( ); ``` -You do not need to pass `identifierPrefix` if you only have one React app on the page. +Bạn không cần phải truyền `identifierPrefix` nếu bạn chỉ có một ứng dụng React trên trang. diff --git a/src/content/reference/react/useImperativeHandle.md b/src/content/reference/react/useImperativeHandle.md index 00b9893be..e33e3d925 100644 --- a/src/content/reference/react/useImperativeHandle.md +++ b/src/content/reference/react/useImperativeHandle.md @@ -4,7 +4,7 @@ title: useImperativeHandle <Intro> -`useImperativeHandle` is a React Hook that lets you customize the handle exposed as a [ref.](/learn/manipulating-the-dom-with-refs) +`useImperativeHandle` là một React Hook cho phép bạn tùy chỉnh handle được hiển thị dưới dạng một [ref.](/learn/manipulating-the-dom-with-refs) ```js useImperativeHandle(ref, createHandle, dependencies?) @@ -16,11 +16,11 @@ useImperativeHandle(ref, createHandle, dependencies?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useImperativeHandle(ref, createHandle, dependencies?)` {/*useimperativehandle*/} -Call `useImperativeHandle` at the top level of your component to customize the ref handle it exposes: +Gọi `useImperativeHandle` ở cấp cao nhất của component để tùy chỉnh ref handle mà nó hiển thị: ```js import { useImperativeHandle } from 'react'; @@ -28,39 +28,39 @@ import { useImperativeHandle } from 'react'; function MyInput({ ref }) { useImperativeHandle(ref, () => { return { - // ... your methods ... + // ... các phương thức của bạn ... }; }, []); // ... ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `ref`: The `ref` you received as a prop to the `MyInput` component. +* `ref`: `ref` bạn nhận được như một prop cho component `MyInput`. -* `createHandle`: A function that takes no arguments and returns the ref handle you want to expose. That ref handle can have any type. Usually, you will return an object with the methods you want to expose. +* `createHandle`: Một hàm không nhận đối số và trả về ref handle bạn muốn hiển thị. Ref handle đó có thể có bất kỳ kiểu nào. Thông thường, bạn sẽ trả về một object với các phương thức bạn muốn hiển thị. -* **optional** `dependencies`: The list of all reactive values referenced inside of the `createHandle` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. If a re-render resulted in a change to some dependency, or if you omitted this argument, your `createHandle` function will re-execute, and the newly created handle will be assigned to the ref. +* **optional** `dependencies`: Danh sách tất cả các giá trị reactive được tham chiếu bên trong code `createHandle`. Các giá trị reactive bao gồm props, state và tất cả các biến và hàm được khai báo trực tiếp bên trong phần thân component của bạn. Nếu linter của bạn được [cấu hình cho React](/learn/editor-setup#linting), nó sẽ xác minh rằng mọi giá trị reactive được chỉ định chính xác như một dependency. Danh sách các dependency phải có một số lượng mục không đổi và được viết nội dòng như `[dep1, dep2, dep3]`. React sẽ so sánh từng dependency với giá trị trước đó của nó bằng cách sử dụng so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Nếu việc render lại dẫn đến thay đổi đối với một số dependency hoặc nếu bạn bỏ qua đối số này, hàm `createHandle` của bạn sẽ thực thi lại và handle mới được tạo sẽ được gán cho ref. <Note> -Starting with React 19, [`ref` is available as a prop.](/blog/2024/12/05/react-19#ref-as-a-prop) In React 18 and earlier, it was necessary to get the `ref` from [`forwardRef`.](/reference/react/forwardRef) +Bắt đầu với React 19, [`ref` có sẵn dưới dạng một prop.](/blog/2024/12/05/react-19#ref-as-a-prop) Trong React 18 trở về trước, cần phải lấy `ref` từ [`forwardRef`.](/reference/react/forwardRef) </Note> #### Returns {/*returns*/} -`useImperativeHandle` returns `undefined`. +`useImperativeHandle` trả về `undefined`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Exposing a custom ref handle to the parent component {/*exposing-a-custom-ref-handle-to-the-parent-component*/} +### Hiển thị một ref handle tùy chỉnh cho component cha {/*exposing-a-custom-ref-handle-to-the-parent-component*/} -To expose a DOM node to the parent element, pass in the `ref` prop to the node. +Để hiển thị một DOM node cho phần tử cha, hãy truyền prop `ref` vào node đó. ```js {2} function MyInput({ ref }) { @@ -68,7 +68,7 @@ function MyInput({ ref }) { }; ``` -With the code above, [a ref to `MyInput` will receive the `<input>` DOM node.](/learn/manipulating-the-dom-with-refs) However, you can expose a custom value instead. To customize the exposed handle, call `useImperativeHandle` at the top level of your component: +Với đoạn code trên, [một ref đến `MyInput` sẽ nhận được DOM node `<input>`.](/learn/manipulating-the-dom-with-refs) Tuy nhiên, bạn có thể hiển thị một giá trị tùy chỉnh thay thế. Để tùy chỉnh handle được hiển thị, hãy gọi `useImperativeHandle` ở cấp cao nhất của component: ```js {4-8} import { useImperativeHandle } from 'react'; @@ -76,7 +76,7 @@ import { useImperativeHandle } from 'react'; function MyInput({ ref }) { useImperativeHandle(ref, () => { return { - // ... your methods ... + // ... các phương thức của bạn ... }; }, []); @@ -84,9 +84,9 @@ function MyInput({ ref }) { }; ``` -Note that in the code above, the `ref` is no longer passed to the `<input>`. +Lưu ý rằng trong đoạn code trên, `ref` không còn được truyền cho `<input>`. -For example, suppose you don't want to expose the entire `<input>` DOM node, but you want to expose two of its methods: `focus` and `scrollIntoView`. To do this, keep the real browser DOM in a separate ref. Then use `useImperativeHandle` to expose a handle with only the methods that you want the parent component to call: +Ví dụ: giả sử bạn không muốn hiển thị toàn bộ DOM node `<input>`, nhưng bạn muốn hiển thị hai trong số các phương thức của nó: `focus` và `scrollIntoView`. Để thực hiện việc này, hãy giữ DOM trình duyệt thực trong một ref riêng biệt. Sau đó, sử dụng `useImperativeHandle` để hiển thị một handle chỉ với các phương thức mà bạn muốn component cha gọi: ```js {7-14} import { useRef, useImperativeHandle } from 'react'; @@ -109,7 +109,7 @@ function MyInput({ ref }) { }; ``` -Now, if the parent component gets a ref to `MyInput`, it will be able to call the `focus` and `scrollIntoView` methods on it. However, it will not have full access to the underlying `<input>` DOM node. +Bây giờ, nếu component cha nhận được một ref đến `MyInput`, nó sẽ có thể gọi các phương thức `focus` và `scrollIntoView` trên đó. Tuy nhiên, nó sẽ không có toàn quyền truy cập vào DOM node `<input>` cơ bản. <Sandpack> @@ -122,15 +122,15 @@ export default function Form() { function handleClick() { ref.current.focus(); - // This won't work because the DOM node isn't exposed: + // Thao tác này sẽ không hoạt động vì DOM node không được hiển thị: // ref.current.style.opacity = 0.5; } return ( <form> - <MyInput placeholder="Enter your name" ref={ref} /> + <MyInput placeholder="Nhập tên của bạn" ref={ref} /> <button type="button" onClick={handleClick}> - Edit + Chỉnh sửa </button> </form> ); @@ -170,9 +170,9 @@ input { --- -### Exposing your own imperative methods {/*exposing-your-own-imperative-methods*/} +### Hiển thị các phương thức imperative của riêng bạn {/*exposing-your-own-imperative-methods*/} -The methods you expose via an imperative handle don't have to match the DOM methods exactly. For example, this `Post` component exposes a `scrollAndFocusAddComment` method via an imperative handle. This lets the parent `Page` scroll the list of comments *and* focus the input field when you click the button: +Các phương thức bạn hiển thị thông qua một imperative handle không nhất thiết phải khớp chính xác với các phương thức DOM. Ví dụ: component `Post` này hiển thị một phương thức `scrollAndFocusAddComment` thông qua một imperative handle. Điều này cho phép `Page` cha cuộn danh sách các comment *và* focus vào trường input khi bạn nhấp vào nút: <Sandpack> @@ -190,7 +190,7 @@ export default function Page() { return ( <> <button onClick={handleClick}> - Write a comment + Viết một bình luận </button> <Post ref={postRef} /> </> @@ -219,7 +219,7 @@ function Post({ ref }) { return ( <> <article> - <p>Welcome to my blog!</p> + <p>Chào mừng đến với blog của tôi!</p> </article> <CommentList ref={commentsRef} /> <AddComment ref={addCommentRef} /> @@ -265,7 +265,7 @@ export default CommentList; import { useRef, useImperativeHandle } from 'react'; function AddComment({ ref }) { - return <input placeholder="Add comment..." ref={ref} />; + return <input placeholder="Thêm bình luận..." ref={ref} />; } export default AddComment; @@ -285,8 +285,8 @@ export default AddComment; <Pitfall> -**Do not overuse refs.** You should only use refs for *imperative* behaviors that you can't express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on. +**Không nên lạm dụng refs.** Bạn chỉ nên sử dụng refs cho các hành vi *imperative* mà bạn không thể thể hiện dưới dạng props: ví dụ: cuộn đến một node, focus một node, kích hoạt một animation, chọn văn bản, v.v. -**If you can express something as a prop, you should not use a ref.** For example, instead of exposing an imperative handle like `{ open, close }` from a `Modal` component, it is better to take `isOpen` as a prop like `<Modal isOpen={isOpen} />`. [Effects](/learn/synchronizing-with-effects) can help you expose imperative behaviors via props. +**Nếu bạn có thể thể hiện một cái gì đó dưới dạng một prop, bạn không nên sử dụng ref.** Ví dụ: thay vì hiển thị một imperative handle như `{ open, close }` từ một component `Modal`, tốt hơn là lấy `isOpen` làm một prop như `<Modal isOpen={isOpen} />`. [Effects](/learn/synchronizing-with-effects) có thể giúp bạn hiển thị các hành vi imperative thông qua props. </Pitfall> diff --git a/src/content/reference/react/useInsertionEffect.md b/src/content/reference/react/useInsertionEffect.md index c088ac340..4ae6a0ee4 100644 --- a/src/content/reference/react/useInsertionEffect.md +++ b/src/content/reference/react/useInsertionEffect.md @@ -4,13 +4,13 @@ title: useInsertionEffect <Pitfall> -`useInsertionEffect` is for CSS-in-JS library authors. Unless you are working on a CSS-in-JS library and need a place to inject the styles, you probably want [`useEffect`](/reference/react/useEffect) or [`useLayoutEffect`](/reference/react/useLayoutEffect) instead. +`useInsertionEffect` dành cho các tác giả thư viện CSS-in-JS. Trừ khi bạn đang làm việc trên một thư viện CSS-in-JS và cần một nơi để chèn các kiểu, bạn có thể muốn sử dụng [`useEffect`](/reference/react/useEffect) hoặc [`useLayoutEffect`](/reference/react/useLayoutEffect) thay thế. </Pitfall> <Intro> -`useInsertionEffect` allows inserting elements into the DOM before any layout Effects fire. +`useInsertionEffect` cho phép chèn các phần tử vào DOM trước khi bất kỳ Effect bố cục nào được kích hoạt. ```js useInsertionEffect(setup, dependencies?) @@ -22,81 +22,82 @@ useInsertionEffect(setup, dependencies?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useInsertionEffect(setup, dependencies?)` {/*useinsertioneffect*/} -Call `useInsertionEffect` to insert styles before any Effects fire that may need to read layout: +Gọi `useInsertionEffect` để chèn các kiểu trước khi bất kỳ Effect nào cần đọc bố cục được kích hoạt: ```js import { useInsertionEffect } from 'react'; -// Inside your CSS-in-JS library +// Bên trong thư viện CSS-in-JS của bạn function useCSS(rule) { useInsertionEffect(() => { - // ... inject <style> tags here ... + // ... chèn các thẻ <style> ở đây ... }); return rule; } ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `setup`: The function with your Effect's logic. Your setup function may also optionally return a *cleanup* function. When your component is added to the DOM, but before any layout Effects fire, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. When your component is removed from the DOM, React will run your cleanup function. - -* **optional** `dependencies`: The list of all reactive values referenced inside of the `setup` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison algorithm. If you don't specify the dependencies at all, your Effect will re-run after every re-render of the component. +* `setup`: Hàm chứa logic Effect của bạn. Hàm thiết lập của bạn cũng có thể trả về một hàm *dọn dẹp* tùy chọn. Khi component của bạn được thêm vào DOM, nhưng trước khi bất kỳ Effect bố cục nào được kích hoạt, React sẽ chạy hàm thiết lập của bạn. Sau mỗi lần render lại với các dependency đã thay đổi, React sẽ chạy trước hàm dọn dẹp (nếu bạn đã cung cấp) với các giá trị cũ, và sau đó chạy hàm thiết lập của bạn với các giá trị mới. Khi component của bạn bị xóa khỏi DOM, React sẽ chạy hàm dọn dẹp của bạn. -#### Returns {/*returns*/} +* **tùy chọn** `dependencies`: Danh sách tất cả các giá trị phản ứng được tham chiếu bên trong code `setup`. Các giá trị phản ứng bao gồm props, state và tất cả các biến và hàm được khai báo trực tiếp bên trong phần thân component của bạn. Nếu trình lint của bạn được [cấu hình cho React](/learn/editor-setup#linting), nó sẽ xác minh rằng mọi giá trị phản ứng được chỉ định chính xác là một dependency. Danh sách các dependency phải có một số lượng mục không đổi và được viết nội dòng như `[dep1, dep2, dep3]`. React sẽ so sánh từng dependency với giá trị trước đó của nó bằng cách sử dụng thuật toán so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Nếu bạn không chỉ định các dependency nào cả, Effect của bạn sẽ chạy lại sau mỗi lần render lại của component. -`useInsertionEffect` returns `undefined`. +#### Giá trị trả về {/*returns*/} -#### Caveats {/*caveats*/} +`useInsertionEffect` trả về `undefined`. + +#### Lưu ý {/*caveats*/} + +* Các Effect chỉ chạy trên máy khách. Chúng không chạy trong quá trình render trên máy chủ. +* Bạn không thể cập nhật state từ bên trong `useInsertionEffect`. +* Vào thời điểm `useInsertionEffect` chạy, các ref chưa được đính kèm. +* `useInsertionEffect` có thể chạy trước hoặc sau khi DOM đã được cập nhật. Bạn không nên dựa vào việc DOM được cập nhật vào một thời điểm cụ thể nào. +* Không giống như các loại Effect khác, loại mà kích hoạt dọn dẹp cho mỗi Effect và sau đó thiết lập cho mỗi Effect, `useInsertionEffect` sẽ kích hoạt cả dọn dẹp và thiết lập một component tại một thời điểm. Điều này dẫn đến sự "xen kẽ" của các hàm dọn dẹp và thiết lập. -* Effects only run on the client. They don't run during server rendering. -* You can't update state from inside `useInsertionEffect`. -* By the time `useInsertionEffect` runs, refs are not attached yet. -* `useInsertionEffect` may run either before or after the DOM has been updated. You shouldn't rely on the DOM being updated at any particular time. -* Unlike other types of Effects, which fire cleanup for every Effect and then setup for every Effect, `useInsertionEffect` will fire both cleanup and setup one component at a time. This results in an "interleaving" of the cleanup and setup functions. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Injecting dynamic styles from CSS-in-JS libraries {/*injecting-dynamic-styles-from-css-in-js-libraries*/} +### Chèn các kiểu động từ các thư viện CSS-in-JS {/*injecting-dynamic-styles-from-css-in-js-libraries*/} -Traditionally, you would style React components using plain CSS. +Theo truyền thống, bạn sẽ tạo kiểu cho các component React bằng CSS thuần túy. ```js -// In your JS file: +// Trong file JS của bạn: <button className="success" /> -// In your CSS file: +// Trong file CSS của bạn: .success { color: green; } ``` -Some teams prefer to author styles directly in JavaScript code instead of writing CSS files. This usually requires using a CSS-in-JS library or a tool. There are three common approaches to CSS-in-JS: +Một số nhóm thích tạo kiểu trực tiếp trong code JavaScript thay vì viết các file CSS. Điều này thường đòi hỏi việc sử dụng một thư viện CSS-in-JS hoặc một công cụ. Có ba cách tiếp cận phổ biến đối với CSS-in-JS: -1. Static extraction to CSS files with a compiler -2. Inline styles, e.g. `<div style={{ opacity: 1 }}>` -3. Runtime injection of `<style>` tags +1. Trích xuất tĩnh sang các file CSS bằng trình biên dịch +2. Các kiểu nội dòng, ví dụ: `<div style={{ opacity: 1 }}>` +3. Chèn thẻ `<style>` lúc chạy -If you use CSS-in-JS, we recommend a combination of the first two approaches (CSS files for static styles, inline styles for dynamic styles). **We don't recommend runtime `<style>` tag injection for two reasons:** +Nếu bạn sử dụng CSS-in-JS, chúng tôi khuyên bạn nên kết hợp hai cách tiếp cận đầu tiên (các file CSS cho các kiểu tĩnh, các kiểu nội dòng cho các kiểu động). **Chúng tôi không khuyến nghị chèn thẻ `<style>` lúc chạy vì hai lý do:** -1. Runtime injection forces the browser to recalculate the styles a lot more often. -2. Runtime injection can be very slow if it happens at the wrong time in the React lifecycle. +1. Chèn lúc chạy buộc trình duyệt phải tính toán lại các kiểu thường xuyên hơn rất nhiều. +2. Chèn lúc chạy có thể rất chậm nếu nó xảy ra vào sai thời điểm trong vòng đời React. -The first problem is not solvable, but `useInsertionEffect` helps you solve the second problem. +Vấn đề đầu tiên là không thể giải quyết được, nhưng `useInsertionEffect` giúp bạn giải quyết vấn đề thứ hai. -Call `useInsertionEffect` to insert the styles before any layout Effects fire: +Gọi `useInsertionEffect` để chèn các kiểu trước khi bất kỳ Effect bố cục nào được kích hoạt: ```js {4-11} -// Inside your CSS-in-JS library +// Bên trong thư viện CSS-in-JS của bạn let isInserted = new Set(); function useCSS(rule) { useInsertionEffect(() => { - // As explained earlier, we don't recommend runtime injection of <style> tags. - // But if you have to do it, then it's important to do in useInsertionEffect. + // Như đã giải thích trước đó, chúng tôi không khuyến nghị chèn thẻ <style> lúc chạy. + // Nhưng nếu bạn phải làm điều đó, thì điều quan trọng là phải làm trong useInsertionEffect. if (!isInserted.has(rule)) { isInserted.add(rule); document.head.appendChild(getStyleForRule(rule)); @@ -111,7 +112,7 @@ function Button() { } ``` -Similarly to `useEffect`, `useInsertionEffect` does not run on the server. If you need to collect which CSS rules have been used on the server, you can do it during rendering: +Tương tự như `useEffect`, `useInsertionEffect` không chạy trên máy chủ. Nếu bạn cần thu thập các quy tắc CSS nào đã được sử dụng trên máy chủ, bạn có thể thực hiện việc đó trong quá trình render: ```js {1,4-6} let collectedRulesSet = new Set(); @@ -127,14 +128,14 @@ function useCSS(rule) { } ``` -[Read more about upgrading CSS-in-JS libraries with runtime injection to `useInsertionEffect`.](https://github.com/reactwg/react-18/discussions/110) +[Đọc thêm về việc nâng cấp các thư viện CSS-in-JS với việc chèn lúc chạy lên `useInsertionEffect`.](https://github.com/reactwg/react-18/discussions/110) <DeepDive> -#### How is this better than injecting styles during rendering or useLayoutEffect? {/*how-is-this-better-than-injecting-styles-during-rendering-or-uselayouteffect*/} +#### Làm thế nào điều này tốt hơn so với việc chèn các kiểu trong quá trình render hoặc useLayoutEffect? {/*how-is-this-better-than-injecting-styles-during-rendering-or-uselayouteffect*/} -If you insert styles during rendering and React is processing a [non-blocking update,](/reference/react/useTransition#marking-a-state-update-as-a-non-blocking-transition) the browser will recalculate the styles every single frame while rendering a component tree, which can be **extremely slow.** +Nếu bạn chèn các kiểu trong quá trình render và React đang xử lý một [bản cập nhật không chặn,](/reference/react/useTransition#marking-a-state-update-as-a-non-blocking-transition) trình duyệt sẽ tính toán lại các kiểu mỗi khung hình trong khi render một cây component, điều này có thể **cực kỳ chậm.** -`useInsertionEffect` is better than inserting styles during [`useLayoutEffect`](/reference/react/useLayoutEffect) or [`useEffect`](/reference/react/useEffect) because it ensures that by the time other Effects run in your components, the `<style>` tags have already been inserted. Otherwise, layout calculations in regular Effects would be wrong due to outdated styles. +`useInsertionEffect` tốt hơn so với việc chèn các kiểu trong [`useLayoutEffect`](/reference/react/useLayoutEffect) hoặc [`useEffect`](/reference/react/useEffect) vì nó đảm bảo rằng vào thời điểm các Effect khác chạy trong các component của bạn, các thẻ `<style>` đã được chèn. Nếu không, các tính toán bố cục trong các Effect thông thường sẽ bị sai do các kiểu đã lỗi thời. </DeepDive> diff --git a/src/content/reference/react/useLayoutEffect.md b/src/content/reference/react/useLayoutEffect.md index d38458f14..0f2b2d695 100644 --- a/src/content/reference/react/useLayoutEffect.md +++ b/src/content/reference/react/useLayoutEffect.md @@ -4,13 +4,13 @@ title: useLayoutEffect <Pitfall> -`useLayoutEffect` can hurt performance. Prefer [`useEffect`](/reference/react/useEffect) when possible. +`useLayoutEffect` có thể làm giảm hiệu năng. Ưu tiên sử dụng [`useEffect`](/reference/react/useEffect) khi có thể. </Pitfall> <Intro> -`useLayoutEffect` is a version of [`useEffect`](/reference/react/useEffect) that fires before the browser repaints the screen. +`useLayoutEffect` là một phiên bản của [`useEffect`](/reference/react/useEffect) được thực thi trước khi trình duyệt vẽ lại màn hình. ```js useLayoutEffect(setup, dependencies?) @@ -22,11 +22,11 @@ useLayoutEffect(setup, dependencies?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useLayoutEffect(setup, dependencies?)` {/*useinsertioneffect*/} -Call `useLayoutEffect` to perform the layout measurements before the browser repaints the screen: +Gọi `useLayoutEffect` để thực hiện các phép đo bố cục trước khi trình duyệt vẽ lại màn hình: ```js import { useState, useRef, useLayoutEffect } from 'react'; @@ -42,74 +42,73 @@ function Tooltip() { // ... ``` +[Xem thêm các ví dụ bên dưới.](#usage) -[See more examples below.](#usage) +#### Tham số {/*parameters*/} -#### Parameters {/*parameters*/} +* `setup`: Hàm chứa logic Effect của bạn. Hàm setup của bạn cũng có thể trả về một hàm *cleanup* (dọn dẹp) tùy chọn. Trước khi component của bạn được thêm vào DOM, React sẽ chạy hàm setup của bạn. Sau mỗi lần re-render với các dependencies đã thay đổi, React sẽ chạy hàm cleanup (nếu bạn cung cấp) với các giá trị cũ, và sau đó chạy hàm setup của bạn với các giá trị mới. Trước khi component của bạn bị xóa khỏi DOM, React sẽ chạy hàm cleanup của bạn. -* `setup`: The function with your Effect's logic. Your setup function may also optionally return a *cleanup* function. Before your component is added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. Before your component is removed from the DOM, React will run your cleanup function. - -* **optional** `dependencies`: The list of all reactive values referenced inside of the `setup` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. If you omit this argument, your Effect will re-run after every re-render of the component. +* **optional** `dependencies`: Danh sách tất cả các giá trị reactive được tham chiếu bên trong code `setup`. Các giá trị reactive bao gồm props, state, và tất cả các biến và hàm được khai báo trực tiếp bên trong phần thân component của bạn. Nếu trình lint của bạn được [cấu hình cho React](/learn/editor-setup#linting), nó sẽ xác minh rằng mọi giá trị reactive được chỉ định chính xác là một dependency. Danh sách các dependencies phải có một số lượng mục không đổi và được viết inline như `[dep1, dep2, dep3]`. React sẽ so sánh mỗi dependency với giá trị trước đó của nó bằng cách sử dụng phép so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Nếu bạn bỏ qua đối số này, Effect của bạn sẽ chạy lại sau mỗi lần re-render của component. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`useLayoutEffect` returns `undefined`. +`useLayoutEffect` trả về `undefined`. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useLayoutEffect` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a component and move the Effect there. +* `useLayoutEffect` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc các Hook của riêng bạn. Bạn không thể gọi nó bên trong các vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component và di chuyển Effect đến đó. -* When Strict Mode is on, React will **run one extra development-only setup+cleanup cycle** before the first real setup. This is a stress-test that ensures that your cleanup logic "mirrors" your setup logic and that it stops or undoes whatever the setup is doing. If this causes a problem, [implement the cleanup function.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +* Khi Strict Mode được bật, React sẽ **chạy thêm một chu kỳ setup+cleanup chỉ dành cho development** trước setup thực tế đầu tiên. Đây là một bài kiểm tra áp lực để đảm bảo rằng logic cleanup của bạn "phản ánh" logic setup của bạn và nó dừng hoặc hoàn tác bất cứ điều gì setup đang làm. Nếu điều này gây ra sự cố, [hãy triển khai hàm cleanup.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) -* If some of your dependencies are objects or functions defined inside the component, there is a risk that they will **cause the Effect to re-run more often than needed.** To fix this, remove unnecessary [object](/reference/react/useEffect#removing-unnecessary-object-dependencies) and [function](/reference/react/useEffect#removing-unnecessary-function-dependencies) dependencies. You can also [extract state updates](/reference/react/useEffect#updating-state-based-on-previous-state-from-an-effect) and [non-reactive logic](/reference/react/useEffect#reading-the-latest-props-and-state-from-an-effect) outside of your Effect. +* Nếu một số dependencies của bạn là các đối tượng hoặc hàm được xác định bên trong component, có một rủi ro là chúng sẽ **khiến Effect chạy lại thường xuyên hơn mức cần thiết.** Để khắc phục điều này, hãy loại bỏ các dependency [đối tượng](/reference/react/useEffect#removing-unnecessary-object-dependencies) và [hàm](/reference/react/useEffect#removing-unnecessary-function-dependencies) không cần thiết. Bạn cũng có thể [trích xuất các cập nhật state](/reference/react/useEffect#updating-state-based-on-previous-state-from-an-effect) và [logic non-reactive](/reference/react/useEffect#reading-the-latest-props-and-state-from-an-effect) ra khỏi Effect của bạn. -* Effects **only run on the client.** They don't run during server rendering. +* Các Effect **chỉ chạy trên client.** Chúng không chạy trong quá trình server rendering. -* The code inside `useLayoutEffect` and all state updates scheduled from it **block the browser from repainting the screen.** When used excessively, this makes your app slow. When possible, prefer [`useEffect`.](/reference/react/useEffect) +* Code bên trong `useLayoutEffect` và tất cả các cập nhật state được lên lịch từ nó **ngăn trình duyệt vẽ lại màn hình.** Khi được sử dụng quá mức, điều này làm cho ứng dụng của bạn chậm. Khi có thể, hãy ưu tiên [`useEffect`.](/reference/react/useEffect) -* If you trigger a state update inside `useLayoutEffect`, React will execute all remaining Effects immediately including `useEffect`. +* Nếu bạn kích hoạt một cập nhật state bên trong `useLayoutEffect`, React sẽ thực thi tất cả các Effect còn lại ngay lập tức bao gồm cả `useEffect`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Measuring layout before the browser repaints the screen {/*measuring-layout-before-the-browser-repaints-the-screen*/} +### Đo lường bố cục trước khi trình duyệt vẽ lại màn hình {/*measuring-layout-before-the-browser-repaints-the-screen*/} -Most components don't need to know their position and size on the screen to decide what to render. They only return some JSX. Then the browser calculates their *layout* (position and size) and repaints the screen. +Hầu hết các component không cần biết vị trí và kích thước của chúng trên màn hình để quyết định những gì cần render. Chúng chỉ trả về một số JSX. Sau đó, trình duyệt tính toán *layout* (vị trí và kích thước) của chúng và vẽ lại màn hình. -Sometimes, that's not enough. Imagine a tooltip that appears next to some element on hover. If there's enough space, the tooltip should appear above the element, but if it doesn't fit, it should appear below. In order to render the tooltip at the right final position, you need to know its height (i.e. whether it fits at the top). +Đôi khi, điều đó là không đủ. Hãy tưởng tượng một tooltip xuất hiện bên cạnh một số phần tử khi di chuột qua. Nếu có đủ không gian, tooltip sẽ xuất hiện phía trên phần tử, nhưng nếu không vừa, nó sẽ xuất hiện bên dưới. Để render tooltip ở đúng vị trí cuối cùng, bạn cần biết chiều cao của nó (tức là nó có vừa ở trên cùng hay không). -To do this, you need to render in two passes: +Để làm điều này, bạn cần render trong hai lượt: -1. Render the tooltip anywhere (even with a wrong position). -2. Measure its height and decide where to place the tooltip. -3. Render the tooltip *again* in the correct place. +1. Render tooltip ở bất kỳ đâu (ngay cả với vị trí sai). +2. Đo chiều cao của nó và quyết định nơi đặt tooltip. +3. Render tooltip *lại* ở đúng vị trí. -**All of this needs to happen before the browser repaints the screen.** You don't want the user to see the tooltip moving. Call `useLayoutEffect` to perform the layout measurements before the browser repaints the screen: +**Tất cả những điều này cần phải xảy ra trước khi trình duyệt vẽ lại màn hình.** Bạn không muốn người dùng nhìn thấy tooltip di chuyển. Gọi `useLayoutEffect` để thực hiện các phép đo bố cục trước khi trình duyệt vẽ lại màn hình: ```js {5-8} function Tooltip() { const ref = useRef(null); - const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet + const [tooltipHeight, setTooltipHeight] = useState(0); // Bạn chưa biết chiều cao thực useLayoutEffect(() => { const { height } = ref.current.getBoundingClientRect(); - setTooltipHeight(height); // Re-render now that you know the real height + setTooltipHeight(height); // Re-render bây giờ bạn đã biết chiều cao thực }, []); - // ...use tooltipHeight in the rendering logic below... + // ...sử dụng tooltipHeight trong logic rendering bên dưới... } ``` -Here's how this works step by step: +Đây là cách nó hoạt động từng bước: -1. `Tooltip` renders with the initial `tooltipHeight = 0` (so the tooltip may be wrongly positioned). -2. React places it in the DOM and runs the code in `useLayoutEffect`. -3. Your `useLayoutEffect` [measures the height](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) of the tooltip content and triggers an immediate re-render. -4. `Tooltip` renders again with the real `tooltipHeight` (so the tooltip is correctly positioned). -5. React updates it in the DOM, and the browser finally displays the tooltip. +1. `Tooltip` render với `tooltipHeight` ban đầu là `0` (vì vậy tooltip có thể được định vị sai). +2. React đặt nó vào DOM và chạy code trong `useLayoutEffect`. +3. `useLayoutEffect` của bạn [đo chiều cao](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) của nội dung tooltip và kích hoạt re-render ngay lập tức. +4. `Tooltip` render lại với `tooltipHeight` thực (vì vậy tooltip được định vị chính xác). +5. React cập nhật nó trong DOM và trình duyệt cuối cùng hiển thị tooltip. -Hover over the buttons below and see how the tooltip adjusts its position depending on whether it fits: +Di chuột qua các nút bên dưới và xem cách tooltip điều chỉnh vị trí của nó tùy thuộc vào việc nó có vừa hay không: <Sandpack> @@ -122,29 +121,29 @@ export default function App() { <ButtonWithTooltip tooltipContent={ <div> - This tooltip does not fit above the button. + Tooltip này không vừa phía trên nút. <br /> - This is why it's displayed below instead! + Đây là lý do tại sao nó được hiển thị bên dưới! </div> } > - Hover over me (tooltip above) + Di chuột qua tôi (tooltip phía trên) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> </div> ); @@ -208,7 +207,7 @@ export default function Tooltip({ children, targetRect }) { tooltipX = targetRect.left; tooltipY = targetRect.top - tooltipHeight; if (tooltipY < 0) { - // It doesn't fit above, so place below. + // Nó không vừa phía trên, vì vậy hãy đặt bên dưới. tooltipY = targetRect.bottom; } } @@ -253,13 +252,13 @@ export default function TooltipContainer({ children, x, y, contentRef }) { </Sandpack> -Notice that even though the `Tooltip` component has to render in two passes (first, with `tooltipHeight` initialized to `0` and then with the real measured height), you only see the final result. This is why you need `useLayoutEffect` instead of [`useEffect`](/reference/react/useEffect) for this example. Let's look at the difference in detail below. +Lưu ý rằng mặc dù component `Tooltip` phải render trong hai lượt (đầu tiên, với `tooltipHeight` được khởi tạo thành `0` và sau đó với chiều cao đo được thực tế), bạn chỉ thấy kết quả cuối cùng. Đây là lý do tại sao bạn cần `useLayoutEffect` thay vì [`useEffect`](/reference/react/useEffect) cho ví dụ này. Hãy xem sự khác biệt chi tiết bên dưới. <Recipes titleText="useLayoutEffect vs useEffect" titleId="examples"> -#### `useLayoutEffect` blocks the browser from repainting {/*uselayouteffect-blocks-the-browser-from-repainting*/} +#### `useLayoutEffect` ngăn trình duyệt vẽ lại {/*uselayouteffect-blocks-the-browser-from-repainting*/} -React guarantees that the code inside `useLayoutEffect` and any state updates scheduled inside it will be processed **before the browser repaints the screen.** This lets you render the tooltip, measure it, and re-render the tooltip again without the user noticing the first extra render. In other words, `useLayoutEffect` blocks the browser from painting. +React đảm bảo rằng code bên trong `useLayoutEffect` và bất kỳ cập nhật state nào được lên lịch bên trong nó sẽ được xử lý **trước khi trình duyệt vẽ lại màn hình.** Điều này cho phép bạn render tooltip, đo nó và re-render tooltip lại mà người dùng không nhận thấy lần render thêm đầu tiên. Nói cách khác, `useLayoutEffect` ngăn trình duyệt vẽ. <Sandpack> @@ -272,29 +271,29 @@ export default function App() { <ButtonWithTooltip tooltipContent={ <div> - This tooltip does not fit above the button. + Tooltip này không vừa phía trên nút. <br /> - This is why it's displayed below instead! + Đây là lý do tại sao nó được hiển thị bên dưới! </div> } > - Hover over me (tooltip above) + Di chuột qua tôi (tooltip phía trên) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> </div> ); @@ -357,7 +356,7 @@ export default function Tooltip({ children, targetRect }) { tooltipX = targetRect.left; tooltipY = targetRect.top - tooltipHeight; if (tooltipY < 0) { - // It doesn't fit above, so place below. + // Nó không vừa phía trên, vì vậy hãy đặt bên dưới. tooltipY = targetRect.bottom; } } @@ -404,9 +403,9 @@ export default function TooltipContainer({ children, x, y, contentRef }) { <Solution /> -#### `useEffect` does not block the browser {/*useeffect-does-not-block-the-browser*/} +#### `useEffect` không ngăn trình duyệt {/*useeffect-does-not-block-the-browser*/} -Here is the same example, but with [`useEffect`](/reference/react/useEffect) instead of `useLayoutEffect`. If you're on a slower device, you might notice that sometimes the tooltip "flickers" and you briefly see its initial position before the corrected position. +Đây là cùng một ví dụ, nhưng với [`useEffect`](/reference/react/useEffect) thay vì `useLayoutEffect`. Nếu bạn đang sử dụng một thiết bị chậm hơn, bạn có thể nhận thấy rằng đôi khi tooltip "nhấp nháy" và bạn thấy vị trí ban đầu của nó trong thời gian ngắn trước vị trí đã sửa. <Sandpack> @@ -419,29 +418,29 @@ export default function App() { <ButtonWithTooltip tooltipContent={ <div> - This tooltip does not fit above the button. + Tooltip này không vừa phía trên nút. <br /> - This is why it's displayed below instead! + Đây là lý do tại sao nó được hiển thị bên dưới! </div> } > - Hover over me (tooltip above) + Di chuột qua tôi (tooltip phía trên) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> </div> ); @@ -504,7 +503,7 @@ export default function Tooltip({ children, targetRect }) { tooltipX = targetRect.left; tooltipY = targetRect.top - tooltipHeight; if (tooltipY < 0) { - // It doesn't fit above, so place below. + // Nó không vừa phía trên, vì vậy hãy đặt bên dưới. tooltipY = targetRect.bottom; } } @@ -549,7 +548,7 @@ export default function TooltipContainer({ children, x, y, contentRef }) { </Sandpack> -To make the bug easier to reproduce, this version adds an artificial delay during rendering. React will let the browser paint the screen before it processes the state update inside `useEffect`. As a result, the tooltip flickers: +Để giúp việc tái tạo lỗi dễ dàng hơn, phiên bản này thêm một độ trễ nhân tạo trong quá trình rendering. React sẽ cho phép trình duyệt vẽ màn hình trước khi nó xử lý cập nhật state bên trong `useEffect`. Do đó, tooltip nhấp nháy: <Sandpack> @@ -562,29 +561,29 @@ export default function App() { <ButtonWithTooltip tooltipContent={ <div> - This tooltip does not fit above the button. + Tooltip này không vừa phía trên nút. <br /> - This is why it's displayed below instead! + Đây là lý do tại sao nó được hiển thị bên dưới! </div> } > - Hover over me (tooltip above) + Di chuột qua tôi (tooltip phía trên) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> <div style={{ height: 50 }} /> <ButtonWithTooltip tooltipContent={ - <div>This tooltip fits above the button</div> + <div>Tooltip này vừa phía trên nút</div> } > - Hover over me (tooltip below) + Di chuột qua tôi (tooltip phía dưới) </ButtonWithTooltip> </div> ); @@ -636,10 +635,10 @@ export default function Tooltip({ children, targetRect }) { const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); - // This artificially slows down rendering + // Điều này làm chậm quá trình rendering một cách nhân tạo let now = performance.now(); while (performance.now() - now < 100) { - // Do nothing for a bit... + // Không làm gì cả trong một khoảng thời gian... } useEffect(() => { @@ -653,7 +652,7 @@ export default function Tooltip({ children, targetRect }) { tooltipX = targetRect.left; tooltipY = targetRect.top - tooltipHeight; if (tooltipY < 0) { - // It doesn't fit above, so place below. + // Nó không vừa phía trên, vì vậy hãy đặt bên dưới. tooltipY = targetRect.bottom; } } @@ -698,7 +697,7 @@ export default function TooltipContainer({ children, x, y, contentRef }) { </Sandpack> -Edit this example to `useLayoutEffect` and observe that it blocks the paint even if rendering is slowed down. +Chỉnh sửa ví dụ này thành `useLayoutEffect` và quan sát rằng nó ngăn chặn việc vẽ ngay cả khi quá trình rendering bị chậm lại. <Solution /> @@ -706,36 +705,36 @@ Edit this example to `useLayoutEffect` and observe that it blocks the paint even <Note> -Rendering in two passes and blocking the browser hurts performance. Try to avoid this when you can. +Rendering trong hai lượt và ngăn trình duyệt gây ảnh hưởng đến hiệu năng. Cố gắng tránh điều này khi bạn có thể. </Note> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I'm getting an error: "`useLayoutEffect` does nothing on the server" {/*im-getting-an-error-uselayouteffect-does-nothing-on-the-server*/} +### Tôi gặp lỗi: "`useLayoutEffect` không làm gì trên server" {/*im-getting-an-error-uselayouteffect-does-nothing-on-the-server*/} -The purpose of `useLayoutEffect` is to let your component [use layout information for rendering:](#measuring-layout-before-the-browser-repaints-the-screen) +Mục đích của `useLayoutEffect` là cho phép component của bạn [sử dụng thông tin bố cục để rendering:](#measuring-layout-before-the-browser-repaints-the-screen) -1. Render the initial content. -2. Measure the layout *before the browser repaints the screen.* -3. Render the final content using the layout information you've read. +1. Render nội dung ban đầu. +2. Đo bố cục *trước khi trình duyệt vẽ lại màn hình.* +3. Render nội dung cuối cùng bằng cách sử dụng thông tin bố cục bạn đã đọc. -When you or your framework uses [server rendering](/reference/react-dom/server), your React app renders to HTML on the server for the initial render. This lets you show the initial HTML before the JavaScript code loads. +Khi bạn hoặc framework của bạn sử dụng [server rendering](/reference/react-dom/server), ứng dụng React của bạn render thành HTML trên server cho lần render ban đầu. Điều này cho phép bạn hiển thị HTML ban đầu trước khi code JavaScript tải. -The problem is that on the server, there is no layout information. +Vấn đề là trên server, không có thông tin bố cục. -In the [earlier example](#measuring-layout-before-the-browser-repaints-the-screen), the `useLayoutEffect` call in the `Tooltip` component lets it position itself correctly (either above or below content) depending on the content height. If you tried to render `Tooltip` as a part of the initial server HTML, this would be impossible to determine. On the server, there is no layout yet! So, even if you rendered it on the server, its position would "jump" on the client after the JavaScript loads and runs. +Trong [ví dụ trước](#measuring-layout-before-the-browser-repaints-the-screen), lệnh gọi `useLayoutEffect` trong component `Tooltip` cho phép nó tự định vị chính xác (hoặc phía trên hoặc phía dưới nội dung) tùy thuộc vào chiều cao nội dung. Nếu bạn cố gắng render `Tooltip` như một phần của HTML server ban đầu, điều này sẽ không thể xác định được. Trên server, chưa có bố cục! Vì vậy, ngay cả khi bạn render nó trên server, vị trí của nó sẽ "nhảy" trên client sau khi JavaScript tải và chạy. -Usually, components that rely on layout information don't need to render on the server anyway. For example, it probably doesn't make sense to show a `Tooltip` during the initial render. It is triggered by a client interaction. +Thông thường, các component dựa vào thông tin bố cục không cần render trên server. Ví dụ: có lẽ không có ý nghĩa gì khi hiển thị `Tooltip` trong quá trình render ban đầu. Nó được kích hoạt bởi một tương tác của client. -However, if you're running into this problem, you have a few different options: +Tuy nhiên, nếu bạn đang gặp phải vấn đề này, bạn có một vài tùy chọn khác nhau: -- Replace `useLayoutEffect` with [`useEffect`.](/reference/react/useEffect) This tells React that it's okay to display the initial render result without blocking the paint (because the original HTML will become visible before your Effect runs). +- Thay thế `useLayoutEffect` bằng [`useEffect`.](/reference/react/useEffect) Điều này cho React biết rằng có thể hiển thị kết quả render ban đầu mà không cần chặn việc vẽ (vì HTML ban đầu sẽ hiển thị trước khi Effect của bạn chạy). -- Alternatively, [mark your component as client-only.](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) This tells React to replace its content up to the closest [`<Suspense>`](/reference/react/Suspense) boundary with a loading fallback (for example, a spinner or a glimmer) during server rendering. +- Ngoài ra, [đánh dấu component của bạn là chỉ dành cho client.](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) Điều này cho React biết thay thế nội dung của nó cho đến ranh giới [`<Suspense>`](/reference/react/Suspense) gần nhất bằng một fallback tải (ví dụ: một spinner hoặc một glimmer) trong quá trình server rendering. -- Alternatively, you can render a component with `useLayoutEffect` only after hydration. Keep a boolean `isMounted` state that's initialized to `false`, and set it to `true` inside a `useEffect` call. Your rendering logic can then be like `return isMounted ? <RealContent /> : <FallbackContent />`. On the server and during the hydration, the user will see `FallbackContent` which should not call `useLayoutEffect`. Then React will replace it with `RealContent` which runs on the client only and can include `useLayoutEffect` calls. +- Ngoài ra, bạn có thể render một component với `useLayoutEffect` chỉ sau khi hydration. Giữ một state boolean `isMounted` được khởi tạo thành `false` và đặt nó thành `true` bên trong một lệnh gọi `useEffect`. Logic rendering của bạn sau đó có thể giống như `return isMounted ? <RealContent /> : <FallbackContent />`. Trên server và trong quá trình hydration, người dùng sẽ thấy `FallbackContent` không nên gọi `useLayoutEffect`. Sau đó, React sẽ thay thế nó bằng `RealContent` chỉ chạy trên client và có thể bao gồm các lệnh gọi `useLayoutEffect`. -- If you synchronize your component with an external data store and rely on `useLayoutEffect` for different reasons than measuring layout, consider [`useSyncExternalStore`](/reference/react/useSyncExternalStore) instead which [supports server rendering.](/reference/react/useSyncExternalStore#adding-support-for-server-rendering) +- Nếu bạn đồng bộ hóa component của mình với một kho dữ liệu bên ngoài và dựa vào `useLayoutEffect` vì những lý do khác với đo lường bố cục, hãy cân nhắc [`useSyncExternalStore`](/reference/react/useSyncExternalStore) thay thế, [hỗ trợ server rendering.](/reference/react/useSyncExternalStore#adding-support-for-server-rendering) diff --git a/src/content/reference/react/useMemo.md b/src/content/reference/react/useMemo.md index 6bfaba8ee..19c0fb7ac 100644 --- a/src/content/reference/react/useMemo.md +++ b/src/content/reference/react/useMemo.md @@ -4,7 +4,7 @@ title: useMemo <Intro> -`useMemo` is a React Hook that lets you cache the result of a calculation between re-renders. +`useMemo` là một React Hook cho phép bạn lưu vào bộ nhớ cache kết quả của một phép tính giữa các lần render lại. ```js const cachedValue = useMemo(calculateValue, dependencies) @@ -16,11 +16,11 @@ const cachedValue = useMemo(calculateValue, dependencies) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useMemo(calculateValue, dependencies)` {/*usememo*/} -Call `useMemo` at the top level of your component to cache a calculation between re-renders: +Gọi `useMemo` ở cấp cao nhất của component để lưu vào bộ nhớ cache một phép tính giữa các lần render lại: ```js import { useMemo } from 'react'; @@ -34,39 +34,39 @@ function TodoList({ todos, tab }) { } ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `calculateValue`: The function calculating the value that you want to cache. It should be pure, should take no arguments, and should return a value of any type. React will call your function during the initial render. On next renders, React will return the same value again if the `dependencies` have not changed since the last render. Otherwise, it will call `calculateValue`, return its result, and store it so it can be reused later. +* `calculateValue`: Hàm tính toán giá trị mà bạn muốn lưu vào bộ nhớ cache. Nó phải là thuần túy, không có tham số và trả về một giá trị thuộc bất kỳ loại nào. React sẽ gọi hàm của bạn trong quá trình render ban đầu. Trong các lần render tiếp theo, React sẽ trả về lại giá trị tương tự nếu `dependencies` không thay đổi so với lần render cuối cùng. Nếu không, nó sẽ gọi `calculateValue`, trả về kết quả của nó và lưu trữ nó để có thể được sử dụng lại sau này. -* `dependencies`: The list of all reactive values referenced inside of the `calculateValue` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. +* `dependencies`: Danh sách tất cả các giá trị phản ứng được tham chiếu bên trong mã `calculateValue`. Các giá trị phản ứng bao gồm props, state và tất cả các biến và hàm được khai báo trực tiếp bên trong phần thân component của bạn. Nếu trình lint của bạn được [cấu hình cho React](/learn/editor-setup#linting), nó sẽ xác minh rằng mọi giá trị phản ứng được chỉ định chính xác là một dependency. Danh sách các dependency phải có một số lượng mục không đổi và được viết nội dòng như `[dep1, dep2, dep3]`. React sẽ so sánh từng dependency với giá trị trước đó của nó bằng cách sử dụng so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -On the initial render, `useMemo` returns the result of calling `calculateValue` with no arguments. +Trong lần render ban đầu, `useMemo` trả về kết quả của việc gọi `calculateValue` mà không có tham số. -During next renders, it will either return an already stored value from the last render (if the dependencies haven't changed), or call `calculateValue` again, and return the result that `calculateValue` has returned. +Trong các lần render tiếp theo, nó sẽ trả về một giá trị đã được lưu trữ từ lần render cuối cùng (nếu các dependency không thay đổi) hoặc gọi lại `calculateValue` và trả về kết quả mà `calculateValue` đã trả về. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useMemo` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. -* In Strict Mode, React will **call your calculation function twice** in order to [help you find accidental impurities.](#my-calculation-runs-twice-on-every-re-render) This is development-only behavior and does not affect production. If your calculation function is pure (as it should be), this should not affect your logic. The result from one of the calls will be ignored. -* React **will not throw away the cached value unless there is a specific reason to do that.** For example, in development, React throws away the cache when you edit the file of your component. Both in development and in production, React will throw away the cache if your component suspends during the initial mount. In the future, React may add more features that take advantage of throwing away the cache--for example, if React adds built-in support for virtualized lists in the future, it would make sense to throw away the cache for items that scroll out of the virtualized table viewport. This should be fine if you rely on `useMemo` solely as a performance optimization. Otherwise, a [state variable](/reference/react/useState#avoiding-recreating-the-initial-state) or a [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) may be more appropriate. +* `useMemo` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc các Hook của riêng bạn. Bạn không thể gọi nó bên trong các vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển state vào đó. +* Trong Strict Mode, React sẽ **gọi hàm tính toán của bạn hai lần** để [giúp bạn tìm ra các tạp chất vô tình.](#my-calculation-runs-twice-on-every-re-render) Đây là hành vi chỉ dành cho development và không ảnh hưởng đến production. Nếu hàm tính toán của bạn là thuần túy (như nó phải vậy), điều này sẽ không ảnh hưởng đến logic của bạn. Kết quả từ một trong các lệnh gọi sẽ bị bỏ qua. +* React **sẽ không loại bỏ giá trị được lưu trong bộ nhớ cache trừ khi có một lý do cụ thể để làm điều đó.** Ví dụ: trong quá trình development, React sẽ loại bỏ bộ nhớ cache khi bạn chỉnh sửa tệp của component. Cả trong development và production, React sẽ loại bỏ bộ nhớ cache nếu component của bạn tạm ngưng trong quá trình mount ban đầu. Trong tương lai, React có thể thêm nhiều tính năng hơn tận dụng việc loại bỏ bộ nhớ cache--ví dụ: nếu React thêm hỗ trợ tích hợp cho danh sách ảo hóa trong tương lai, thì việc loại bỏ bộ nhớ cache cho các mục cuộn ra khỏi khung nhìn của bảng ảo hóa sẽ có ý nghĩa. Điều này sẽ ổn nếu bạn chỉ dựa vào `useMemo` như một tối ưu hóa hiệu suất. Nếu không, một [biến state](/reference/react/useState#avoiding-recreating-the-initial-state) hoặc một [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) có thể phù hợp hơn. <Note> -Caching return values like this is also known as [*memoization*,](https://en.wikipedia.org/wiki/Memoization) which is why this Hook is called `useMemo`. +Việc lưu vào bộ nhớ cache các giá trị trả về như thế này còn được gọi là [*memoization*,](https://en.wikipedia.org/wiki/Memoization) đó là lý do tại sao Hook này được gọi là `useMemo`. </Note> --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Skipping expensive recalculations {/*skipping-expensive-recalculations*/} +### Bỏ qua các phép tính lại tốn kém {/*skipping-expensive-recalculations*/} -To cache a calculation between re-renders, wrap it in a `useMemo` call at the top level of your component: +Để lưu vào bộ nhớ cache một phép tính giữa các lần render lại, hãy bọc nó trong một lệnh gọi `useMemo` ở cấp cao nhất của component: ```js [[3, 4, "visibleTodos"], [1, 4, "() => filterTodos(todos, tab)"], [2, 4, "[todos, tab]"]] import { useMemo } from 'react'; @@ -77,20 +77,20 @@ function TodoList({ todos, tab, theme }) { } ``` -You need to pass two things to `useMemo`: +Bạn cần chuyển hai thứ cho `useMemo`: -1. A <CodeStep step={1}>calculation function</CodeStep> that takes no arguments, like `() =>`, and returns what you wanted to calculate. -2. A <CodeStep step={2}>list of dependencies</CodeStep> including every value within your component that's used inside your calculation. +1. Một <CodeStep step={1}>hàm tính toán</CodeStep> không có tham số, như `() =>`, và trả về những gì bạn muốn tính toán. +2. Một <CodeStep step={2}>danh sách các dependency</CodeStep> bao gồm mọi giá trị trong component của bạn được sử dụng bên trong phép tính của bạn. -On the initial render, the <CodeStep step={3}>value</CodeStep> you'll get from `useMemo` will be the result of calling your <CodeStep step={1}>calculation</CodeStep>. +Trong lần render ban đầu, <CodeStep step={3}>giá trị</CodeStep> bạn nhận được từ `useMemo` sẽ là kết quả của việc gọi <CodeStep step={1}>phép tính</CodeStep> của bạn. -On every subsequent render, React will compare the <CodeStep step={2}>dependencies</CodeStep> with the dependencies you passed during the last render. If none of the dependencies have changed (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useMemo` will return the value you already calculated before. Otherwise, React will re-run your calculation and return the new value. +Trong mỗi lần render tiếp theo, React sẽ so sánh <CodeStep step={2}>các dependency</CodeStep> với các dependency bạn đã chuyển trong lần render cuối cùng. Nếu không có dependency nào thay đổi (so sánh với [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useMemo` sẽ trả về giá trị bạn đã tính toán trước đó. Nếu không, React sẽ chạy lại phép tính của bạn và trả về giá trị mới. -In other words, `useMemo` caches a calculation result between re-renders until its dependencies change. +Nói cách khác, `useMemo` lưu vào bộ nhớ cache kết quả tính toán giữa các lần render lại cho đến khi các dependency của nó thay đổi. -**Let's walk through an example to see when this is useful.** +**Hãy xem qua một ví dụ để xem khi nào điều này hữu ích.** -By default, React will re-run the entire body of your component every time that it re-renders. For example, if this `TodoList` updates its state or receives new props from its parent, the `filterTodos` function will re-run: +Theo mặc định, React sẽ chạy lại toàn bộ phần thân component của bạn mỗi khi nó render lại. Ví dụ: nếu `TodoList` này cập nhật state của nó hoặc nhận các props mới từ cha mẹ của nó, hàm `filterTodos` sẽ chạy lại: ```js {2} function TodoList({ todos, tab, theme }) { @@ -99,21 +99,22 @@ function TodoList({ todos, tab, theme }) { } ``` -Usually, this isn't a problem because most calculations are very fast. However, if you're filtering or transforming a large array, or doing some expensive computation, you might want to skip doing it again if data hasn't changed. If both `todos` and `tab` are the same as they were during the last render, wrapping the calculation in `useMemo` like earlier lets you reuse `visibleTodos` you've already calculated before. +Thông thường, đây không phải là một vấn đề vì hầu hết các phép tính đều rất nhanh. Tuy nhiên, nếu bạn đang lọc hoặc chuyển đổi một mảng lớn hoặc thực hiện một số tính toán tốn kém, bạn có thể muốn bỏ qua việc thực hiện lại nếu dữ liệu không thay đổi. Nếu cả `todos` và `tab` đều giống như trong lần render cuối cùng, việc bọc phép tính trong `useMemo` như trước cho phép bạn sử dụng lại `visibleTodos` mà bạn đã tính toán trước đó. -This type of caching is called *[memoization.](https://en.wikipedia.org/wiki/Memoization)* +Loại lưu vào bộ nhớ cache này được gọi là *[memoization.](https://en.wikipedia.org/wiki/Memoization)* <Note> -**You should only rely on `useMemo` as a performance optimization.** If your code doesn't work without it, find the underlying problem and fix it first. Then you may add `useMemo` to improve performance. +**Bạn chỉ nên dựa vào `useMemo` như một tối ưu hóa hiệu suất.** Nếu mã của bạn không hoạt động nếu không có nó, hãy tìm vấn đề cơ bản và khắc phục nó trước. Sau đó, bạn có thể thêm `useMemo` để cải thiện hiệu suất. + </Note> <DeepDive> -#### How to tell if a calculation is expensive? {/*how-to-tell-if-a-calculation-is-expensive*/} +#### Làm thế nào để biết một phép tính có tốn kém hay 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 ngàn đối tượng, có lẽ nó không tốn kém. Nếu bạn muốn tự tin hơn, bạn có thể thêm một bản ghi console để đo thời gian dành cho một đoạn mã: ```js {1,3} console.time('filter array'); @@ -121,59 +122,59 @@ const visibleTodos = filterTodos(todos, tab); 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 bạn đang đo (ví dụ: nhập vào đầu vào). Sau đó, bạn sẽ thấy các bản ghi như `filter array: 0.15ms` trong bảng điều khiển của mình. Nếu tổng thời gian được ghi lại cộng lại thành một lượng đáng kể (ví dụ: `1ms` trở lên), có thể có ý nghĩa khi ghi nhớ 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 được ghi lại có giảm cho tương tác đó hay không: ```js console.time('filter array'); const visibleTodos = useMemo(() => { - return filterTodos(todos, tab); // Skipped if todos and tab haven't changed + return filterTodos(todos, tab); // Bỏ qua nếu todos và tab không thay đổi }, [todos, tab]); 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 khi 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 của bạn, vì vậy bạn nên kiểm tra hiệu suất với một độ chậm nhân tạo. Ví dụ: Chrome cung cấp 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. +Ngoài ra, hãy lưu ý rằng việc đo hiệu suất trong quá trình phát triển sẽ không cung cấp 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 thành phần render hai lần thay vì một lần.) Để có được thời gian chính xác nhất, hãy xây dựng ứng dụng của bạn để sản xuất và kiểm tra nó trên một thiết bị giống như thiết bị mà người dùng của bạn có. </DeepDive> <DeepDive> -#### Should you add useMemo everywhere? {/*should-you-add-usememo-everywhere*/} +#### Bạn có nên thêm useMemo ở mọi nơi không? {/*should-you-add-usememo-everywhere*/} -If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful. +Nếu ứng dụng của bạn giống như trang web này và hầu hết các tương tác đều thô (như thay thế một trang hoặc toàn bộ một phần), thì việc ghi nhớ thường là không cần thiết. Mặt khác, nếu ứng dụng của bạn giống một trình chỉnh sửa bản vẽ hơn và hầu hết các tương tác đều chi tiết (như di chuyển hình dạng), thì bạn có thể thấy việc ghi nhớ rất hữu ích. -Optimizing with `useMemo` is only valuable in a few cases: +Tối ưu hóa với `useMemo` chỉ có giá trị trong một vài trường hợp: -- The calculation you're putting in `useMemo` is noticeably slow, and its dependencies rarely change. -- You pass it as a prop to a component wrapped in [`memo`.](/reference/react/memo) You want to skip re-rendering if the value hasn't changed. Memoization lets your component re-render only when dependencies aren't the same. -- The value you're passing is later used as a dependency of some Hook. For example, maybe another `useMemo` calculation value depends on it. Or maybe you are depending on this value from [`useEffect.`](/reference/react/useEffect) +- Phép tính bạn đang đưa vào `useMemo` chậm đáng kể và các dependency của nó hiếm khi thay đổi. +- Bạn chuyển nó dưới dạng một prop cho một thành phần được bọc trong [`memo`.](/reference/react/memo) Bạn muốn bỏ qua việc render lại nếu giá trị không thay đổi. Việc ghi nhớ cho phép thành phần của bạn chỉ render lại khi các dependency không giống nhau. +- Giá trị bạn đang chuyển sau đó được sử dụng làm dependency của một Hook nào đó. Ví dụ: có thể một giá trị tính toán `useMemo` khác phụ thuộc vào nó. Hoặc có thể bạn đang phụ thuộc vào giá trị này từ [`useEffect.`](/reference/react/useEffect) -There is no benefit to wrapping a calculation in `useMemo` in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside of this approach is that code becomes less readable. Also, not all memoization is effective: a single value that's "always new" is enough to break memoization for an entire component. +Không có lợi ích gì khi bọc một phép tính trong `useMemo` trong các trường hợp khác. Cũng không có tác hại đáng kể nào khi làm như vậy, vì vậy một số nhóm chọn không nghĩ về các trường hợp riêng lẻ và ghi nhớ càng nhiều càng tốt. Nhược điểm của phương pháp này là mã trở nên khó đọc hơn. Ngoài ra, không phải tất cả các ghi nhớ đều hiệu quả: một giá trị duy nhất "luôn mới" là đủ để phá vỡ việc ghi nhớ cho toàn bộ một thành phần. -**In practice, you can make a lot of memoization unnecessary by following a few principles:** +**Trong thực tế, bạn có thể làm cho rất nhiều ghi nhớ trở nên không cần thiết bằng cách tuân theo một vài nguyên tắc:** -1. When a component visually wraps other components, let it [accept JSX as children.](/learn/passing-props-to-a-component#passing-jsx-as-children) This way, when the wrapper component updates its own state, React knows that its children don't need to re-render. -1. Prefer local state and don't [lift state up](/learn/sharing-state-between-components) any further than necessary. For example, don't keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library. -1. Keep your [rendering logic pure.](/learn/keeping-components-pure) If re-rendering a component causes a problem or produces some noticeable visual artifact, it's a bug in your component! Fix the bug instead of adding memoization. -1. Avoid [unnecessary Effects that update state.](/learn/you-might-not-need-an-effect) Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over. -1. Try to [remove unnecessary dependencies from your Effects.](/learn/removing-effect-dependencies) For example, instead of memoization, it's often simpler to move some object or a function inside an Effect or outside the component. +1. Khi một thành phần bao bọc trực quan các thành phần khác, hãy để nó [chấp nhận JSX làm children.](/learn/passing-props-to-a-component#passing-jsx-as-children) Bằng cách này, khi thành phần bao bọc cập nhật trạng thái của chính nó, React biết rằng các children của nó không cần phải render lại. +2. Ưu tiên trạng thái cục bộ và không [nâng trạng thái lên](/learn/sharing-state-between-components) xa hơn mức cần thiết. Ví dụ: không giữ trạng thái tạm thời như biểu mẫu và việc một mục có được di chuột hay không ở đầu cây của bạn hoặc trong một thư viện trạng thái toàn cục. +3. Giữ cho [logic render của bạn thuần túy.](/learn/keeping-components-pure) Nếu việc render lại một thành phần gây ra sự cố hoặc tạo ra một tạo tác trực quan đáng chú ý, đó là một lỗi trong thành phần của bạn! Hãy sửa lỗi thay vì thêm ghi nhớ. +4. Tránh [các Effect không cần thiết cập nhật trạng thái.](/learn/you-might-not-need-an-effect) Hầu hết các vấn đề về hiệu suất trong các ứng dụng React đều do chuỗi các bản cập nhật bắt nguồn từ các Effect khiến các thành phần của bạn render đi render lại. +5. Cố gắng [loại bỏ các dependency không cần thiết khỏi các Effect của bạn.](/learn/removing-effect-dependencies) Ví dụ: thay vì ghi nhớ, thường đơn giản hơn là di chuyển một số đối tượng hoặc một hàm vào bên trong một Effect hoặc bên ngoài thành phần. -If a specific interaction still feels laggy, [use the React Developer Tools profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) to see which components would benefit the most from memoization, and add memoization where needed. These principles make your components easier to debug and understand, so it's good to follow them in any case. In the long term, we're researching [doing granular memoization automatically](https://www.youtube.com/watch?v=lGEMwh32soc) to solve this once and for all. +Nếu một tương tác cụ thể vẫn cảm thấy chậm, [hãy sử dụng trình phân tích hiệu năng React Developer Tools](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) để xem thành phần nào sẽ được hưởng lợi nhiều nhất từ việc ghi nhớ và thêm ghi nhớ khi cần thiết. Các nguyên tắc này giúp các thành phần của bạn dễ gỡ lỗi và hiểu hơn, vì vậy tốt nhất là tuân theo chúng trong mọi trường hợp. Về lâu dài, chúng tôi đang nghiên cứu [tự động thực hiện ghi nhớ chi tiết](https://www.youtube.com/watch?v=lGEMwh32soc) để giải quyết vấn đề này một lần và mãi mãi. </DeepDive> -<Recipes titleText="The difference between useMemo and calculating a value directly" titleId="examples-recalculation"> +<Recipes titleText="Sự khác biệt giữa useMemo và tính toán trực tiếp một giá trị" titleId="examples-recalculation"> -#### Skipping recalculation with `useMemo` {/*skipping-recalculation-with-usememo*/} +#### Bỏ qua tính toán lại với `useMemo` {/*skipping-recalculation-with-usememo*/} -In this example, the `filterTodos` implementation is **artificially slowed down** so that you can see what happens when some JavaScript function you're calling during rendering is genuinely slow. Try switching the tabs and toggling the theme. +Trong ví dụ này, việc triển khai `filterTodos` **bị làm chậm một cách giả tạo** để bạn có thể thấy điều gì xảy ra khi một số hàm JavaScript bạn đang gọi trong quá trình render thực sự chậm. Hãy thử chuyển đổi các tab và bật tắt chủ đề. -Switching the tabs feels slow because it forces the slowed down `filterTodos` to re-execute. That's expected because the `tab` has changed, and so the entire calculation *needs* to re-run. (If you're curious why it runs twice, it's explained [here.](#my-calculation-runs-twice-on-every-re-render)) +Việc chuyển đổi các tab có cảm giác chậm vì nó buộc `filterTodos` bị làm chậm phải thực thi lại. Điều đó được mong đợi vì `tab` đã thay đổi và do đó toàn bộ phép tính *cần* phải chạy lại. (Nếu bạn tò mò tại sao nó chạy hai lần, nó được giải thích [ở đây.](#my-calculation-runs-twice-on-every-re-render)) -Toggle the theme. **Thanks to `useMemo`, it's fast despite the artificial slowdown!** The slow `filterTodos` call was skipped because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render. +Bật tắt chủ đề. **Nhờ `useMemo`, nó nhanh chóng mặc dù bị làm chậm nhân tạo!** Lệnh gọi `filterTodos` chậm đã bị bỏ qua vì cả `todos` và `tab` (mà bạn chuyển làm dependency cho `useMemo`) đều không thay đổi kể từ lần render cuối cùng. <Sandpack> @@ -230,7 +231,7 @@ export default function TodoList({ todos, theme, tab }) { ); return ( <div className={theme}> - <p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p> + <p><b>Lưu ý: <code>filterTodos</code> bị làm chậm một cách giả tạo!</b></p> <ul> {visibleTodos.map(todo => ( <li key={todo.id}> @@ -299,11 +300,11 @@ label { <Solution /> -#### Always recalculating a value {/*always-recalculating-a-value*/} +#### Luôn tính toán lại một giá trị {/*always-recalculating-a-value*/} -In this example, the `filterTodos` implementation is also **artificially slowed down** so that you can see what happens when some JavaScript function you're calling during rendering is genuinely slow. Try switching the tabs and toggling the theme. +Trong ví dụ này, việc triển khai `filterTodos` cũng **bị làm chậm một cách giả tạo** để bạn có thể thấy điều gì xảy ra khi một số hàm JavaScript bạn đang gọi trong quá trình render thực sự chậm. Hãy thử chuyển đổi các tab và bật tắt chủ đề. -Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so the artificially slowed down `filterTodos` gets called on every re-render. It is called even if only `theme` has changed. +Không giống như trong ví dụ trước, việc bật tắt chủ đề cũng chậm bây giờ! Điều này là do **không có lệnh gọi `useMemo` trong phiên bản này,** vì vậy `filterTodos` bị làm chậm một cách giả tạo được gọi trên mỗi lần render lại. Nó được gọi ngay cả khi chỉ có `theme` đã thay đổi. <Sandpack> @@ -357,7 +358,7 @@ export default function TodoList({ todos, theme, tab }) { return ( <div className={theme}> <ul> - <p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p> + <p><b>Lưu ý: <code>filterTodos</code> bị làm chậm một cách giả tạo!</b></p> {visibleTodos.map(todo => ( <li key={todo.id}> {todo.completed ? @@ -423,7 +424,7 @@ label { </Sandpack> -However, here is the same code **with the artificial slowdown removed.** Does the lack of `useMemo` feel noticeable or not? +Tuy nhiên, đây là cùng một mã **với độ chậm nhân tạo đã được loại bỏ.** Việc thiếu `useMemo` có cảm thấy đáng chú ý hay không? <Sandpack> @@ -538,9 +539,9 @@ label { </Sandpack> -Quite often, code without memoization works fine. If your interactions are fast enough, you might not need memoization. +Khá thường xuyên, mã không có ghi nhớ hoạt động tốt. Nếu các tương tác của bạn đủ nhanh, bạn có thể không cần ghi nhớ. -You can try increasing the number of todo items in `utils.js` and see how the behavior changes. This particular calculation wasn't very expensive to begin with, but if the number of todos grows significantly, most of the overhead will be in re-rendering rather than in the filtering. Keep reading below to see how you can optimize re-rendering with `useMemo`. +Bạn có thể thử tăng số lượng mục todo trong `utils.js` và xem hành vi thay đổi như thế nào. Phép tính cụ thể này không tốn kém lắm ngay từ đầu, nhưng nếu số lượng todo tăng lên đáng kể, hầu hết chi phí sẽ nằm ở việc render lại hơn là ở việc lọc. Hãy tiếp tục đọc bên dưới để xem cách bạn có thể tối ưu hóa việc render lại bằng `useMemo`. <Solution /> @@ -548,9 +549,9 @@ You can try increasing the number of todo items in `utils.js` and see how the be --- -### Skipping re-rendering of components {/*skipping-re-rendering-of-components*/} +### Bỏ qua việc render lại các thành phần {/*skipping-re-rendering-of-components*/} -In some cases, `useMemo` can also help you optimize performance of re-rendering child components. To illustrate this, let's say this `TodoList` component passes the `visibleTodos` as a prop to the child `List` component: +Trong một số trường hợp, `useMemo` cũng có thể giúp bạn tối ưu hóa hiệu suất của việc render lại các thành phần con. Để minh họa điều này, giả sử thành phần `TodoList` này chuyển `visibleTodos` làm một prop cho thành phần `List` con: ```js {5} export default function TodoList({ todos, tab, theme }) { @@ -563,9 +564,9 @@ export default function TodoList({ todos, tab, theme }) { } ``` -You've noticed that toggling the `theme` prop freezes the app for a moment, but if you remove `<List />` from your JSX, it feels fast. This tells you that it's worth trying to optimize the `List` component. +Bạn đã nhận thấy rằng việc bật tắt prop `theme` làm đóng băng ứng dụng trong một khoảnh khắc, nhưng nếu bạn xóa `<List />` khỏi JSX của mình, nó sẽ cảm thấy nhanh. Điều này cho bạn biết rằng đáng để thử tối ưu hóa thành phần `List`. -**By default, when a component re-renders, React re-renders all of its children recursively.** This is why, when `TodoList` re-renders with a different `theme`, the `List` component *also* re-renders. This is fine for components that don't require much calculation to re-render. But if you've verified that a re-render is slow, you can tell `List` to skip re-rendering when its props are the same as on last render by wrapping it in [`memo`:](/reference/react/memo) +**Theo mặc định, khi một thành phần render lại, React render lại tất cả các children của nó một cách đệ quy.** Đây là lý do tại sao, khi `TodoList` render lại với một `theme` khác, thành phần `List` *cũng* render lại. Điều này là tốt cho các thành phần không yêu cầu nhiều tính toán để render lại. Nhưng nếu bạn đã xác minh rằng việc render lại chậm, bạn có thể yêu cầu `List` bỏ qua việc render lại khi các prop của nó giống như trên lần render cuối cùng bằng cách bọc nó trong [`memo`:](/reference/react/memo) ```js {3,5} import { memo } from 'react'; @@ -575,47 +576,46 @@ const List = memo(function List({ items }) { }); ``` -**With this change, `List` will skip re-rendering if all of its props are the *same* as on the last render.** This is where caching the calculation becomes important! Imagine that you calculated `visibleTodos` without `useMemo`: +**Với thay đổi này, `List` sẽ bỏ qua việc render lại nếu tất cả các prop của nó *giống nhau* như trên lần render cuối cùng.** Đây là nơi việc lưu vào bộ nhớ cache tính toán trở nên quan trọng! Hãy tưởng tượng rằng bạn đã tính toán `visibleTodos` mà không có `useMemo`: ```js {2-3,6-7} export default function TodoList({ todos, tab, theme }) { - // Every time the theme changes, this will be a different array... + // Mỗi khi chủ đề thay đổi, đây sẽ là một mảng khác... const visibleTodos = filterTodos(todos, tab); return ( <div className={theme}> - {/* ... so List's props will never be the same, and it will re-render every time */} + {/* ... vì vậy các prop của List sẽ không bao giờ giống nhau và nó sẽ render lại mỗi lần */} <List items={visibleTodos} /> </div> ); } ``` -**In the above example, the `filterTodos` function always creates a *different* array,** similar to how the `{}` object literal always creates a new object. Normally, this wouldn't be a problem, but it means that `List` props will never be the same, and your [`memo`](/reference/react/memo) optimization won't work. This is where `useMemo` comes in handy: +**Trong ví dụ trên, hàm `filterTodos` luôn tạo ra một mảng *khác*,** tương tự như cách ký tự đối tượng `{}` luôn tạo ra một đối tượng mới. Thông thường, điều này sẽ không phải là một vấn đề, nhưng nó có nghĩa là các prop của `List` sẽ không bao giờ giống nhau và tối ưu hóa [`memo`](/reference/react/memo) của bạn sẽ không hoạt động. Đây là nơi `useMemo` пригодится: ```js {2-3,5,9-10} export default function TodoList({ todos, tab, theme }) { - // Tell React to cache your calculation between re-renders... + // Yêu cầu React lưu vào bộ nhớ cache tính toán của bạn giữa các lần render lại... const visibleTodos = useMemo( () => filterTodos(todos, tab), - [todos, tab] // ...so as long as these dependencies don't change... + [todos, tab] // ...miễn là các dependency này không thay đổi... ); return ( <div className={theme}> - {/* ...List will receive the same props and can skip re-rendering */} + {/* ...List sẽ nhận được các prop giống nhau và có thể bỏ qua việc render lại */} <List items={visibleTodos} /> </div> ); } ``` - -**By wrapping the `visibleTodos` calculation in `useMemo`, you ensure that it has the *same* value between the re-renders** (until dependencies change). You don't *have to* wrap a calculation in `useMemo` unless you do it for some specific reason. In this example, the reason is that you pass it to a component wrapped in [`memo`,](/reference/react/memo) and this lets it skip re-rendering. There are a few other reasons to add `useMemo` which are described further on this page. +**Bằng cách bọc tính toán `visibleTodos` trong `useMemo`, bạn đảm bảo rằng nó có giá trị *giống nhau* giữa các lần render lại** (cho đến khi các dependency thay đổi). Bạn không *phải* bọc một tính toán trong `useMemo` trừ khi bạn làm điều đó vì một lý do cụ thể nào đó. Trong ví dụ này, lý do là bạn chuyển nó cho một thành phần được bọc trong [`memo`,](/reference/react/memo) và điều này cho phép nó bỏ qua việc render lại. Có một vài lý do khác để thêm `useMemo` được mô tả thêm trên trang này. <DeepDive> -#### Memoizing individual JSX nodes {/*memoizing-individual-jsx-nodes*/} +#### Ghi nhớ các nút JSX riêng lẻ {/*memoizing-individual-jsx-nodes*/} -Instead of wrapping `List` in [`memo`](/reference/react/memo), you could wrap the `<List />` JSX node itself in `useMemo`: +Thay vì bọc `List` trong [`memo`](/reference/react/memo), bạn có thể bọc chính nút JSX `<List />` trong `useMemo`: ```js {3,6} export default function TodoList({ todos, tab, theme }) { @@ -629,25 +629,25 @@ export default function TodoList({ todos, tab, theme }) { } ``` -The behavior would be the same. If the `visibleTodos` haven't changed, `List` won't be re-rendered. +Hành vi sẽ giống nhau. Nếu `visibleTodos` không thay đổi, `List` sẽ không được render lại. -A JSX node like `<List items={visibleTodos} />` is an object like `{ type: List, props: { items: visibleTodos } }`. Creating this object is very cheap, but React doesn't know whether its contents is the same as last time or not. This is why by default, React will re-render the `List` component. +Một nút JSX như `<List items={visibleTodos} />` là một đối tượng như `{ type: List, props: { items: visibleTodos } }`. Việc tạo đối tượng này rất rẻ, nhưng React không biết liệu nội dung của nó có giống như lần trước hay không. Đây là lý do tại sao theo mặc định, React sẽ render lại thành phần `List`. -However, if React sees the same exact JSX as during the previous render, it won't try to re-render your component. This is because JSX nodes are [immutable.](https://en.wikipedia.org/wiki/Immutable_object) A JSX node object could not have changed over time, so React knows it's safe to skip a re-render. However, for this to work, the node has to *actually be the same object*, not merely look the same in code. This is what `useMemo` does in this example. +Tuy nhiên, nếu React thấy chính xác JSX giống như trong quá trình render trước đó, nó sẽ không cố gắng render lại thành phần của bạn. Điều này là do các nút JSX là [bất biến.](https://en.wikipedia.org/wiki/Immutable_object) Một đối tượng nút JSX không thể thay đổi theo thời gian, vì vậy React biết rằng việc bỏ qua render lại là an toàn. Tuy nhiên, để điều này hoạt động, nút phải *thực sự là cùng một đối tượng*, không chỉ trông giống nhau trong mã. Đây là những gì `useMemo` làm trong ví dụ này. -Manually wrapping JSX nodes into `useMemo` is not convenient. For example, you can't do this conditionally. This is usually why you would wrap components with [`memo`](/reference/react/memo) instead of wrapping JSX nodes. +Việc bọc thủ công các nút JSX vào `useMemo` không thuận tiện. Ví dụ: bạn không thể làm điều này có điều kiện. Đây thường là lý do tại sao bạn sẽ bọc các thành phần bằng [`memo`](/reference/react/memo) thay vì bọc các nút JSX. </DeepDive> -<Recipes titleText="The difference between skipping re-renders and always re-rendering" titleId="examples-rerendering"> +<Recipes titleText="Sự khác biệt giữa việc bỏ qua render lại và luôn render lại" titleId="examples-rerendering"> -#### Skipping re-rendering with `useMemo` and `memo` {/*skipping-re-rendering-with-usememo-and-memo*/} +#### Bỏ qua render lại với `useMemo` và `memo` {/*skipping-re-rendering-with-usememo-and-memo*/} -In this example, the `List` component is **artificially slowed down** so that you can see what happens when a React component you're rendering is genuinely slow. Try switching the tabs and toggling the theme. +Trong ví dụ này, thành phần `List` **bị làm chậm một cách giả tạo** để bạn có thể thấy điều gì xảy ra khi một thành phần React bạn đang render thực sự chậm. Hãy thử chuyển đổi các tab và bật tắt chủ đề. -Switching the tabs feels slow because it forces the slowed down `List` to re-render. That's expected because the `tab` has changed, and so you need to reflect the user's new choice on the screen. +Việc chuyển đổi các tab có cảm giác chậm vì nó buộc `List` bị làm chậm phải render lại. Điều đó được mong đợi vì `tab` đã thay đổi và do đó bạn cần phản ánh lựa chọn mới của người dùng trên màn hình. -Next, try toggling the theme. **Thanks to `useMemo` together with [`memo`](/reference/react/memo), it’s fast despite the artificial slowdown!** The `List` skipped re-rendering because the `visibleTodos` array has not changed since the last render. The `visibleTodos` array has not changed because both `todos` and `tab` (which you pass as dependencies to `useMemo`) haven't changed since the last render. +Tiếp theo, hãy thử bật tắt chủ đề. **Nhờ `useMemo` cùng với [`memo`](/reference/react/memo), nó nhanh chóng mặc dù bị làm chậm nhân tạo!** `List` đã bỏ qua việc render lại vì mảng `visibleTodos` không thay đổi kể từ lần render cuối cùng. Mảng `visibleTodos` không thay đổi vì cả `todos` và `tab` (mà bạn chuyển làm dependency cho `useMemo`) đều không thay đổi kể từ lần render cuối cùng. <Sandpack> @@ -785,11 +785,11 @@ label { <Solution /> -#### Always re-rendering a component {/*always-re-rendering-a-component*/} +#### Luôn luôn kết xuất lại một thành phần {/*always-re-rendering-a-component*/} -In this example, the `List` implementation is also **artificially slowed down** so that you can see what happens when some React component you're rendering is genuinely slow. Try switching the tabs and toggling the theme. +Trong ví dụ này, việc triển khai `List` cũng **bị làm chậm một cách giả tạo** để bạn có thể thấy điều gì xảy ra khi một thành phần React mà bạn đang kết xuất thực sự chậm. Hãy thử chuyển đổi các tab và bật tắt chủ đề. -Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useMemo` call in this version,** so the `visibleTodos` is always a different array, and the slowed down `List` component can't skip re-rendering. +Không giống như trong ví dụ trước, việc bật tắt chủ đề bây giờ cũng chậm! Điều này là do **không có lệnh gọi `useMemo` trong phiên bản này,** vì vậy `visibleTodos` luôn là một mảng khác và thành phần `List` bị chậm không thể bỏ qua việc kết xuất lại. <Sandpack> @@ -921,7 +921,7 @@ label { </Sandpack> -However, here is the same code **with the artificial slowdown removed.** Does the lack of `useMemo` feel noticeable or not? +Tuy nhiên, đây là cùng một mã **với độ chậm nhân tạo đã được loại bỏ.** Việc thiếu `useMemo` có cảm thấy đáng chú ý hay không? <Sandpack> @@ -1046,9 +1046,9 @@ label { </Sandpack> -Quite often, code without memoization works fine. If your interactions are fast enough, you don't need memoization. +Thông thường, mã không có memoization hoạt động tốt. Nếu các tương tác của bạn đủ nhanh, bạn không cần memoization. -Keep in mind that you need to run React in production mode, disable [React Developer Tools](/learn/react-developer-tools), and use devices similar to the ones your app's users have in order to get a realistic sense of what's actually slowing down your app. +Hãy nhớ rằng bạn cần chạy React ở chế độ production, tắt [React Developer Tools](/learn/react-developer-tools) và sử dụng các thiết bị tương tự như những thiết bị mà người dùng ứng dụng của bạn có để có được cảm giác thực tế về những gì thực sự làm chậm ứng dụng của bạn. <Solution /> @@ -1056,9 +1056,9 @@ Keep in mind that you need to run React in production mode, disable [React Devel --- -### Preventing an Effect from firing too often {/*preventing-an-effect-from-firing-too-often*/} +### Ngăn chặn một Effect kích hoạt quá thường xuyên {/*preventing-an-effect-from-firing-too-often*/} -Sometimes, you might want to use a value inside an [Effect:](/learn/synchronizing-with-effects) +Đôi khi, bạn có thể muốn sử dụng một giá trị bên trong một [Effect:](/learn/synchronizing-with-effects) ```js {4-7,10} function ChatRoom({ roomId }) { @@ -1075,7 +1075,7 @@ function ChatRoom({ roomId }) { // ... ``` -This creates a problem. [Every reactive value must be declared as a dependency of your Effect.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) However, if you declare `options` as a dependency, it will cause your Effect to constantly reconnect to the chat room: +Điều này tạo ra một vấn đề. [Mọi giá trị phản ứng phải được khai báo là một dependency của Effect của bạn.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Tuy nhiên, nếu bạn khai báo `options` là một dependency, nó sẽ khiến Effect của bạn liên tục kết nối lại với phòng chat: ```js {5} @@ -1083,11 +1083,11 @@ This creates a problem. [Every reactive value must be declared as a dependency o const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [options]); // 🔴 Problem: This dependency changes on every render + }, [options]); // 🔴 Vấn đề: Dependency này thay đổi trên mỗi lần render // ... ``` -To solve this, you can wrap the object you need to call from an Effect in `useMemo`: +Để giải quyết vấn đề này, bạn có thể bọc đối tượng bạn cần gọi từ một Effect trong `useMemo`: ```js {4-9,16} function ChatRoom({ roomId }) { @@ -1098,26 +1098,26 @@ function ChatRoom({ roomId }) { serverUrl: 'https://localhost:1234', roomId: roomId }; - }, [roomId]); // ✅ Only changes when roomId changes + }, [roomId]); // ✅ Chỉ thay đổi khi roomId thay đổi useEffect(() => { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [options]); // ✅ Only changes when options changes + }, [options]); // ✅ Chỉ thay đổi khi options thay đổi // ... ``` -This ensures that the `options` object is the same between re-renders if `useMemo` returns the cached object. +Điều này đảm bảo rằng đối tượng `options` là giống nhau giữa các lần render lại nếu `useMemo` trả về đối tượng được lưu trong bộ nhớ cache. -However, since `useMemo` is performance optimization, not a semantic guarantee, React may throw away the cached value if [there is a specific reason to do that](#caveats). This will also cause the effect to re-fire, **so it's even better to remove the need for a function dependency** by moving your object *inside* the Effect: +Tuy nhiên, vì `useMemo` là tối ưu hóa hiệu suất, không phải là một đảm bảo về ngữ nghĩa, React có thể loại bỏ giá trị được lưu trong bộ nhớ cache nếu [có một lý do cụ thể để làm điều đó](#caveats). Điều này cũng sẽ khiến effect kích hoạt lại, **vì vậy tốt hơn nữa là loại bỏ nhu cầu về một dependency hàm** bằng cách di chuyển đối tượng của bạn *vào bên trong* Effect: ```js {5-8,13} function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { - const options = { // ✅ No need for useMemo or object dependencies! + const options = { // ✅ Không cần useMemo hoặc các dependencies đối tượng! serverUrl: 'https://localhost:1234', roomId: roomId } @@ -1125,16 +1125,16 @@ function ChatRoom({ roomId }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ Only changes when roomId changes + }, [roomId]); // ✅ Chỉ thay đổi khi roomId thay đổi // ... ``` -Now your code is simpler and doesn't need `useMemo`. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) +Bây giờ mã của bạn đơn giản hơn và không cần `useMemo`. [Tìm hiểu thêm về việc loại bỏ các dependencies Effect.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) -### Memoizing a dependency of another Hook {/*memoizing-a-dependency-of-another-hook*/} +### Memoizing một dependency của một Hook khác {/*memoizing-a-dependency-of-another-hook*/} -Suppose you have a calculation that depends on an object created directly in the component body: +Giả sử bạn có một phép tính phụ thuộc vào một đối tượng được tạo trực tiếp trong phần thân của thành phần: ```js {2} function Dropdown({ allItems, text }) { @@ -1142,44 +1142,44 @@ function Dropdown({ allItems, text }) { const visibleItems = useMemo(() => { return searchItems(allItems, searchOptions); - }, [allItems, searchOptions]); // 🚩 Caution: Dependency on an object created in the component body + }, [allItems, searchOptions]); // 🚩 Thận trọng: Dependency trên một đối tượng được tạo trong phần thân của thành phần // ... ``` -Depending on an object like this defeats the point of memoization. When a component re-renders, all of the code directly inside the component body runs again. **The lines of code creating the `searchOptions` object will also run on every re-render.** Since `searchOptions` is a dependency of your `useMemo` call, and it's different every time, React knows the dependencies are different, and recalculate `searchItems` every time. +Việc phụ thuộc vào một đối tượng như thế này làm mất đi ý nghĩa của memoization. Khi một thành phần render lại, tất cả mã trực tiếp bên trong phần thân của thành phần sẽ chạy lại. **Các dòng mã tạo đối tượng `searchOptions` cũng sẽ chạy trên mỗi lần render lại.** Vì `searchOptions` là một dependency của lệnh gọi `useMemo` của bạn và nó khác nhau mỗi lần, React biết các dependency khác nhau và tính toán lại `searchItems` mỗi lần. -To fix this, you could memoize the `searchOptions` object *itself* before passing it as a dependency: +Để khắc phục điều này, bạn có thể memoize đối tượng `searchOptions` *trước* khi chuyển nó làm dependency: ```js {2-4} function Dropdown({ allItems, text }) { const searchOptions = useMemo(() => { return { matchMode: 'whole-word', text }; - }, [text]); // ✅ Only changes when text changes + }, [text]); // ✅ Chỉ thay đổi khi text thay đổi const visibleItems = useMemo(() => { return searchItems(allItems, searchOptions); - }, [allItems, searchOptions]); // ✅ Only changes when allItems or searchOptions changes + }, [allItems, searchOptions]); // ✅ Chỉ thay đổi khi allItems hoặc searchOptions thay đổi // ... ``` -In the example above, if the `text` did not change, the `searchOptions` object also won't change. However, an even better fix is to move the `searchOptions` object declaration *inside* of the `useMemo` calculation function: +Trong ví dụ trên, nếu `text` không thay đổi, đối tượng `searchOptions` cũng sẽ không thay đổi. Tuy nhiên, một cách khắc phục thậm chí còn tốt hơn là di chuyển khai báo đối tượng `searchOptions` *vào bên trong* hàm tính toán `useMemo`: ```js {3} function Dropdown({ allItems, text }) { const visibleItems = useMemo(() => { const searchOptions = { matchMode: 'whole-word', text }; return searchItems(allItems, searchOptions); - }, [allItems, text]); // ✅ Only changes when allItems or text changes + }, [allItems, text]); // ✅ Chỉ thay đổi khi allItems hoặc text thay đổi // ... ``` -Now your calculation depends on `text` directly (which is a string and can't "accidentally" become different). +Bây giờ phép tính của bạn phụ thuộc trực tiếp vào `text` (là một chuỗi và không thể "vô tình" trở nên khác biệt). --- -### Memoizing a function {/*memoizing-a-function*/} +### Memoizing một hàm {/*memoizing-a-function*/} -Suppose the `Form` component is wrapped in [`memo`.](/reference/react/memo) You want to pass a function to it as a prop: +Giả sử thành phần `Form` được bọc trong [`memo`.](/reference/react/memo) Bạn muốn chuyển một hàm cho nó dưới dạng một prop: ```js {2-7} export default function ProductPage({ productId, referrer }) { @@ -1194,9 +1194,9 @@ export default function ProductPage({ productId, referrer }) { } ``` -Just as `{}` creates a different object, function declarations like `function() {}` and expressions like `() => {}` produce a *different* function on every re-render. By itself, creating a new function is not a problem. This is not something to avoid! However, if the `Form` component is memoized, presumably you want to skip re-rendering it when no props have changed. A prop that is *always* different would defeat the point of memoization. +Giống như `{}` tạo ra một đối tượng khác, các khai báo hàm như `function() {}` và các biểu thức như `() => {}` tạo ra một hàm *khác* trên mỗi lần render lại. Bản thân việc tạo một hàm mới không phải là một vấn đề. Đây không phải là điều cần tránh! Tuy nhiên, nếu thành phần `Form` được memoize, có lẽ bạn muốn bỏ qua việc render lại nó khi không có prop nào thay đổi. Một prop *luôn* khác biệt sẽ làm mất đi ý nghĩa của memoization. -To memoize a function with `useMemo`, your calculation function would have to return another function: +Để memoize một hàm với `useMemo`, hàm tính toán của bạn sẽ phải trả về một hàm khác: ```js {2-3,8-9} export default function Page({ productId, referrer }) { @@ -1213,7 +1213,7 @@ export default function Page({ productId, referrer }) { } ``` -This looks clunky! **Memoizing functions is common enough that React has a built-in Hook specifically for that. Wrap your functions into [`useCallback`](/reference/react/useCallback) instead of `useMemo`** to avoid having to write an extra nested function: +Điều này trông vụng về! **Memoizing các hàm là đủ phổ biến để React có một Hook tích hợp dành riêng cho việc đó. Bọc các hàm của bạn vào [`useCallback`](/reference/react/useCallback) thay vì `useMemo`** để tránh phải viết một hàm lồng nhau bổ sung: ```js {2,7} export default function Page({ productId, referrer }) { @@ -1228,88 +1228,88 @@ export default function Page({ productId, referrer }) { } ``` -The two examples above are completely equivalent. The only benefit to `useCallback` is that it lets you avoid writing an extra nested function inside. It doesn't do anything else. [Read more about `useCallback`.](/reference/react/useCallback) +Hai ví dụ trên hoàn toàn tương đương. Lợi ích duy nhất của `useCallback` là nó cho phép bạn tránh viết một hàm lồng nhau bổ sung bên trong. Nó không làm bất cứ điều gì khác. [Đọc thêm về `useCallback`.](/reference/react/useCallback) --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### My calculation runs twice on every re-render {/*my-calculation-runs-twice-on-every-re-render*/} +### Phép tính của tôi chạy hai lần trên mỗi lần render lại {/*my-calculation-runs-twice-on-every-re-render*/} -In [Strict Mode](/reference/react/StrictMode), React will call some of your functions twice instead of once: +Trong [Strict Mode](/reference/react/StrictMode), React sẽ gọi một số hàm của bạn hai lần thay vì một lần: ```js {2,5,6} function TodoList({ todos, tab }) { - // This component function will run twice for every render. + // Hàm thành phần này sẽ chạy hai lần cho mỗi lần render. const visibleTodos = useMemo(() => { - // This calculation will run twice if any of the dependencies change. + // Phép tính này sẽ chạy hai lần nếu bất kỳ dependency nào thay đổi. return filterTodos(todos, tab); }, [todos, tab]); // ... ``` -This is expected and shouldn't break your code. +Điều này được mong đợi và không nên làm hỏng mã của bạn. -This **development-only** behavior helps you [keep components pure.](/learn/keeping-components-pure) React uses the result of one of the calls, and ignores the result of the other call. As long as your component and calculation functions are pure, this shouldn't affect your logic. However, if they are accidentally impure, this helps you notice and fix the mistake. +Hành vi **chỉ dành cho development** này giúp bạn [giữ cho các thành phần thuần túy.](/learn/keeping-components-pure) React sử dụng kết quả của một trong các lệnh gọi và bỏ qua kết quả của lệnh gọi kia. Miễn là thành phần và các hàm tính toán của bạn là thuần túy, điều này sẽ không ảnh hưởng đến logic của bạn. Tuy nhiên, nếu chúng vô tình không thuần túy, điều này sẽ giúp bạn nhận thấy và sửa chữa sai lầm. -For example, this impure calculation function mutates an array you received as a prop: +Ví dụ: hàm tính toán không thuần túy này làm thay đổi một mảng bạn nhận được dưới dạng một prop: ```js {2-3} const visibleTodos = useMemo(() => { - // 🚩 Mistake: mutating a prop + // 🚩 Sai lầm: làm thay đổi một prop todos.push({ id: 'last', text: 'Go for a walk!' }); const filtered = filterTodos(todos, tab); return filtered; }, [todos, tab]); ``` -React calls your function twice, so you'd notice the todo is added twice. Your calculation shouldn't change any existing objects, but it's okay to change any *new* objects you created during the calculation. For example, if the `filterTodos` function always returns a *different* array, you can mutate *that* array instead: +React gọi hàm của bạn hai lần, vì vậy bạn sẽ nhận thấy todo được thêm hai lần. Phép tính của bạn không được thay đổi bất kỳ đối tượng hiện có nào, nhưng bạn có thể thay đổi bất kỳ đối tượng *mới* nào bạn đã tạo trong quá trình tính toán. Ví dụ: nếu hàm `filterTodos` luôn trả về một mảng *khác*, bạn có thể thay đổi *mảng đó* thay thế: ```js {3,4} const visibleTodos = useMemo(() => { const filtered = filterTodos(todos, tab); - // ✅ Correct: mutating an object you created during the calculation + // ✅ Chính xác: làm thay đổi một đối tượng bạn đã tạo trong quá trình tính toán filtered.push({ id: 'last', text: 'Go for a walk!' }); return filtered; }, [todos, tab]); ``` -Read [keeping components pure](/learn/keeping-components-pure) to learn more about purity. +Đọc [giữ cho các thành phần thuần túy](/learn/keeping-components-pure) để tìm hiểu thêm về tính thuần túy. -Also, check out the guides on [updating objects](/learn/updating-objects-in-state) and [updating arrays](/learn/updating-arrays-in-state) without mutation. +Ngoài ra, hãy xem các hướng dẫn về [cập nhật các đối tượng](/learn/updating-objects-in-state) và [cập nhật các mảng](/learn/updating-arrays-in-state) mà không cần thay đổi. --- -### My `useMemo` call is supposed to return an object, but returns undefined {/*my-usememo-call-is-supposed-to-return-an-object-but-returns-undefined*/} +### Lệnh gọi `useMemo` của tôi được cho là trả về một đối tượng, nhưng trả về undefined {/*my-usememo-call-is-supposed-to-return-an-object-but-returns-undefined*/} -This code doesn't work: +Mã này không hoạt động: ```js {1-2,5} - // 🔴 You can't return an object from an arrow function with () => { + // 🔴 Bạn không thể trả về một đối tượng từ một hàm mũi tên với () => { const searchOptions = useMemo(() => { matchMode: 'whole-word', text: text }, [text]); ``` -In JavaScript, `() => {` starts the arrow function body, so the `{` brace is not a part of your object. This is why it doesn't return an object, and leads to mistakes. You could fix it by adding parentheses like `({` and `})`: +Trong JavaScript, `() => {` bắt đầu phần thân của hàm mũi tên, vì vậy dấu ngoặc nhọn `{` không phải là một phần của đối tượng của bạn. Đây là lý do tại sao nó không trả về một đối tượng và dẫn đến sai lầm. Bạn có thể sửa nó bằng cách thêm dấu ngoặc đơn như `({` và `})`: ```js {1-2,5} - // This works, but is easy for someone to break again + // Điều này hoạt động, nhưng rất dễ để ai đó phá vỡ lại const searchOptions = useMemo(() => ({ matchMode: 'whole-word', text: text }), [text]); ``` -However, this is still confusing and too easy for someone to break by removing the parentheses. +Tuy nhiên, điều này vẫn gây nhầm lẫn và quá dễ để ai đó phá vỡ bằng cách xóa dấu ngoặc đơn. -To avoid this mistake, write a `return` statement explicitly: +Để tránh sai lầm này, hãy viết một câu lệnh `return` một cách rõ ràng: ```js {1-3,6-7} - // ✅ This works and is explicit + // ✅ Điều này hoạt động và rõ ràng const searchOptions = useMemo(() => { return { matchMode: 'whole-word', @@ -1320,57 +1320,57 @@ To avoid this mistake, write a `return` statement explicitly: --- -### Every time my component renders, the calculation in `useMemo` re-runs {/*every-time-my-component-renders-the-calculation-in-usememo-re-runs*/} +### Mỗi khi thành phần của tôi render, phép tính trong `useMemo` chạy lại {/*every-time-my-component-renders-the-calculation-in-usememo-re-runs*/} -Make sure you've specified the dependency array as a second argument! +Đảm bảo rằng bạn đã chỉ định mảng dependency làm đối số thứ hai! -If you forget the dependency array, `useMemo` will re-run the calculation every time: +Nếu bạn quên mảng dependency, `useMemo` sẽ chạy lại phép tính mỗi lần: ```js {2-3} function TodoList({ todos, tab }) { - // 🔴 Recalculates every time: no dependency array + // 🔴 Tính toán lại mỗi lần: không có mảng dependency const visibleTodos = useMemo(() => filterTodos(todos, tab)); // ... ``` -This is the corrected version passing the dependency array as a second argument: +Đây là phiên bản đã sửa, chuyển mảng dependency làm đối số thứ hai: ```js {2-3} function TodoList({ todos, tab }) { - // ✅ Does not recalculate unnecessarily + // ✅ Không tính toán lại một cách không cần thiết const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); // ... ``` -If this doesn't help, then the problem is that at least one of your dependencies is different from the previous render. You can debug this problem by manually logging your dependencies to the console: +Nếu điều này không giúp ích, thì vấn đề là ít nhất một trong các dependency của bạn khác với lần render trước. Bạn có thể gỡ lỗi vấn đề này bằng cách ghi nhật ký các dependency của bạn vào bảng điều khiển theo cách thủ công: ```js const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]); console.log([todos, tab]); ``` -You can then right-click on the arrays from different re-renders in the console and select "Store as a global variable" for both of them. Assuming the first one got saved as `temp1` and the second one got saved as `temp2`, you can then use the browser console to check whether each dependency in both arrays is the same: +Sau đó, bạn có thể nhấp chuột phải vào các mảng từ các lần render lại khác nhau trong bảng điều khiển và chọn "Store as a global variable" cho cả hai. Giả sử cái đầu tiên được lưu dưới dạng `temp1` và cái thứ hai được lưu dưới dạng `temp2`, sau đó bạn có thể sử dụng bảng điều khiển của trình duyệt để kiểm tra xem mỗi dependency trong cả hai mảng có giống nhau hay không: ```js -Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays? -Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays? -Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ... +Object.is(temp1[0], temp2[0]); // Dependency đầu tiên có giống nhau giữa các mảng không? +Object.is(temp1[1], temp2[1]); // Dependency thứ hai có giống nhau giữa các mảng không? +Object.is(temp1[2], temp2[2]); // ... và cứ thế cho mọi dependency ... ``` -When you find which dependency breaks memoization, either find a way to remove it, or [memoize it as well.](#memoizing-a-dependency-of-another-hook) +Khi bạn tìm thấy dependency nào phá vỡ memoization, hãy tìm cách loại bỏ nó hoặc [memoize nó.](#memoizing-a-dependency-of-another-hook) --- -### I need to call `useMemo` for each list item in a loop, but it's not allowed {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/} +### Tôi cần gọi `useMemo` cho mỗi mục danh sách trong một vòng lặp, nhưng nó không được phép {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/} -Suppose the `Chart` component is wrapped in [`memo`](/reference/react/memo). You want to skip re-rendering every `Chart` in the list when the `ReportList` component re-renders. However, you can't call `useMemo` in a loop: +Giả sử thành phần `Chart` được bọc trong [`memo`.](/reference/react/memo) Bạn muốn bỏ qua việc render lại mọi `Chart` trong danh sách khi thành phần `ReportList` render lại. Tuy nhiên, bạn không thể gọi `useMemo` trong một vòng lặp: ```js {5-11} function ReportList({ items }) { return ( <article> {items.map(item => { - // 🔴 You can't call useMemo in a loop like this: + // 🔴 Bạn không thể gọi useMemo trong một vòng lặp như thế này: const data = useMemo(() => calculateReport(item), [item]); return ( <figure key={item.id}> @@ -1383,7 +1383,7 @@ function ReportList({ items }) { } ``` -Instead, extract a component for each item and memoize data for individual items: +Thay vào đó, hãy trích xuất một thành phần cho mỗi mục và memoize dữ liệu cho các mục riêng lẻ: ```js {5,12-18} function ReportList({ items }) { @@ -1397,7 +1397,7 @@ function ReportList({ items }) { } function Report({ item }) { - // ✅ Call useMemo at the top level: + // ✅ Gọi useMemo ở cấp cao nhất: const data = useMemo(() => calculateReport(item), [item]); return ( <figure> @@ -1407,7 +1407,22 @@ function Report({ item }) { } ``` -Alternatively, you could remove `useMemo` and instead wrap `Report` itself in [`memo`.](/reference/react/memo) If the `item` prop does not change, `Report` will skip re-rendering, so `Chart` will skip re-rendering too: +Ngoài ra, bạn có thể xóa `useMemo` và thay vào đó bọc chính `Report` trong [`memo`.](/reference/react/memo) Nếu prop `item` không thay đổi, `Report` sẽ bỏ qua việc render lại, vì vậy `Chart` cũng sẽ bỏ qua việc render lại: + +```js {5,6,12} +function ReportList({ items }) { + // ... +} + +const Report = memo(function Report({ item }) { + const data = calculateReport(item); + return ( + <figure> + <Chart data={data} /> + </figure> + ); +}); +``` ```js {5,6,12} function ReportList({ items }) { diff --git a/src/content/reference/react/useOptimistic.md b/src/content/reference/react/useOptimistic.md index d191bbb55..4e41bde1b 100644 --- a/src/content/reference/react/useOptimistic.md +++ b/src/content/reference/react/useOptimistic.md @@ -4,7 +4,7 @@ title: useOptimistic <Intro> -`useOptimistic` is a React Hook that lets you optimistically update the UI. +`useOptimistic` là một React Hook cho phép bạn cập nhật giao diện người dùng một cách lạc quan. ```js const [optimisticState, addOptimistic] = useOptimistic(state, updateFn); @@ -16,13 +16,13 @@ title: useOptimistic --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useOptimistic(state, updateFn)` {/*use*/} -`useOptimistic` is a React Hook that lets you show a different state while an async action is underway. It accepts some state as an argument and returns a copy of that state that can be different during the duration of an async action such as a network request. You provide a function that takes the current state and the input to the action, and returns the optimistic state to be used while the action is pending. +`useOptimistic` là một React Hook cho phép bạn hiển thị một trạng thái khác trong khi một hành động không đồng bộ đang diễn ra. Nó chấp nhận một số trạng thái làm đối số và trả về một bản sao của trạng thái đó có thể khác trong suốt thời gian của một hành động không đồng bộ như một yêu cầu mạng. Bạn cung cấp một hàm lấy trạng thái hiện tại và đầu vào cho hành động, và trả về trạng thái lạc quan sẽ được sử dụng trong khi hành động đang chờ xử lý. -This state is called the "optimistic" state because it is usually used to immediately present the user with the result of performing an action, even though the action actually takes time to complete. +Trạng thái này được gọi là trạng thái "lạc quan" vì nó thường được sử dụng để ngay lập tức trình bày cho người dùng kết quả của việc thực hiện một hành động, mặc dù hành động đó thực sự mất thời gian để hoàn thành. ```js import { useOptimistic } from 'react'; @@ -32,39 +32,37 @@ function AppContainer() { state, // updateFn (currentState, optimisticValue) => { - // merge and return new state - // with optimistic value + // hợp nhất và trả về trạng thái mới + // với giá trị lạc quan } ); } ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `state`: the value to be returned initially and whenever no action is pending. -* `updateFn(currentState, optimisticValue)`: a function that takes the current state and the optimistic value passed to `addOptimistic` and returns the resulting optimistic state. It must be a pure function. `updateFn` takes in two parameters. The `currentState` and the `optimisticValue`. The return value will be the merged value of the `currentState` and `optimisticValue`. +* `state`: giá trị sẽ được trả về ban đầu và bất cứ khi nào không có hành động nào đang chờ xử lý. +* `updateFn(currentState, optimisticValue)`: một hàm lấy trạng thái hiện tại và giá trị lạc quan được truyền cho `addOptimistic` và trả về trạng thái lạc quan kết quả. Nó phải là một hàm thuần túy. `updateFn` nhận hai tham số. `currentState` và `optimisticValue`. Giá trị trả về sẽ là giá trị được hợp nhất của `currentState` và `optimisticValue`. +#### Giá trị trả về {/*returns*/} -#### Returns {/*returns*/} - -* `optimisticState`: The resulting optimistic state. It is equal to `state` unless an action is pending, in which case it is equal to the value returned by `updateFn`. -* `addOptimistic`: `addOptimistic` is the dispatching function to call when you have an optimistic update. It takes one argument, `optimisticValue`, of any type and will call the `updateFn` with `state` and `optimisticValue`. +* `optimisticState`: Trạng thái lạc quan kết quả. Nó bằng `state` trừ khi một hành động đang chờ xử lý, trong trường hợp đó nó bằng giá trị được trả về bởi `updateFn`. +* `addOptimistic`: `addOptimistic` là hàm điều phối để gọi khi bạn có một bản cập nhật lạc quan. Nó nhận một đối số, `optimisticValue`, thuộc bất kỳ loại nào và sẽ gọi `updateFn` với `state` và `optimisticValue`. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Optimistically updating forms {/*optimistically-updating-with-forms*/} +### Cập nhật lạc quan với biểu mẫu {/*optimistically-updating-with-forms*/} -The `useOptimistic` Hook provides a way to optimistically update the user interface before a background operation, like a network request, completes. In the context of forms, this technique helps to make apps feel more responsive. When a user submits a form, instead of waiting for the server's response to reflect the changes, the interface is immediately updated with the expected outcome. +Hook `useOptimistic` cung cấp một cách để cập nhật giao diện người dùng một cách lạc quan trước khi một hoạt động nền, như một yêu cầu mạng, hoàn thành. Trong ngữ cảnh của biểu mẫu, kỹ thuật này giúp làm cho ứng dụng có cảm giác phản hồi nhanh hơn. Khi người dùng gửi biểu mẫu, thay vì chờ phản hồi của máy chủ để phản ánh các thay đổi, giao diện sẽ được cập nhật ngay lập tức với kết quả dự kiến. -For example, when a user types a message into the form and hits the "Send" button, the `useOptimistic` Hook allows the message to immediately appear in the list with a "Sending..." label, even before the message is actually sent to a server. This "optimistic" approach gives the impression of speed and responsiveness. The form then attempts to truly send the message in the background. Once the server confirms the message has been received, the "Sending..." label is removed. +Ví dụ: khi người dùng nhập một tin nhắn vào biểu mẫu và nhấn nút "Gửi", Hook `useOptimistic` cho phép tin nhắn xuất hiện ngay lập tức trong danh sách với nhãn "Đang gửi...", ngay cả trước khi tin nhắn thực sự được gửi đến máy chủ. Cách tiếp cận "lạc quan" này tạo ấn tượng về tốc độ và khả năng phản hồi. Sau đó, biểu mẫu cố gắng thực sự gửi tin nhắn trong nền. Khi máy chủ xác nhận rằng tin nhắn đã được nhận, nhãn "Đang gửi..." sẽ bị xóa. <Sandpack> - ```js src/App.js import { useOptimistic, useState, useRef } from "react"; import { deliverMessage } from "./actions.js"; @@ -92,12 +90,12 @@ function Thread({ messages, sendMessage }) { {optimisticMessages.map((message, index) => ( <div key={index}> {message.text} - {!!message.sending && <small> (Sending...)</small>} + {!!message.sending && <small> (Đang gửi...)</small>} </div> ))} <form action={formAction} ref={formRef}> - <input type="text" name="message" placeholder="Hello!" /> - <button type="submit">Send</button> + <input type="text" name="message" placeholder="Xin chào!" /> + <button type="submit">Gửi</button> </form> </> ); @@ -105,7 +103,7 @@ function Thread({ messages, sendMessage }) { export default function App() { const [messages, setMessages] = useState([ - { text: "Hello there!", sending: false, key: 1 } + { text: "Xin chào!", sending: false, key: 1 } ]); async function sendMessage(formData) { const sentMessage = await deliverMessage(formData.get("message")); @@ -122,5 +120,4 @@ export async function deliverMessage(message) { } ``` - </Sandpack> diff --git a/src/content/reference/react/useReducer.md b/src/content/reference/react/useReducer.md index ed3dc68c2..0cc3c69cc 100644 --- a/src/content/reference/react/useReducer.md +++ b/src/content/reference/react/useReducer.md @@ -4,7 +4,7 @@ title: useReducer <Intro> -`useReducer` is a React Hook that lets you add a [reducer](/learn/extracting-state-logic-into-a-reducer) to your component. +`useReducer` là một React Hook cho phép bạn thêm một [reducer](/learn/extracting-state-logic-into-a-reducer) vào component của bạn. ```js const [state, dispatch] = useReducer(reducer, initialArg, init?) @@ -16,11 +16,11 @@ const [state, dispatch] = useReducer(reducer, initialArg, init?) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useReducer(reducer, initialArg, init?)` {/*usereducer*/} -Call `useReducer` at the top level of your component to manage its state with a [reducer.](/learn/extracting-state-logic-into-a-reducer) +Gọi `useReducer` ở cấp cao nhất của component để quản lý trạng thái của nó bằng một [reducer.](/learn/extracting-state-logic-into-a-reducer) ```js import { useReducer } from 'react'; @@ -34,32 +34,32 @@ function MyComponent() { // ... ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `reducer`: The reducer function that specifies how the state gets updated. It must be pure, should take the state and action as arguments, and should return the next state. State and action can be of any types. -* `initialArg`: The value from which the initial state is calculated. It can be a value of any type. How the initial state is calculated from it depends on the next `init` argument. -* **optional** `init`: The initializer function that should return the initial state. If it's not specified, the initial state is set to `initialArg`. Otherwise, the initial state is set to the result of calling `init(initialArg)`. +* `reducer`: Hàm reducer chỉ định cách trạng thái được cập nhật. Nó phải là thuần túy, nhận trạng thái và action làm đối số và trả về trạng thái tiếp theo. Trạng thái và action có thể thuộc bất kỳ loại nào. +* `initialArg`: Giá trị từ đó trạng thái ban đầu được tính toán. Nó có thể là một giá trị của bất kỳ loại nào. Cách trạng thái ban đầu được tính toán từ nó phụ thuộc vào đối số `init` tiếp theo. +* **tùy chọn** `init`: Hàm khởi tạo nên trả về trạng thái ban đầu. Nếu nó không được chỉ định, trạng thái ban đầu được đặt thành `initialArg`. Nếu không, trạng thái ban đầu được đặt thành kết quả của việc gọi `init(initialArg)`. -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`useReducer` returns an array with exactly two values: +`useReducer` trả về một mảng với chính xác hai giá trị: -1. The current state. During the first render, it's set to `init(initialArg)` or `initialArg` (if there's no `init`). -2. The [`dispatch` function](#dispatch) that lets you update the state to a different value and trigger a re-render. +1. Trạng thái hiện tại. Trong quá trình render đầu tiên, nó được đặt thành `init(initialArg)` hoặc `initialArg` (nếu không có `init`). +2. Hàm [`dispatch`](#dispatch) cho phép bạn cập nhật trạng thái thành một giá trị khác và kích hoạt render lại. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useReducer` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. -* The `dispatch` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire. If the linter lets you omit a dependency without errors, it is safe to do. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) -* In Strict Mode, React will **call your reducer and initializer twice** in order to [help you find accidental impurities.](#my-reducer-or-initializer-function-runs-twice) This is development-only behavior and does not affect production. If your reducer and initializer are pure (as they should be), this should not affect your logic. The result from one of the calls is ignored. +* `useReducer` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc Hook của riêng bạn. Bạn không thể gọi nó bên trong các vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển trạng thái vào đó. +* Hàm `dispatch` có một định danh ổn định, vì vậy bạn sẽ thường thấy nó bị bỏ qua khỏi các dependencies của Effect, nhưng việc bao gồm nó sẽ không làm cho Effect kích hoạt. Nếu trình kiểm tra lỗi cho phép bạn bỏ qua một dependency mà không có lỗi, thì việc đó là an toàn. [Tìm hiểu thêm về việc loại bỏ các dependencies của Effect.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) +* Trong Strict Mode, React sẽ **gọi reducer và trình khởi tạo của bạn hai lần** để [giúp bạn tìm thấy các tạp chất vô tình.](#my-reducer-or-initializer-function-runs-twice) Đây là hành vi chỉ dành cho phát triển và không ảnh hưởng đến sản xuất. Nếu reducer và trình khởi tạo của bạn là thuần túy (như chúng phải vậy), điều này sẽ không ảnh hưởng đến logic của bạn. Kết quả từ một trong các lệnh gọi bị bỏ qua. --- -### `dispatch` function {/*dispatch*/} +### Hàm `dispatch` {/*dispatch*/} -The `dispatch` function returned by `useReducer` lets you update the state to a different value and trigger a re-render. You need to pass the action as the only argument to the `dispatch` function: +Hàm `dispatch` được trả về bởi `useReducer` cho phép bạn cập nhật trạng thái thành một giá trị khác và kích hoạt render lại. Bạn cần chuyển action làm đối số duy nhất cho hàm `dispatch`: ```js const [state, dispatch] = useReducer(reducer, { age: 42 }); @@ -69,31 +69,31 @@ function handleClick() { // ... ``` -React will set the next state to the result of calling the `reducer` function you've provided with the current `state` and the action you've passed to `dispatch`. +React sẽ đặt trạng thái tiếp theo thành kết quả của việc gọi hàm `reducer` mà bạn đã cung cấp với `state` hiện tại và action bạn đã chuyển cho `dispatch`. -#### Parameters {/*dispatch-parameters*/} +#### Tham số {/*dispatch-parameters*/} -* `action`: The action performed by the user. It can be a value of any type. By convention, an action is usually an object with a `type` property identifying it and, optionally, other properties with additional information. +* `action`: Hành động được thực hiện bởi người dùng. Nó có thể là một giá trị của bất kỳ loại nào. Theo quy ước, một action thường là một đối tượng có thuộc tính `type` xác định nó và, tùy chọn, các thuộc tính khác với thông tin bổ sung. -#### Returns {/*dispatch-returns*/} +#### Trả về {/*dispatch-returns*/} -`dispatch` functions do not have a return value. +Hàm `dispatch` không có giá trị trả về. -#### Caveats {/*setstate-caveats*/} +#### Lưu ý {/*setstate-caveats*/} -* The `dispatch` function **only updates the state variable for the *next* render**. If you read the state variable after calling the `dispatch` function, [you will still get the old value](#ive-dispatched-an-action-but-logging-gives-me-the-old-state-value) that was on the screen before your call. +* Hàm `dispatch` **chỉ cập nhật biến trạng thái cho lần render *tiếp theo***. Nếu bạn đọc biến trạng thái sau khi gọi hàm `dispatch`, [bạn vẫn sẽ nhận được giá trị cũ](#ive-dispatched-an-action-but-logging-gives-me-the-old-state-value) đã có trên màn hình trước khi bạn gọi. -* If the new value you provide is identical to the current `state`, as determined by an [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison, React will **skip re-rendering the component and its children.** This is an optimization. React may still need to call your component before ignoring the result, but it shouldn't affect your code. +* Nếu giá trị mới bạn cung cấp giống hệt với `state` hiện tại, như được xác định bởi so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is), React sẽ **bỏ qua việc render lại component và các component con của nó.** Đây là một tối ưu hóa. React vẫn có thể cần gọi component của bạn trước khi bỏ qua kết quả, nhưng nó không ảnh hưởng đến mã của bạn. -* React [batches state updates.](/learn/queueing-a-series-of-state-updates) It updates the screen **after all the event handlers have run** and have called their `set` functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use [`flushSync`.](/reference/react-dom/flushSync) +* React [gom các bản cập nhật trạng thái.](/learn/queueing-a-series-of-state-updates) Nó cập nhật màn hình **sau khi tất cả các trình xử lý sự kiện đã chạy** và đã gọi các hàm `set` của chúng. Điều này ngăn chặn nhiều lần render lại trong một sự kiện duy nhất. Trong trường hợp hiếm hoi bạn cần buộc React cập nhật màn hình sớm hơn, ví dụ: để truy cập DOM, bạn có thể sử dụng [`flushSync`.](/reference/react-dom/flushSync) --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Adding a reducer to a component {/*adding-a-reducer-to-a-component*/} +### Thêm một reducer vào một component {/*adding-a-reducer-to-a-component*/} -Call `useReducer` at the top level of your component to manage state with a [reducer.](/learn/extracting-state-logic-into-a-reducer) +Gọi `useReducer` ở cấp cao nhất của component để quản lý trạng thái bằng một [reducer.](/learn/extracting-state-logic-into-a-reducer) ```js [[1, 8, "state"], [2, 8, "dispatch"], [4, 8, "reducer"], [3, 8, "{ age: 42 }"]] import { useReducer } from 'react'; @@ -107,12 +107,12 @@ function MyComponent() { // ... ``` -`useReducer` returns an array with exactly two items: +`useReducer` trả về một mảng với chính xác hai mục: -1. The <CodeStep step={1}>current state</CodeStep> of this state variable, initially set to the <CodeStep step={3}>initial state</CodeStep> you provided. -2. The <CodeStep step={2}>`dispatch` function</CodeStep> that lets you change it in response to interaction. +1. <CodeStep step={1}>Trạng thái hiện tại</CodeStep> của biến trạng thái này, ban đầu được đặt thành <CodeStep step={3}>trạng thái ban đầu</CodeStep> mà bạn đã cung cấp. +2. Hàm <CodeStep step={2}>`dispatch`</CodeStep> cho phép bạn thay đổi nó để đáp ứng với tương tác. -To update what's on the screen, call <CodeStep step={2}>`dispatch`</CodeStep> with an object representing what the user did, called an *action*: +Để cập nhật những gì trên màn hình, hãy gọi <CodeStep step={2}>`dispatch`</CodeStep> với một đối tượng đại diện cho những gì người dùng đã làm, được gọi là một *action*: ```js [[2, 2, "dispatch"]] function handleClick() { @@ -120,7 +120,7 @@ function handleClick() { } ``` -React will pass the current state and the action to your <CodeStep step={4}>reducer function</CodeStep>. Your reducer will calculate and return the next state. React will store that next state, render your component with it, and update the UI. +React sẽ chuyển trạng thái hiện tại và action cho <CodeStep step={4}>hàm reducer</CodeStep> của bạn. Reducer của bạn sẽ tính toán và trả về trạng thái tiếp theo. React sẽ lưu trữ trạng thái tiếp theo đó, render component của bạn với nó và cập nhật UI. <Sandpack> @@ -158,13 +158,13 @@ button { display: block; margin-top: 10px; } </Sandpack> -`useReducer` is very similar to [`useState`](/reference/react/useState), but it lets you move the state update logic from event handlers into a single function outside of your component. Read more about [choosing between `useState` and `useReducer`.](/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer) +`useReducer` rất giống với [`useState`](/reference/react/useState), nhưng nó cho phép bạn di chuyển logic cập nhật trạng thái từ các trình xử lý sự kiện vào một hàm duy nhất bên ngoài component của bạn. Đọc thêm về [lựa chọn giữa `useState` và `useReducer`.](/learn/extracting-state-logic-into-a-reducer#comparing-usestate-and-usereducer) --- -### Writing the reducer function {/*writing-the-reducer-function*/} +### Viết hàm reducer {/*writing-the-reducer-function*/} -A reducer function is declared like this: +Một hàm reducer được khai báo như thế này: ```js function reducer(state, action) { @@ -172,7 +172,7 @@ function reducer(state, action) { } ``` -Then you need to fill in the code that will calculate and return the next state. By convention, it is common to write it as a [`switch` statement.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch) For each `case` in the `switch`, calculate and return some next state. +Sau đó, bạn cần điền vào mã sẽ tính toán và trả về trạng thái tiếp theo. Theo quy ước, nó thường được viết dưới dạng một câu lệnh [`switch`.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch) Đối với mỗi `case` trong `switch`, hãy tính toán và trả về một số trạng thái tiếp theo. ```js {4-7,10-13} function reducer(state, action) { @@ -194,7 +194,7 @@ function reducer(state, action) { } ``` -Actions can have any shape. By convention, it's common to pass objects with a `type` property identifying the action. It should include the minimal necessary information that the reducer needs to compute the next state. +Actions có thể có bất kỳ hình dạng nào. Theo quy ước, người ta thường truyền các đối tượng có thuộc tính `type` xác định action. Nó nên bao gồm thông tin cần thiết tối thiểu mà reducer cần để tính toán trạng thái tiếp theo. ```js {5,9-12} function Form() { @@ -213,31 +213,31 @@ function Form() { // ... ``` -The action type names are local to your component. [Each action describes a single interaction, even if that leads to multiple changes in data.](/learn/extracting-state-logic-into-a-reducer#writing-reducers-well) The shape of the state is arbitrary, but usually it'll be an object or an array. +Tên loại action là cục bộ đối với component của bạn. [Mỗi action mô tả một tương tác duy nhất, ngay cả khi điều đó dẫn đến nhiều thay đổi trong dữ liệu.](/learn/extracting-state-logic-into-a-reducer#writing-reducers-well) Hình dạng của trạng thái là tùy ý, nhưng thông thường nó sẽ là một đối tượng hoặc một mảng. -Read [extracting state logic into a reducer](/learn/extracting-state-logic-into-a-reducer) to learn more. +Đọc [trích xuất logic trạng thái vào một reducer](/learn/extracting-state-logic-into-a-reducer) để tìm hiểu thêm. <Pitfall> -State is read-only. Don't modify any objects or arrays in state: +Trạng thái là chỉ đọc. Không sửa đổi bất kỳ đối tượng hoặc mảng nào trong trạng thái: ```js {4,5} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // 🚩 Don't mutate an object in state like this: + // 🚩 Đừng đột biến một đối tượng trong trạng thái như thế này: state.age = state.age + 1; return state; } ``` -Instead, always return new objects from your reducer: +Thay vào đó, luôn trả về các đối tượng mới từ reducer của bạn: ```js {4-8} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // ✅ Instead, return a new object + // ✅ Thay vào đó, hãy trả về một đối tượng mới return { ...state, age: state.age + 1 @@ -245,15 +245,15 @@ function reducer(state, action) { } ``` -Read [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) to learn more. +Đọc [cập nhật các đối tượng trong state](/learn/updating-objects-in-state) và [cập nhật các mảng trong state](/learn/updating-arrays-in-state) để tìm hiểu thêm. </Pitfall> -<Recipes titleText="Basic useReducer examples" titleId="examples-basic"> +<Recipes titleText="Các ví dụ cơ bản về useReducer" titleId="examples-basic"> -#### Form (object) {/*form-object*/} +#### Biểu mẫu (đối tượng) {/*form-object*/} -In this example, the reducer manages a state object with two fields: `name` and `age`. +Trong ví dụ này, reducer quản lý một đối tượng trạng thái với hai trường: `name` và `age`. <Sandpack> @@ -317,9 +317,9 @@ button { display: block; margin-top: 10px; } <Solution /> -#### Todo list (array) {/*todo-list-array*/} +#### Danh sách việc cần làm (mảng) {/*todo-list-array*/} -In this example, the reducer manages an array of tasks. The array needs to be updated [without mutation.](/learn/updating-arrays-in-state) +Trong ví dụ này, reducer quản lý một mảng các nhiệm vụ. Mảng cần được cập nhật [mà không bị thay đổi.](/learn/updating-arrays-in-state) <Sandpack> @@ -510,9 +510,9 @@ ul, li { margin: 0; padding: 0; } <Solution /> -#### Writing concise update logic with Immer {/*writing-concise-update-logic-with-immer*/} +#### Viết logic cập nhật ngắn gọn với Immer {/*writing-concise-update-logic-with-immer*/} -If updating arrays and objects without mutation feels tedious, you can use a library like [Immer](https://github.com/immerjs/use-immer#useimmerreducer) to reduce repetitive code. Immer lets you write concise code as if you were mutating objects, but under the hood it performs immutable updates: +Nếu việc cập nhật các mảng và đối tượng mà không bị thay đổi cảm thấy tẻ nhạt, bạn có thể sử dụng một thư viện như [Immer](https://github.com/immerjs/use-immer#useimmerreducer) để giảm mã lặp đi lặp lại. Immer cho phép bạn viết mã ngắn gọn như thể bạn đang thay đổi các đối tượng, nhưng bên dưới nó thực hiện các cập nhật bất biến: <Sandpack> @@ -692,12 +692,6 @@ function Task({ task, onChange, onDelete }) { } ``` -```css -button { margin: 5px; } -li { list-style-type: none; } -ul, li { margin: 0; padding: 0; } -``` - ```json package.json { "dependencies": { @@ -724,9 +718,9 @@ ul, li { margin: 0; padding: 0; } --- -### Avoiding recreating the initial state {/*avoiding-recreating-the-initial-state*/} +### Tránh tạo lại trạng thái ban đầu {/*avoiding-recreating-the-initial-state*/} -React saves the initial state once and ignores it on the next renders. +React lưu trạng thái ban đầu một lần và bỏ qua nó trong các lần kết xuất tiếp theo. ```js function createInitialState(username) { @@ -736,11 +730,12 @@ function createInitialState(username) { function TodoList({ username }) { const [state, dispatch] = useReducer(reducer, createInitialState(username)); // ... +} ``` -Although the result of `createInitialState(username)` is only used for the initial render, you're still calling this function on every render. This can be wasteful if it's creating large arrays or performing expensive calculations. +Mặc dù kết quả của `createInitialState(username)` chỉ được sử dụng cho lần kết xuất ban đầu, nhưng bạn vẫn gọi hàm này trên mỗi lần kết xuất. Điều này có thể gây lãng phí nếu nó tạo ra các mảng lớn hoặc thực hiện các tính toán tốn kém. -To solve this, you may **pass it as an _initializer_ function** to `useReducer` as the third argument instead: +Để giải quyết vấn đề này, bạn có thể **truyền nó như một hàm _khởi tạo_** cho `useReducer` làm đối số thứ ba: ```js {6} function createInitialState(username) { @@ -751,16 +746,15 @@ function TodoList({ username }) { const [state, dispatch] = useReducer(reducer, username, createInitialState); // ... ``` +Lưu ý rằng bạn đang truyền `createInitialState`, là *chính hàm*, chứ không phải `createInitialState()`, là kết quả của việc gọi nó. Bằng cách này, trạng thái ban đầu không bị tạo lại sau khi khởi tạo. -Notice that you’re passing `createInitialState`, which is the *function itself*, and not `createInitialState()`, which is the result of calling it. This way, the initial state does not get re-created after initialization. - -In the above example, `createInitialState` takes a `username` argument. If your initializer doesn't need any information to compute the initial state, you may pass `null` as the second argument to `useReducer`. +Trong ví dụ trên, `createInitialState` nhận một đối số `username`. Nếu trình khởi tạo của bạn không cần bất kỳ thông tin nào để tính toán trạng thái ban đầu, bạn có thể truyền `null` làm đối số thứ hai cho `useReducer`. -<Recipes titleText="The difference between passing an initializer and passing the initial state directly" titleId="examples-initializer"> +<Recipes titleText="Sự khác biệt giữa việc truyền một trình khởi tạo và truyền trực tiếp trạng thái ban đầu" titleId="examples-initializer"> -#### Passing the initializer function {/*passing-the-initializer-function*/} +#### Truyền hàm khởi tạo {/*passing-the-initializer-function*/} -This example passes the initializer function, so the `createInitialState` function only runs during initialization. It does not run when component re-renders, such as when you type into the input. +Ví dụ này truyền hàm khởi tạo, vì vậy hàm `createInitialState` chỉ chạy trong quá trình khởi tạo. Nó không chạy khi thành phần kết xuất lại, chẳng hạn như khi bạn nhập vào đầu vào. <Sandpack> @@ -846,9 +840,9 @@ export default function TodoList({ username }) { <Solution /> -#### Passing the initial state directly {/*passing-the-initial-state-directly*/} +#### Truyền trực tiếp trạng thái ban đầu {/*passing-the-initial-state-directly*/} -This example **does not** pass the initializer function, so the `createInitialState` function runs on every render, such as when you type into the input. There is no observable difference in behavior, but this code is less efficient. +Ví dụ này **không** truyền hàm khởi tạo, vì vậy hàm `createInitialState` chạy trên mỗi lần kết xuất, chẳng hạn như khi bạn nhập vào đầu vào. Không có sự khác biệt đáng chú ý nào về hành vi, nhưng mã này kém hiệu quả hơn. <Sandpack> @@ -937,28 +931,28 @@ export default function TodoList({ username }) { --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I've dispatched an action, but logging gives me the old state value {/*ive-dispatched-an-action-but-logging-gives-me-the-old-state-value*/} +### Tôi đã gửi một action, nhưng nhật ký cho tôi giá trị trạng thái cũ {/*ive-dispatched-an-action-but-logging-gives-me-the-old-state-value*/} -Calling the `dispatch` function **does not change state in the running code**: +Gọi hàm `dispatch` **không thay đổi trạng thái trong mã đang chạy**: ```js {4,5,8} function handleClick() { console.log(state.age); // 42 - dispatch({ type: 'incremented_age' }); // Request a re-render with 43 - console.log(state.age); // Still 42! + dispatch({ type: 'incremented_age' }); // Yêu cầu kết xuất lại với 43 + console.log(state.age); // Vẫn là 42! setTimeout(() => { - console.log(state.age); // Also 42! + console.log(state.age); // Cũng là 42! }, 5000); } ``` -This is because [states behaves like a snapshot.](/learn/state-as-a-snapshot) Updating state requests another render with the new state value, but does not affect the `state` JavaScript variable in your already-running event handler. +Điều này là do [trạng thái hoạt động như một ảnh chụp nhanh.](/learn/state-as-a-snapshot) Cập nhật trạng thái yêu cầu một kết xuất khác với giá trị trạng thái mới, nhưng không ảnh hưởng đến biến JavaScript `state` trong trình xử lý sự kiện đang chạy của bạn. -If you need to guess the next state value, you can calculate it manually by calling the reducer yourself: +Nếu bạn cần đoán giá trị trạng thái tiếp theo, bạn có thể tính toán nó theo cách thủ công bằng cách tự gọi reducer: ```js const action = { type: 'incremented_age' }; @@ -971,20 +965,20 @@ console.log(nextState); // { age: 43 } --- -### I've dispatched an action, but the screen doesn't update {/*ive-dispatched-an-action-but-the-screen-doesnt-update*/} +### Tôi đã gửi một action, nhưng màn hình không cập nhật {/*ive-dispatched-an-action-but-the-screen-doesnt-update*/} -React will **ignore your update if the next state is equal to the previous state,** as determined by an [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. This usually happens when you change an object or an array in state directly: +React sẽ **bỏ qua bản cập nhật của bạn nếu trạng thái tiếp theo bằng với trạng thái trước đó,** như được xác định bởi so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Điều này thường xảy ra khi bạn thay đổi trực tiếp một đối tượng hoặc một mảng trong trạng thái: ```js {4-5,9-10} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // 🚩 Wrong: mutating existing object + // 🚩 Sai: đột biến đối tượng hiện có state.age++; return state; } case 'changed_name': { - // 🚩 Wrong: mutating existing object + // 🚩 Sai: đột biến đối tượng hiện có state.name = action.nextName; return state; } @@ -993,20 +987,20 @@ function reducer(state, action) { } ``` -You mutated an existing `state` object and returned it, so React ignored the update. To fix this, you need to ensure that you're always [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) instead of mutating them: +Bạn đã đột biến một đối tượng `state` hiện có và trả về nó, vì vậy React đã bỏ qua bản cập nhật. Để khắc phục điều này, bạn cần đảm bảo rằng bạn luôn [cập nhật các đối tượng trong trạng thái](/learn/updating-objects-in-state) và [cập nhật các mảng trong trạng thái](/learn/updating-arrays-in-state) thay vì đột biến chúng: ```js {4-8,11-15} function reducer(state, action) { switch (action.type) { case 'incremented_age': { - // ✅ Correct: creating a new object + // ✅ Đúng: tạo một đối tượng mới return { ...state, age: state.age + 1 }; } case 'changed_name': { - // ✅ Correct: creating a new object + // ✅ Đúng: tạo một đối tượng mới return { ...state, name: action.nextName @@ -1019,29 +1013,29 @@ function reducer(state, action) { --- -### A part of my reducer state becomes undefined after dispatching {/*a-part-of-my-reducer-state-becomes-undefined-after-dispatching*/} +### Một phần trạng thái reducer của tôi trở thành không xác định sau khi gửi {/*a-part-of-my-reducer-state-becomes-undefined-after-dispatching*/} -Make sure that every `case` branch **copies all of the existing fields** when returning the new state: +Đảm bảo rằng mọi nhánh `case` **sao chép tất cả các trường hiện có** khi trả về trạng thái mới: ```js {5} function reducer(state, action) { switch (action.type) { case 'incremented_age': { return { - ...state, // Don't forget this! + ...state, // Đừng quên điều này! age: state.age + 1 }; } // ... ``` -Without `...state` above, the returned next state would only contain the `age` field and nothing else. +Nếu không có `...state` ở trên, trạng thái tiếp theo được trả về sẽ chỉ chứa trường `age` và không có gì khác. --- -### My entire reducer state becomes undefined after dispatching {/*my-entire-reducer-state-becomes-undefined-after-dispatching*/} +### Toàn bộ trạng thái reducer của tôi trở thành không xác định sau khi gửi {/*my-entire-reducer-state-becomes-undefined-after-dispatching*/} -If your state unexpectedly becomes `undefined`, you're likely forgetting to `return` state in one of the cases, or your action type doesn't match any of the `case` statements. To find why, throw an error outside the `switch`: +Nếu trạng thái của bạn bất ngờ trở thành `undefined`, có thể bạn đang quên `return` trạng thái trong một trong các trường hợp hoặc loại action của bạn không khớp với bất kỳ câu lệnh `case` nào. Để tìm lý do, hãy đưa ra một lỗi bên ngoài `switch`: ```js {10} function reducer(state, action) { @@ -1057,42 +1051,42 @@ function reducer(state, action) { } ``` -You can also use a static type checker like TypeScript to catch such mistakes. +Bạn cũng có thể sử dụng trình kiểm tra kiểu tĩnh như TypeScript để bắt các lỗi như vậy. --- -### I'm getting an error: "Too many re-renders" {/*im-getting-an-error-too-many-re-renders*/} +### Tôi gặp lỗi: "Quá nhiều lần kết xuất lại" {/*im-getting-an-error-too-many-re-renders*/} -You might get an error that says: `Too many re-renders. React limits the number of renders to prevent an infinite loop.` Typically, this means that you're unconditionally dispatching an action *during render*, so your component enters a loop: render, dispatch (which causes a render), render, dispatch (which causes a render), and so on. Very often, this is caused by a mistake in specifying an event handler: +Bạn có thể gặp lỗi cho biết: `Too many re-renders. React limits the number of renders to prevent an infinite loop.` (Quá nhiều lần kết xuất lại. React giới hạn số lần kết xuất để ngăn chặn vòng lặp vô hạn.) Thông thường, điều này có nghĩa là bạn đang gửi một action vô điều kiện *trong quá trình kết xuất*, vì vậy thành phần của bạn đi vào một vòng lặp: kết xuất, gửi (gây ra kết xuất), kết xuất, gửi (gây ra kết xuất), v.v. Rất thường xuyên, điều này là do một sai lầm trong việc chỉ định một trình xử lý sự kiện: ```js {1-2} -// 🚩 Wrong: calls the handler during render +// 🚩 Sai: gọi trình xử lý trong quá trình kết xuất return <button onClick={handleClick()}>Click me</button> -// ✅ Correct: passes down the event handler +// ✅ Đúng: chuyển trình xử lý sự kiện xuống return <button onClick={handleClick}>Click me</button> -// ✅ Correct: passes down an inline function +// ✅ Đúng: chuyển một hàm nội tuyến xuống return <button onClick={(e) => handleClick(e)}>Click me</button> ``` -If you can't find the cause of this error, click on the arrow next to the error in the console and look through the JavaScript stack to find the specific `dispatch` function call responsible for the error. +Nếu bạn không thể tìm thấy nguyên nhân của lỗi này, hãy nhấp vào mũi tên bên cạnh lỗi trong bảng điều khiển và xem qua ngăn xếp JavaScript để tìm lệnh gọi hàm `dispatch` cụ thể chịu trách nhiệm cho lỗi. --- -### My reducer or initializer function runs twice {/*my-reducer-or-initializer-function-runs-twice*/} +### Hàm reducer hoặc hàm khởi tạo của tôi chạy hai lần {/*my-reducer-or-initializer-function-runs-twice*/} -In [Strict Mode](/reference/react/StrictMode), React will call your reducer and initializer functions twice. This shouldn't break your code. +Trong [Chế độ nghiêm ngặt](/reference/react/StrictMode), React sẽ gọi các hàm reducer và hàm khởi tạo của bạn hai lần. Điều này sẽ không phá vỡ mã của bạn. -This **development-only** behavior helps you [keep components pure.](/learn/keeping-components-pure) React uses the result of one of the calls, and ignores the result of the other call. As long as your component, initializer, and reducer functions are pure, this shouldn't affect your logic. However, if they are accidentally impure, this helps you notice the mistakes. +Hành vi **chỉ dành cho phát triển** này giúp bạn [giữ cho các thành phần thuần túy.](/learn/keeping-components-pure) React sử dụng kết quả của một trong các lệnh gọi và bỏ qua kết quả của lệnh gọi kia. Miễn là thành phần, trình khởi tạo và các hàm reducer của bạn là thuần túy, điều này sẽ không ảnh hưởng đến logic của bạn. Tuy nhiên, nếu chúng vô tình không thuần túy, điều này sẽ giúp bạn nhận thấy những sai lầm. -For example, this impure reducer function mutates an array in state: +Ví dụ: hàm reducer không thuần túy này đột biến một mảng trong trạng thái: ```js {4-6} function reducer(state, action) { switch (action.type) { case 'added_todo': { - // 🚩 Mistake: mutating state + // 🚩 Sai lầm: đột biến trạng thái state.todos.push({ id: nextId++, text: action.text }); return state; } @@ -1101,13 +1095,13 @@ function reducer(state, action) { } ``` -Because React calls your reducer function twice, you'll see the todo was added twice, so you'll know that there is a mistake. In this example, you can fix the mistake by [replacing the array instead of mutating it](/learn/updating-arrays-in-state#adding-to-an-array): +Vì React gọi hàm reducer của bạn hai lần, bạn sẽ thấy todo đã được thêm hai lần, vì vậy bạn sẽ biết rằng có một sai lầm. Trong ví dụ này, bạn có thể sửa sai lầm bằng cách [thay thế mảng thay vì đột biến nó](/learn/updating-arrays-in-state#adding-to-an-array): ```js {4-11} function reducer(state, action) { switch (action.type) { case 'added_todo': { - // ✅ Correct: replacing with new state + // ✅ Đúng: thay thế bằng trạng thái mới return { ...state, todos: [ @@ -1121,6 +1115,6 @@ function reducer(state, action) { } ``` -Now that this reducer function is pure, calling it an extra time doesn't make a difference in behavior. This is why React calling it twice helps you find mistakes. **Only component, initializer, and reducer functions need to be pure.** Event handlers don't need to be pure, so React will never call your event handlers twice. +Bây giờ hàm reducer này là thuần túy, việc gọi nó thêm một lần không tạo ra sự khác biệt trong hành vi. Đây là lý do tại sao React gọi nó hai lần giúp bạn tìm thấy những sai lầm. **Chỉ các hàm thành phần, trình khởi tạo và reducer cần phải thuần túy.** Các trình xử lý sự kiện không cần phải thuần túy, vì vậy React sẽ không bao giờ gọi các trình xử lý sự kiện của bạn hai lần. -Read [keeping components pure](/learn/keeping-components-pure) to learn more. +Đọc [giữ cho các thành phần thuần túy](/learn/keeping-components-pure) để tìm hiểu thêm. diff --git a/src/content/reference/react/useRef.md b/src/content/reference/react/useRef.md index 8ab53aef3..ebc0c364c 100644 --- a/src/content/reference/react/useRef.md +++ b/src/content/reference/react/useRef.md @@ -4,7 +4,7 @@ title: useRef <Intro> -`useRef` is a React Hook that lets you reference a value that's not needed for rendering. +`useRef` là một React Hook cho phép bạn tham chiếu một giá trị không cần thiết cho việc hiển thị. ```js const ref = useRef(initialValue) @@ -16,11 +16,11 @@ const ref = useRef(initialValue) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useRef(initialValue)` {/*useref*/} -Call `useRef` at the top level of your component to declare a [ref.](/learn/referencing-values-with-refs) +Gọi `useRef` ở cấp cao nhất của component để khai báo một [ref.](/learn/referencing-values-with-refs) ```js import { useRef } from 'react'; @@ -31,34 +31,34 @@ function MyComponent() { // ... ``` -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `initialValue`: The value you want the ref object's `current` property to be initially. It can be a value of any type. This argument is ignored after the initial render. +* `initialValue`: Giá trị bạn muốn thuộc tính `current` của đối tượng ref được khởi tạo ban đầu. Nó có thể là một giá trị của bất kỳ kiểu nào. Đối số này bị bỏ qua sau lần hiển thị ban đầu. -#### Returns {/*returns*/} +#### Giá trị trả về {/*returns*/} -`useRef` returns an object with a single property: +`useRef` trả về một đối tượng với một thuộc tính duy nhất: -* `current`: Initially, it's set to the `initialValue` you have passed. You can later set it to something else. If you pass the ref object to React as a `ref` attribute to a JSX node, React will set its `current` property. +* `current`: Ban đầu, nó được đặt thành `initialValue` mà bạn đã truyền. Sau đó, bạn có thể đặt nó thành một giá trị khác. Nếu bạn truyền đối tượng ref cho React dưới dạng thuộc tính `ref` cho một nút JSX, React sẽ đặt thuộc tính `current` của nó. -On the next renders, `useRef` will return the same object. +Trong các lần hiển thị tiếp theo, `useRef` sẽ trả về cùng một đối tượng. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* You can mutate the `ref.current` property. Unlike state, it is mutable. However, if it holds an object that is used for rendering (for example, a piece of your state), then you shouldn't mutate that object. -* When you change the `ref.current` property, React does not re-render your component. React is not aware of when you change it because a ref is a plain JavaScript object. -* Do not write _or read_ `ref.current` during rendering, except for [initialization.](#avoiding-recreating-the-ref-contents) This makes your component's behavior unpredictable. -* In Strict Mode, React will **call your component function twice** in order to [help you find accidental impurities.](/reference/react/useState#my-initializer-or-updater-function-runs-twice) This is development-only behavior and does not affect production. Each ref object will be created twice, but one of the versions will be discarded. If your component function is pure (as it should be), this should not affect the behavior. +* Bạn có thể thay đổi thuộc tính `ref.current`. Không giống như state, nó có thể thay đổi được. Tuy nhiên, nếu nó chứa một đối tượng được sử dụng để hiển thị (ví dụ: một phần của state của bạn), thì bạn không nên thay đổi đối tượng đó. +* Khi bạn thay đổi thuộc tính `ref.current`, React không hiển thị lại component của bạn. React không nhận biết được khi bạn thay đổi nó vì ref là một đối tượng JavaScript thuần túy. +* Không viết _hoặc đọc_ `ref.current` trong quá trình hiển thị, ngoại trừ [khởi tạo.](#avoiding-recreating-the-ref-contents) Điều này làm cho hành vi của component của bạn trở nên khó đoán. +* Trong Strict Mode, React sẽ **gọi hàm component của bạn hai lần** để [giúp bạn tìm ra những tạp chất vô tình.](/reference/react/useState#my-initializer-or-updater-function-runs-twice) Đây là hành vi chỉ dành cho quá trình phát triển và không ảnh hưởng đến production. Mỗi đối tượng ref sẽ được tạo hai lần, nhưng một trong các phiên bản sẽ bị loại bỏ. Nếu hàm component của bạn là thuần túy (như nó phải vậy), điều này sẽ không ảnh hưởng đến hành vi. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Referencing a value with a ref {/*referencing-a-value-with-a-ref*/} +### Tham chiếu một giá trị với ref {/*referencing-a-value-with-a-ref*/} -Call `useRef` at the top level of your component to declare one or more [refs.](/learn/referencing-values-with-refs) +Gọi `useRef` ở cấp cao nhất của component để khai báo một hoặc nhiều [refs.](/learn/referencing-values-with-refs) ```js [[1, 4, "intervalRef"], [3, 4, "0"]] import { useRef } from 'react'; @@ -68,11 +68,11 @@ function Stopwatch() { // ... ``` -`useRef` returns a <CodeStep step={1}>ref object</CodeStep> with a single <CodeStep step={2}>`current` property</CodeStep> initially set to the <CodeStep step={3}>initial value</CodeStep> you provided. +`useRef` trả về một <CodeStep step={1}>đối tượng ref</CodeStep> với một <CodeStep step={2}>thuộc tính `current`</CodeStep> duy nhất ban đầu được đặt thành <CodeStep step={3}>giá trị ban đầu</CodeStep> mà bạn đã cung cấp. -On the next renders, `useRef` will return the same object. You can change its `current` property to store information and read it later. This might remind you of [state](/reference/react/useState), but there is an important difference. +Trong các lần hiển thị tiếp theo, `useRef` sẽ trả về cùng một đối tượng. Bạn có thể thay đổi thuộc tính `current` của nó để lưu trữ thông tin và đọc nó sau này. Điều này có thể khiến bạn nhớ đến [state](/reference/react/useState), nhưng có một sự khác biệt quan trọng. -**Changing a ref does not trigger a re-render.** This means refs are perfect for storing information that doesn't affect the visual output of your component. For example, if you need to store an [interval ID](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) and retrieve it later, you can put it in a ref. To update the value inside the ref, you need to manually change its <CodeStep step={2}>`current` property</CodeStep>: +**Thay đổi ref không kích hoạt hiển thị lại.** Điều này có nghĩa là ref là hoàn hảo để lưu trữ thông tin không ảnh hưởng đến đầu ra trực quan của component của bạn. Ví dụ: nếu bạn cần lưu trữ một [ID khoảng thời gian](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) và truy xuất nó sau này, bạn có thể đặt nó trong một ref. Để cập nhật giá trị bên trong ref, bạn cần thay đổi thủ công <CodeStep step={2}>thuộc tính `current`</CodeStep> của nó: ```js [[2, 5, "intervalRef.current"]] function handleStartClick() { @@ -83,7 +83,7 @@ function handleStartClick() { } ``` -Later, you can read that interval ID from the ref so that you can call [clear that interval](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval): +Sau đó, bạn có thể đọc ID khoảng thời gian đó từ ref để bạn có thể gọi [xóa khoảng thời gian đó](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval): ```js [[2, 2, "intervalRef.current"]] function handleStopClick() { @@ -92,19 +92,19 @@ function handleStopClick() { } ``` -By using a ref, you ensure that: +Bằng cách sử dụng ref, bạn đảm bảo rằng: -- You can **store information** between re-renders (unlike regular variables, which reset on every render). -- Changing it **does not trigger a re-render** (unlike state variables, which trigger a re-render). -- The **information is local** to each copy of your component (unlike the variables outside, which are shared). +- Bạn có thể **lưu trữ thông tin** giữa các lần hiển thị lại (không giống như các biến thông thường, được đặt lại trên mỗi lần hiển thị). +- Thay đổi nó **không kích hoạt hiển thị lại** (không giống như các biến state, kích hoạt hiển thị lại). +- **Thông tin là cục bộ** cho mỗi bản sao của component của bạn (không giống như các biến bên ngoài, được chia sẻ). -Changing a ref does not trigger a re-render, so refs are not appropriate for storing information you want to display on the screen. Use state for that instead. Read more about [choosing between `useRef` and `useState`.](/learn/referencing-values-with-refs#differences-between-refs-and-state) +Thay đổi ref không kích hoạt hiển thị lại, vì vậy ref không phù hợp để lưu trữ thông tin bạn muốn hiển thị trên màn hình. Thay vào đó, hãy sử dụng state. Đọc thêm về [lựa chọn giữa `useRef` và `useState`.](/learn/referencing-values-with-refs#differences-between-refs-and-state) -<Recipes titleText="Examples of referencing a value with useRef" titleId="examples-value"> +<Recipes titleText="Ví dụ về tham chiếu một giá trị với useRef" titleId="examples-value"> -#### Click counter {/*click-counter*/} +#### Bộ đếm nhấp chuột {/*click-counter*/} -This component uses a ref to keep track of how many times the button was clicked. Note that it's okay to use a ref instead of state here because the click count is only read and written in an event handler. +Component này sử dụng ref để theo dõi số lần nút được nhấp. Lưu ý rằng bạn có thể sử dụng ref thay vì state ở đây vì số lần nhấp chỉ được đọc và ghi trong một trình xử lý sự kiện. <Sandpack> @@ -116,12 +116,12 @@ export default function Counter() { function handleClick() { ref.current = ref.current + 1; - alert('You clicked ' + ref.current + ' times!'); + alert('Bạn đã nhấp ' + ref.current + ' lần!'); } return ( <button onClick={handleClick}> - Click me! + Nhấp vào tôi! </button> ); } @@ -129,13 +129,13 @@ export default function Counter() { </Sandpack> -If you show `{ref.current}` in the JSX, the number won't update on click. This is because setting `ref.current` does not trigger a re-render. Information that's used for rendering should be state instead. +Nếu bạn hiển thị `{ref.current}` trong JSX, số sẽ không cập nhật khi nhấp. Điều này là do việc đặt `ref.current` không kích hoạt hiển thị lại. Thông tin được sử dụng để hiển thị nên là state. <Solution /> -#### A stopwatch {/*a-stopwatch*/} +#### Đồng hồ bấm giờ {/*a-stopwatch*/} -This example uses a combination of state and refs. Both `startTime` and `now` are state variables because they are used for rendering. But we also need to hold an [interval ID](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) so that we can stop the interval on button press. Since the interval ID is not used for rendering, it's appropriate to keep it in a ref, and manually update it. +Ví dụ này sử dụng kết hợp state và ref. Cả `startTime` và `now` đều là các biến state vì chúng được sử dụng để hiển thị. Nhưng chúng ta cũng cần giữ một [ID khoảng thời gian](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) để chúng ta có thể dừng khoảng thời gian khi nhấn nút. Vì ID khoảng thời gian không được sử dụng để hiển thị, nên việc giữ nó trong một ref và cập nhật thủ công là phù hợp. <Sandpack> @@ -168,12 +168,12 @@ export default function Stopwatch() { return ( <> - <h1>Time passed: {secondsPassed.toFixed(3)}</h1> + <h1>Thời gian đã trôi qua: {secondsPassed.toFixed(3)}</h1> <button onClick={handleStart}> - Start + Bắt đầu </button> <button onClick={handleStop}> - Stop + Dừng lại </button> </> ); @@ -188,57 +188,57 @@ export default function Stopwatch() { <Pitfall> -**Do not write _or read_ `ref.current` during rendering.** +**Không viết _hoặc đọc_ `ref.current` trong quá trình hiển thị.** -React expects that the body of your component [behaves like a pure function](/learn/keeping-components-pure): +React mong đợi rằng phần thân của component của bạn [hoạt động như một hàm thuần túy](/learn/keeping-components-pure): -- If the inputs ([props](/learn/passing-props-to-a-component), [state](/learn/state-a-components-memory), and [context](/learn/passing-data-deeply-with-context)) are the same, it should return exactly the same JSX. -- Calling it in a different order or with different arguments should not affect the results of other calls. +- Nếu các đầu vào ([props](/learn/passing-props-to-a-component), [state](/learn/state-a-components-memory) và [context](/learn/passing-data-deeply-with-context)) giống nhau, nó sẽ trả về chính xác cùng một JSX. +- Gọi nó theo một thứ tự khác hoặc với các đối số khác nhau sẽ không ảnh hưởng đến kết quả của các lệnh gọi khác. -Reading or writing a ref **during rendering** breaks these expectations. +Đọc hoặc ghi ref **trong quá trình hiển thị** phá vỡ những kỳ vọng này. ```js {3-4,6-7} function MyComponent() { // ... - // 🚩 Don't write a ref during rendering + // 🚩 Không viết ref trong quá trình hiển thị myRef.current = 123; // ... - // 🚩 Don't read a ref during rendering + // 🚩 Không đọc ref trong quá trình hiển thị return <h1>{myOtherRef.current}</h1>; } ``` -You can read or write refs **from event handlers or effects instead**. +Bạn có thể đọc hoặc ghi ref **từ các trình xử lý sự kiện hoặc hiệu ứng thay thế**. ```js {4-5,9-10} function MyComponent() { // ... useEffect(() => { - // ✅ You can read or write refs in effects + // ✅ Bạn có thể đọc hoặc ghi ref trong các hiệu ứng myRef.current = 123; }); // ... function handleClick() { - // ✅ You can read or write refs in event handlers + // ✅ Bạn có thể đọc hoặc ghi ref trong các trình xử lý sự kiện doSomething(myOtherRef.current); } // ... } ``` -If you *have to* read [or write](/reference/react/useState#storing-information-from-previous-renders) something during rendering, [use state](/reference/react/useState) instead. +Nếu bạn *phải* đọc [hoặc viết](/reference/react/useState#storing-information-from-previous-renders) một cái gì đó trong quá trình hiển thị, hãy [sử dụng state](/reference/react/useState) thay thế. -When you break these rules, your component might still work, but most of the newer features we're adding to React will rely on these expectations. Read more about [keeping your components pure.](/learn/keeping-components-pure#where-you-_can_-cause-side-effects) +Khi bạn phá vỡ các quy tắc này, component của bạn vẫn có thể hoạt động, nhưng hầu hết các tính năng mới hơn mà chúng tôi đang thêm vào React sẽ dựa trên những kỳ vọng này. Đọc thêm về [giữ cho component của bạn thuần túy.](/learn/keeping-components-pure#where-you-_can_-cause-side-effects) </Pitfall> --- -### Manipulating the DOM with a ref {/*manipulating-the-dom-with-a-ref*/} +### Thao tác DOM với ref {/*manipulating-the-dom-with-a-ref*/} -It's particularly common to use a ref to manipulate the [DOM.](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API) React has built-in support for this. +Việc sử dụng ref để thao tác [DOM](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API) là đặc biệt phổ biến. React có hỗ trợ tích hợp cho việc này. -First, declare a <CodeStep step={1}>ref object</CodeStep> with an <CodeStep step={3}>initial value</CodeStep> of `null`: +Đầu tiên, khai báo một <CodeStep step={1}>đối tượng ref</CodeStep> với một <CodeStep step={3}>giá trị ban đầu</CodeStep> là `null`: ```js [[1, 4, "inputRef"], [3, 4, "null"]] import { useRef } from 'react'; @@ -248,14 +248,14 @@ function MyComponent() { // ... ``` -Then pass your ref object as the `ref` attribute to the JSX of the DOM node you want to manipulate: +Sau đó, chuyển đối tượng ref của bạn làm thuộc tính `ref` cho JSX của nút DOM bạn muốn thao tác: ```js [[1, 2, "inputRef"]] // ... return <input ref={inputRef} />; ``` -After React creates the DOM node and puts it on the screen, React will set the <CodeStep step={2}>`current` property</CodeStep> of your ref object to that DOM node. Now you can access the `<input>`'s DOM node and call methods like [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus): +Sau khi React tạo nút DOM và đặt nó trên màn hình, React sẽ đặt <CodeStep step={2}>thuộc tính `current`</CodeStep> của đối tượng ref của bạn thành nút DOM đó. Bây giờ bạn có thể truy cập nút DOM của `<input>` và gọi các phương thức như [`focus()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus): ```js [[2, 2, "inputRef.current"]] function handleClick() { @@ -263,15 +263,15 @@ After React creates the DOM node and puts it on the screen, React will set the < } ``` -React will set the `current` property back to `null` when the node is removed from the screen. +React sẽ đặt thuộc tính `current` trở lại `null` khi nút bị xóa khỏi màn hình. -Read more about [manipulating the DOM with refs.](/learn/manipulating-the-dom-with-refs) +Đọc thêm về [thao tác DOM với ref.](/learn/manipulating-the-dom-with-refs) -<Recipes titleText="Examples of manipulating the DOM with useRef" titleId="examples-dom"> +<Recipes titleText="Ví dụ về thao tác DOM với useRef" titleId="examples-dom"> -#### Focusing a text input {/*focusing-a-text-input*/} +#### Tập trung vào một ô nhập văn bản {/*focusing-a-text-input*/} -In this example, clicking the button will focus the input: +Trong ví dụ này, việc nhấp vào nút sẽ tập trung vào ô nhập: <Sandpack> @@ -289,7 +289,7 @@ export default function Form() { <> <input ref={inputRef} /> <button onClick={handleClick}> - Focus the input + Tập trung vào ô nhập </button> </> ); @@ -300,9 +300,9 @@ export default function Form() { <Solution /> -#### Scrolling an image into view {/*scrolling-an-image-into-view*/} +#### Cuộn một hình ảnh vào chế độ xem {/*scrolling-an-image-into-view*/} -In this example, clicking the button will scroll an image into view. It uses a ref to the list DOM node, and then calls DOM [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) API to find the image we want to scroll to. +Trong ví dụ này, việc nhấp vào nút sẽ cuộn một hình ảnh vào chế độ xem. Nó sử dụng ref cho nút DOM danh sách, sau đó gọi API DOM [`querySelectorAll`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) để tìm hình ảnh chúng ta muốn cuộn đến. <Sandpack> @@ -314,7 +314,7 @@ export default function CatFriends() { function scrollToIndex(index) { const listNode = listRef.current; - // This line assumes a particular DOM structure: + // Dòng này giả định một cấu trúc DOM cụ thể: const imgNode = listNode.querySelectorAll('li > img')[index]; imgNode.scrollIntoView({ behavior: 'smooth', @@ -393,9 +393,9 @@ li { <Solution /> -#### Playing and pausing a video {/*playing-and-pausing-a-video*/} +#### Phát và tạm dừng video {/*playing-and-pausing-a-video*/} -This example uses a ref to call [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) and [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) on a `<video>` DOM node. +Ví dụ này sử dụng ref để gọi [`play()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) và [`pause()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause) trên một nút DOM `<video>`. <Sandpack> @@ -420,7 +420,7 @@ export default function VideoPlayer() { return ( <> <button onClick={handleClick}> - {isPlaying ? 'Pause' : 'Play'} + {isPlaying ? 'Tạm dừng' : 'Phát'} </button> <video width="250" @@ -446,9 +446,9 @@ button { display: block; margin-bottom: 20px; } <Solution /> -#### Exposing a ref to your own component {/*exposing-a-ref-to-your-own-component*/} +#### Hiển thị ref cho component của riêng bạn {/*exposing-a-ref-to-your-own-component*/} -Sometimes, you may want to let the parent component manipulate the DOM inside of your component. For example, maybe you're writing a `MyInput` component, but you want the parent to be able to focus the input (which the parent has no access to). You can create a `ref` in the parent and pass the `ref` as prop to the child component. Read a [detailed walkthrough](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) here. +Đôi khi, bạn có thể muốn cho phép component cha thao tác DOM bên trong component của bạn. Ví dụ: có thể bạn đang viết một component `MyInput`, nhưng bạn muốn component cha có thể tập trung vào ô nhập (mà component cha không có quyền truy cập). Bạn có thể tạo một `ref` trong component cha và chuyển `ref` làm prop cho component con. Đọc [hướng dẫn chi tiết](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) tại đây. <Sandpack> @@ -470,7 +470,7 @@ export default function Form() { <> <MyInput ref={inputRef} /> <button onClick={handleClick}> - Focus the input + Tập trung vào ô nhập </button> </> ); @@ -485,9 +485,9 @@ export default function Form() { --- -### Avoiding recreating the ref contents {/*avoiding-recreating-the-ref-contents*/} +### Tránh tạo lại nội dung ref {/*avoiding-recreating-the-ref-contents*/} -React saves the initial ref value once and ignores it on the next renders. +React lưu giá trị ref ban đầu một lần và bỏ qua nó trong các lần hiển thị tiếp theo. ```js function Video() { @@ -495,9 +495,9 @@ function Video() { // ... ``` -Although the result of `new VideoPlayer()` is only used for the initial render, you're still calling this function on every render. This can be wasteful if it's creating expensive objects. +Mặc dù kết quả của `new VideoPlayer()` chỉ được sử dụng cho lần hiển thị ban đầu, nhưng bạn vẫn đang gọi hàm này trên mỗi lần hiển thị. Điều này có thể gây lãng phí nếu nó đang tạo ra các đối tượng tốn kém. -To solve it, you may initialize the ref like this instead: +Để giải quyết vấn đề này, bạn có thể khởi tạo ref như thế này thay thế: ```js function Video() { @@ -508,13 +508,13 @@ function Video() { // ... ``` -Normally, writing or reading `ref.current` during render is not allowed. However, it's fine in this case because the result is always the same, and the condition only executes during initialization so it's fully predictable. +Thông thường, việc viết hoặc đọc `ref.current` trong quá trình hiển thị là không được phép. Tuy nhiên, điều này là ổn trong trường hợp này vì kết quả luôn giống nhau và điều kiện chỉ thực thi trong quá trình khởi tạo nên nó hoàn toàn có thể đoán trước được. <DeepDive> -#### How to avoid null checks when initializing useRef later {/*how-to-avoid-null-checks-when-initializing-use-ref-later*/} +#### Làm thế nào để tránh kiểm tra null khi khởi tạo useRef sau {/*how-to-avoid-null-checks-when-initializing-use-ref-later*/} -If you use a type checker and don't want to always check for `null`, you can try a pattern like this instead: +Nếu bạn sử dụng trình kiểm tra kiểu và không muốn luôn kiểm tra `null`, bạn có thể thử một mẫu như thế này thay thế: ```js function Video() { @@ -532,17 +532,17 @@ function Video() { // ... ``` -Here, the `playerRef` itself is nullable. However, you should be able to convince your type checker that there is no case in which `getPlayer()` returns `null`. Then use `getPlayer()` in your event handlers. +Ở đây, bản thân `playerRef` có thể null. Tuy nhiên, bạn sẽ có thể thuyết phục trình kiểm tra kiểu của mình rằng không có trường hợp nào `getPlayer()` trả về `null`. Sau đó, sử dụng `getPlayer()` trong các trình xử lý sự kiện của bạn. </DeepDive> --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I can't get a ref to a custom component {/*i-cant-get-a-ref-to-a-custom-component*/} +### Tôi không thể lấy ref cho một component tùy chỉnh {/*i-cant-get-a-ref-to-a-custom-component*/} -If you try to pass a `ref` to your own component like this: +Nếu bạn cố gắng chuyển một `ref` cho component của riêng bạn như thế này: ```js const inputRef = useRef(null); @@ -550,17 +550,17 @@ const inputRef = useRef(null); return <MyInput ref={inputRef} />; ``` -You might get an error in the console: +Bạn có thể gặp lỗi trong bảng điều khiển: <ConsoleBlock level="error"> -TypeError: Cannot read properties of null +TypeError: Không thể đọc các thuộc tính của null </ConsoleBlock> -By default, your own components don't expose refs to the DOM nodes inside them. +Theo mặc định, các component của riêng bạn không hiển thị ref cho các nút DOM bên trong chúng. -To fix this, find the component that you want to get a ref to: +Để khắc phục điều này, hãy tìm component mà bạn muốn lấy ref: ```js export default function MyInput({ value, onChange }) { @@ -573,7 +573,7 @@ export default function MyInput({ value, onChange }) { } ``` -And then add `ref` to the list of props your component accepts and pass `ref` as a prop to the relevent child [built-in component](/reference/react-dom/components/common) like this: +Và sau đó thêm `ref` vào danh sách các prop mà component của bạn chấp nhận và chuyển `ref` làm prop cho [component tích hợp sẵn](/reference/react-dom/components/common) có liên quan như thế này: ```js {1,6} function MyInput({ value, onChange, ref }) { @@ -589,6 +589,6 @@ function MyInput({ value, onChange, ref }) { export default MyInput; ``` -Then the parent component can get a ref to it. +Sau đó, component cha có thể lấy ref cho nó. -Read more about [accessing another component's DOM nodes.](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) +Đọc thêm về [truy cập các nút DOM của component khác.](/learn/manipulating-the-dom-with-refs#accessing-another-components-dom-nodes) diff --git a/src/content/reference/react/useState.md b/src/content/reference/react/useState.md index 23db1aae5..99c6b0833 100644 --- a/src/content/reference/react/useState.md +++ b/src/content/reference/react/useState.md @@ -4,7 +4,7 @@ title: useState <Intro> -`useState` is a React Hook that lets you add a [state variable](/learn/state-a-components-memory) to your component. +`useState` là một React Hook cho phép bạn thêm một [biến trạng thái](/learn/state-a-components-memory) vào component của bạn. ```js const [state, setState] = useState(initialState) @@ -16,11 +16,11 @@ const [state, setState] = useState(initialState) --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `useState(initialState)` {/*usestate*/} -Call `useState` at the top level of your component to declare a [state variable.](/learn/state-a-components-memory) +Gọi `useState` ở cấp cao nhất của component để khai báo một [biến trạng thái.](/learn/state-a-components-memory) ```js import { useState } from 'react'; @@ -32,32 +32,32 @@ function MyComponent() { // ... ``` -The convention is to name state variables like `[something, setSomething]` using [array destructuring.](https://javascript.info/destructuring-assignment) +Quy ước là đặt tên các biến trạng thái như `[something, setSomething]` bằng cách sử dụng [destructuring mảng.](https://javascript.info/destructuring-assignment) -[See more examples below.](#usage) +[Xem thêm các ví dụ bên dưới.](#usage) -#### Parameters {/*parameters*/} +#### Tham số {/*parameters*/} -* `initialState`: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render. - * If you pass a function as `initialState`, it will be treated as an _initializer function_. It should be pure, should take no arguments, and should return a value of any type. React will call your initializer function when initializing the component, and store its return value as the initial state. [See an example below.](#avoiding-recreating-the-initial-state) +* `initialState`: Giá trị bạn muốn trạng thái ban đầu là. Nó có thể là một giá trị của bất kỳ kiểu nào, nhưng có một hành vi đặc biệt đối với các hàm. Đối số này bị bỏ qua sau lần render ban đầu. + * Nếu bạn truyền một hàm làm `initialState`, nó sẽ được coi là một _hàm khởi tạo_. Nó phải thuần khiết, không nhận bất kỳ đối số nào và phải trả về một giá trị thuộc bất kỳ kiểu nào. React sẽ gọi hàm khởi tạo của bạn khi khởi tạo component và lưu trữ giá trị trả về của nó làm trạng thái ban đầu. [Xem một ví dụ bên dưới.](#avoiding-recreating-the-initial-state) -#### Returns {/*returns*/} +#### Trả về {/*returns*/} -`useState` returns an array with exactly two values: +`useState` trả về một mảng chính xác hai giá trị: -1. The current state. During the first render, it will match the `initialState` you have passed. -2. The [`set` function](#setstate) that lets you update the state to a different value and trigger a re-render. +1. Trạng thái hiện tại. Trong quá trình render đầu tiên, nó sẽ khớp với `initialState` mà bạn đã truyền. +2. Hàm [`set`](#setstate) cho phép bạn cập nhật trạng thái thành một giá trị khác và kích hoạt re-render. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `useState` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. -* In Strict Mode, React will **call your initializer function twice** in order to [help you find accidental impurities.](#my-initializer-or-updater-function-runs-twice) This is development-only behavior and does not affect production. If your initializer function is pure (as it should be), this should not affect the behavior. The result from one of the calls will be ignored. +* `useState` là một Hook, vì vậy bạn chỉ có thể gọi nó **ở cấp cao nhất của component** hoặc Hook của riêng bạn. Bạn không thể gọi nó bên trong các vòng lặp hoặc điều kiện. Nếu bạn cần điều đó, hãy trích xuất một component mới và di chuyển trạng thái vào đó. +* Trong Strict Mode, React sẽ **gọi hàm khởi tạo của bạn hai lần** để [giúp bạn tìm thấy các tạp chất vô tình.](#my-initializer-or-updater-function-runs-twice) Đây là hành vi chỉ dành cho quá trình phát triển và không ảnh hưởng đến sản xuất. Nếu hàm khởi tạo của bạn là thuần khiết (như nó phải vậy), điều này sẽ không ảnh hưởng đến hành vi. Kết quả từ một trong các lệnh gọi sẽ bị bỏ qua. --- -### `set` functions, like `setSomething(nextState)` {/*setstate*/} +### Các hàm `set`, như `setSomething(nextState)` {/*setstate*/} -The `set` function returned by `useState` lets you update the state to a different value and trigger a re-render. You can pass the next state directly, or a function that calculates it from the previous state: +Hàm `set` được trả về bởi `useState` cho phép bạn cập nhật trạng thái thành một giá trị khác và kích hoạt re-render. Bạn có thể truyền trạng thái tiếp theo trực tiếp hoặc một hàm tính toán nó từ trạng thái trước đó: ```js const [name, setName] = useState('Edward'); @@ -68,36 +68,36 @@ function handleClick() { // ... ``` -#### Parameters {/*setstate-parameters*/} +#### Tham số {/*setstate-parameters*/} -* `nextState`: The value that you want the state to be. It can be a value of any type, but there is a special behavior for functions. - * If you pass a function as `nextState`, it will be treated as an _updater function_. It must be pure, should take the pending state as its only argument, and should return the next state. React will put your updater function in a queue and re-render your component. During the next render, React will calculate the next state by applying all of the queued updaters to the previous state. [See an example below.](#updating-state-based-on-the-previous-state) +* `nextState`: Giá trị bạn muốn trạng thái là. Nó có thể là một giá trị của bất kỳ kiểu nào, nhưng có một hành vi đặc biệt đối với các hàm. + * Nếu bạn truyền một hàm làm `nextState`, nó sẽ được coi là một _hàm cập nhật_. Nó phải thuần khiết, chỉ nhận trạng thái đang chờ xử lý làm đối số duy nhất và phải trả về trạng thái tiếp theo. React sẽ đặt hàm cập nhật của bạn vào một hàng đợi và re-render component của bạn. Trong quá trình render tiếp theo, React sẽ tính toán trạng thái tiếp theo bằng cách áp dụng tất cả các trình cập nhật được xếp hàng vào trạng thái trước đó. [Xem một ví dụ bên dưới.](#updating-state-based-on-the-previous-state) -#### Returns {/*setstate-returns*/} +#### Trả về {/*setstate-returns*/} -`set` functions do not have a return value. +Các hàm `set` không có giá trị trả về. -#### Caveats {/*setstate-caveats*/} +#### Lưu ý {/*setstate-caveats*/} -* The `set` function **only updates the state variable for the *next* render**. If you read the state variable after calling the `set` function, [you will still get the old value](#ive-updated-the-state-but-logging-gives-me-the-old-value) that was on the screen before your call. +* Hàm `set` **chỉ cập nhật biến trạng thái cho lần render *tiếp theo***. Nếu bạn đọc biến trạng thái sau khi gọi hàm `set`, [bạn vẫn sẽ nhận được giá trị cũ](#ive-updated-the-state-but-logging-gives-me-the-old-value) đã có trên màn hình trước khi bạn gọi. -* If the new value you provide is identical to the current `state`, as determined by an [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison, React will **skip re-rendering the component and its children.** This is an optimization. Although in some cases React may still need to call your component before skipping the children, it shouldn't affect your code. +* Nếu giá trị mới bạn cung cấp giống hệt với `state` hiện tại, như được xác định bởi so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is), React sẽ **bỏ qua việc re-render component và các children của nó.** Đây là một tối ưu hóa. Mặc dù trong một số trường hợp, React vẫn có thể cần gọi component của bạn trước khi bỏ qua các children, nhưng nó không ảnh hưởng đến mã của bạn. -* React [batches state updates.](/learn/queueing-a-series-of-state-updates) It updates the screen **after all the event handlers have run** and have called their `set` functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use [`flushSync`.](/reference/react-dom/flushSync) +* React [gom các bản cập nhật trạng thái.](/learn/queueing-a-series-of-state-updates) Nó cập nhật màn hình **sau khi tất cả các trình xử lý sự kiện đã chạy** và đã gọi các hàm `set` của chúng. Điều này ngăn chặn nhiều lần re-render trong một sự kiện duy nhất. Trong trường hợp hiếm hoi bạn cần buộc React cập nhật màn hình sớm hơn, ví dụ: để truy cập DOM, bạn có thể sử dụng [`flushSync`.](/reference/react-dom/flushSync) -* The `set` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire. If the linter lets you omit a dependency without errors, it is safe to do. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) +* Hàm `set` có một identity ổn định, vì vậy bạn sẽ thường thấy nó bị bỏ qua khỏi các dependency của Effect, nhưng việc bao gồm nó sẽ không khiến Effect kích hoạt. Nếu trình lint cho phép bạn bỏ qua một dependency mà không có lỗi, thì điều đó là an toàn. [Tìm hiểu thêm về việc loại bỏ các dependency của Effect.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) -* Calling the `set` function *during rendering* is only allowed from within the currently rendering component. React will discard its output and immediately attempt to render it again with the new state. This pattern is rarely needed, but you can use it to **store information from the previous renders**. [See an example below.](#storing-information-from-previous-renders) +* Gọi hàm `set` *trong quá trình render* chỉ được phép từ bên trong component đang render. React sẽ loại bỏ đầu ra của nó và ngay lập tức cố gắng render lại với trạng thái mới. Mẫu này hiếm khi cần thiết, nhưng bạn có thể sử dụng nó để **lưu trữ thông tin từ các lần render trước**. [Xem một ví dụ bên dưới.](#storing-information-from-previous-renders) -* In Strict Mode, React will **call your updater function twice** in order to [help you find accidental impurities.](#my-initializer-or-updater-function-runs-twice) This is development-only behavior and does not affect production. If your updater function is pure (as it should be), this should not affect the behavior. The result from one of the calls will be ignored. +* Trong Strict Mode, React sẽ **gọi hàm cập nhật của bạn hai lần** để [giúp bạn tìm thấy các tạp chất vô tình.](#my-initializer-or-updater-function-runs-twice) Đây là hành vi chỉ dành cho quá trình phát triển và không ảnh hưởng đến sản xuất. Nếu hàm cập nhật của bạn là thuần khiết (như nó phải vậy), điều này sẽ không ảnh hưởng đến hành vi. Kết quả từ một trong các lệnh gọi sẽ bị bỏ qua. --- -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Adding state to a component {/*adding-state-to-a-component*/} +### Thêm trạng thái vào một component {/*adding-state-to-a-component*/} -Call `useState` at the top level of your component to declare one or more [state variables.](/learn/state-a-components-memory) +Gọi `useState` ở cấp cao nhất của component để khai báo một hoặc nhiều [biến trạng thái.](/learn/state-a-components-memory) ```js [[1, 4, "age"], [2, 4, "setAge"], [3, 4, "42"], [1, 5, "name"], [2, 5, "setName"], [3, 5, "'Taylor'"]] import { useState } from 'react'; @@ -108,14 +108,14 @@ function MyComponent() { // ... ``` -The convention is to name state variables like `[something, setSomething]` using [array destructuring.](https://javascript.info/destructuring-assignment) +Quy ước là đặt tên các biến trạng thái như `[something, setSomething]` bằng cách sử dụng [destructuring mảng.](https://javascript.info/destructuring-assignment) -`useState` returns an array with exactly two items: +`useState` trả về một mảng chính xác hai mục: -1. The <CodeStep step={1}>current state</CodeStep> of this state variable, initially set to the <CodeStep step={3}>initial state</CodeStep> you provided. -2. The <CodeStep step={2}>`set` function</CodeStep> that lets you change it to any other value in response to interaction. +1. <CodeStep step={1}>Trạng thái hiện tại</CodeStep> của biến trạng thái này, ban đầu được đặt thành <CodeStep step={3}>trạng thái ban đầu</CodeStep> mà bạn đã cung cấp. +2. Hàm <CodeStep step={2}>`set`</CodeStep> cho phép bạn thay đổi nó thành bất kỳ giá trị nào khác để đáp ứng với tương tác. -To update what’s on the screen, call the `set` function with some next state: +Để cập nhật những gì trên màn hình, hãy gọi hàm `set` với một số trạng thái tiếp theo: ```js [[2, 2, "setName"]] function handleClick() { @@ -123,28 +123,28 @@ function handleClick() { } ``` -React will store the next state, render your component again with the new values, and update the UI. +React sẽ lưu trữ trạng thái tiếp theo, render lại component của bạn với các giá trị mới và cập nhật UI. <Pitfall> -Calling the `set` function [**does not** change the current state in the already executing code](#ive-updated-the-state-but-logging-gives-me-the-old-value): +Gọi hàm `set` [**không** thay đổi trạng thái hiện tại trong mã đã thực thi](#ive-updated-the-state-but-logging-gives-me-the-old-value): ```js {3} function handleClick() { setName('Robin'); - console.log(name); // Still "Taylor"! + console.log(name); // Vẫn là "Taylor"! } ``` -It only affects what `useState` will return starting from the *next* render. +Nó chỉ ảnh hưởng đến những gì `useState` sẽ trả về bắt đầu từ lần render *tiếp theo*. </Pitfall> -<Recipes titleText="Basic useState examples" titleId="examples-basic"> +<Recipes titleText="Các ví dụ cơ bản về useState" titleId="examples-basic"> -#### Counter (number) {/*counter-number*/} +#### Bộ đếm (số) {/*counter-number*/} -In this example, the `count` state variable holds a number. Clicking the button increments it. +Trong ví dụ này, biến trạng thái `count` giữ một số. Nhấp vào nút sẽ tăng nó. <Sandpack> @@ -170,9 +170,9 @@ export default function Counter() { <Solution /> -#### Text field (string) {/*text-field-string*/} +#### Trường văn bản (chuỗi) {/*text-field-string*/} -In this example, the `text` state variable holds a string. When you type, `handleChange` reads the latest input value from the browser input DOM element, and calls `setText` to update the state. This allows you to display the current `text` below. +Trong ví dụ này, biến trạng thái `text` giữ một chuỗi. Khi bạn nhập, `handleChange` đọc giá trị đầu vào mới nhất từ phần tử DOM đầu vào của trình duyệt và gọi `setText` để cập nhật trạng thái. Điều này cho phép bạn hiển thị `text` hiện tại bên dưới. <Sandpack> @@ -202,9 +202,9 @@ export default function MyInput() { <Solution /> -#### Checkbox (boolean) {/*checkbox-boolean*/} +#### Hộp kiểm (boolean) {/*checkbox-boolean*/} -In this example, the `liked` state variable holds a boolean. When you click the input, `setLiked` updates the `liked` state variable with whether the browser checkbox input is checked. The `liked` variable is used to render the text below the checkbox. +Trong ví dụ này, biến trạng thái `liked` giữ một boolean. Khi bạn nhấp vào đầu vào, `setLiked` cập nhật biến trạng thái `liked` với việc đầu vào hộp kiểm của trình duyệt có được chọn hay không. Biến `liked` được sử dụng để render văn bản bên dưới hộp kiểm. <Sandpack> @@ -238,9 +238,9 @@ export default function MyCheckbox() { <Solution /> -#### Form (two variables) {/*form-two-variables*/} +#### Biểu mẫu (hai biến) {/*form-two-variables*/} -You can declare more than one state variable in the same component. Each state variable is completely independent. +Bạn có thể khai báo nhiều hơn một biến trạng thái trong cùng một component. Mỗi biến trạng thái hoàn toàn độc lập. <Sandpack> @@ -278,9 +278,10 @@ button { display: block; margin-top: 10px; } --- -### Updating state based on the previous state {/*updating-state-based-on-the-previous-state*/} +### Cập nhật trạng thái dựa trên trạng thái trước đó {/*updating-state-based-on-the-previous-state*/} + +Giả sử `age` là `42`. Trình xử lý này gọi `setAge(age + 1)` ba lần: -Suppose the `age` is `42`. This handler calls `setAge(age + 1)` three times: ```js function handleClick() { @@ -289,10 +290,9 @@ function handleClick() { setAge(age + 1); // setAge(42 + 1) } ``` +Tuy nhiên, sau một lần nhấp, `age` sẽ chỉ là `43` thay vì `45`! Điều này là do việc gọi hàm `set` [không cập nhật](/learn/state-as-a-snapshot) biến trạng thái `age` trong mã đang chạy. Vì vậy, mỗi lệnh gọi `setAge(age + 1)` trở thành `setAge(43)`. -However, after one click, `age` will only be `43` rather than `45`! This is because calling the `set` function [does not update](/learn/state-as-a-snapshot) the `age` state variable in the already running code. So each `setAge(age + 1)` call becomes `setAge(43)`. - -To solve this problem, **you may pass an *updater function*** to `setAge` instead of the next state: +Để giải quyết vấn đề này, **bạn có thể truyền một *hàm cập nhật*** cho `setAge` thay vì trạng thái tiếp theo: ```js [[1, 2, "a", 0], [2, 2, "a + 1"], [1, 3, "a", 0], [2, 3, "a + 1"], [1, 4, "a", 0], [2, 4, "a + 1"]] function handleClick() { @@ -302,39 +302,39 @@ function handleClick() { } ``` -Here, `a => a + 1` is your updater function. It takes the <CodeStep step={1}>pending state</CodeStep> and calculates the <CodeStep step={2}>next state</CodeStep> from it. +Ở đây, `a => a + 1` là hàm cập nhật của bạn. Nó lấy <CodeStep step={1}>trạng thái đang chờ xử lý</CodeStep> và tính toán <CodeStep step={2}>trạng thái tiếp theo</CodeStep> từ đó. -React puts your updater functions in a [queue.](/learn/queueing-a-series-of-state-updates) Then, during the next render, it will call them in the same order: +React đặt các hàm cập nhật của bạn vào một [hàng đợi.](/learn/queueing-a-series-of-state-updates) Sau đó, trong quá trình render tiếp theo, nó sẽ gọi chúng theo cùng một thứ tự: -1. `a => a + 1` will receive `42` as the pending state and return `43` as the next state. -1. `a => a + 1` will receive `43` as the pending state and return `44` as the next state. -1. `a => a + 1` will receive `44` as the pending state and return `45` as the next state. +1. `a => a + 1` sẽ nhận `42` làm trạng thái đang chờ xử lý và trả về `43` làm trạng thái tiếp theo. +2. `a => a + 1` sẽ nhận `43` làm trạng thái đang chờ xử lý và trả về `44` làm trạng thái tiếp theo. +3. `a => a + 1` sẽ nhận `44` làm trạng thái đang chờ xử lý và trả về `45` làm trạng thái tiếp theo. -There are no other queued updates, so React will store `45` as the current state in the end. +Không có bản cập nhật nào khác đang chờ xử lý, vì vậy React sẽ lưu trữ `45` làm trạng thái hiện tại cuối cùng. -By convention, it's common to name the pending state argument for the first letter of the state variable name, like `a` for `age`. However, you may also call it like `prevAge` or something else that you find clearer. +Theo quy ước, người ta thường đặt tên đối số trạng thái đang chờ xử lý cho chữ cái đầu tiên của tên biến trạng thái, chẳng hạn như `a` cho `age`. Tuy nhiên, bạn cũng có thể gọi nó là `prevAge` hoặc một cái gì đó khác mà bạn thấy rõ ràng hơn. -React may [call your updaters twice](#my-initializer-or-updater-function-runs-twice) in development to verify that they are [pure.](/learn/keeping-components-pure) +React có thể [gọi các hàm cập nhật của bạn hai lần](#my-initializer-or-updater-function-runs-twice) trong quá trình phát triển để xác minh rằng chúng [thuần khiết.](/learn/keeping-components-pure) <DeepDive> -#### Is using an updater always preferred? {/*is-using-an-updater-always-preferred*/} +#### Có phải việc sử dụng hàm cập nhật luôn được ưu tiên? {/*is-using-an-updater-always-preferred*/} -You might hear a recommendation to always write code like `setAge(a => a + 1)` if the state you're setting is calculated from the previous state. There is no harm in it, but it is also not always necessary. +Bạn có thể nghe một khuyến nghị là luôn viết mã như `setAge(a => a + 1)` nếu trạng thái bạn đang đặt được tính từ trạng thái trước đó. Không có hại gì trong đó, nhưng nó cũng không phải lúc nào cũng cần thiết. -In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the `age` state variable would be updated before the next click. This means there is no risk of a click handler seeing a "stale" `age` at the beginning of the event handler. +Trong hầu hết các trường hợp, không có sự khác biệt giữa hai cách tiếp cận này. React luôn đảm bảo rằng đối với các hành động cố ý của người dùng, chẳng hạn như nhấp chuột, biến trạng thái `age` sẽ được cập nhật trước lần nhấp tiếp theo. Điều này có nghĩa là không có rủi ro nào khi trình xử lý nhấp chuột nhìn thấy một `age` "cũ" khi bắt đầu trình xử lý sự kiện. -However, if you do multiple updates within the same event, updaters can be helpful. They're also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders). +Tuy nhiên, nếu bạn thực hiện nhiều cập nhật trong cùng một sự kiện, thì hàm cập nhật có thể hữu ích. Chúng cũng hữu ích nếu việc truy cập chính biến trạng thái là bất tiện (bạn có thể gặp phải điều này khi tối ưu hóa việc render lại). -If you prefer consistency over slightly more verbose syntax, it's reasonable to always write an updater if the state you're setting is calculated from the previous state. If it's calculated from the previous state of some *other* state variable, you might want to combine them into one object and [use a reducer.](/learn/extracting-state-logic-into-a-reducer) +Nếu bạn thích tính nhất quán hơn là cú pháp dài dòng hơn một chút, thì việc luôn viết một hàm cập nhật nếu trạng thái bạn đang đặt được tính từ trạng thái trước đó là hợp lý. Nếu nó được tính từ trạng thái trước đó của một số biến trạng thái *khác*, bạn có thể muốn kết hợp chúng thành một đối tượng và [sử dụng một reducer.](/learn/extracting-state-logic-into-a-reducer) </DeepDive> -<Recipes titleText="The difference between passing an updater and passing the next state directly" titleId="examples-updater"> +<Recipes titleText="Sự khác biệt giữa việc truyền một hàm cập nhật và truyền trực tiếp trạng thái tiếp theo" titleId="examples-updater"> -#### Passing the updater function {/*passing-the-updater-function*/} +#### Truyền hàm cập nhật {/*passing-the-updater-function*/} -This example passes the updater function, so the "+3" button works. +Ví dụ này truyền hàm cập nhật, vì vậy nút "+3" hoạt động. <Sandpack> @@ -373,9 +373,9 @@ h1 { display: block; margin: 10px; } <Solution /> -#### Passing the next state directly {/*passing-the-next-state-directly*/} +#### Truyền trực tiếp trạng thái tiếp theo {/*passing-the-next-state-directly*/} -This example **does not** pass the updater function, so the "+3" button **doesn't work as intended**. +Ví dụ này **không** truyền hàm cập nhật, vì vậy nút "+3" **không hoạt động như dự định**. <Sandpack> @@ -418,32 +418,32 @@ h1 { display: block; margin: 10px; } --- -### Updating objects and arrays in state {/*updating-objects-and-arrays-in-state*/} +### Cập nhật các đối tượng và mảng trong trạng thái {/*updating-objects-and-arrays-in-state*/} -You can put objects and arrays into state. In React, state is considered read-only, so **you should *replace* it rather than *mutate* your existing objects**. For example, if you have a `form` object in state, don't mutate it: +Bạn có thể đặt các đối tượng và mảng vào trạng thái. Trong React, trạng thái được coi là chỉ đọc, vì vậy **bạn nên *thay thế* nó thay vì *mutate* các đối tượng hiện có của bạn**. Ví dụ: nếu bạn có một đối tượng `form` trong trạng thái, đừng mutate nó: ```js -// 🚩 Don't mutate an object in state like this: +// 🚩 Đừng mutate một đối tượng trong trạng thái như thế này: form.firstName = 'Taylor'; ``` -Instead, replace the whole object by creating a new one: +Thay vào đó, hãy thay thế toàn bộ đối tượng bằng cách tạo một đối tượng mới: ```js -// ✅ Replace state with a new object +// ✅ Thay thế trạng thái bằng một đối tượng mới setForm({ ...form, firstName: 'Taylor' }); ``` -Read [updating objects in state](/learn/updating-objects-in-state) and [updating arrays in state](/learn/updating-arrays-in-state) to learn more. +Đọc [cập nhật các đối tượng trong trạng thái](/learn/updating-objects-in-state) và [cập nhật các mảng trong trạng thái](/learn/updating-arrays-in-state) để tìm hiểu thêm. -<Recipes titleText="Examples of objects and arrays in state" titleId="examples-objects"> +<Recipes titleText="Ví dụ về các đối tượng và mảng trong trạng thái" titleId="examples-objects"> -#### Form (object) {/*form-object*/} +#### Form (đối tượng) {/*form-object*/} -In this example, the `form` state variable holds an object. Each input has a change handler that calls `setForm` with the next state of the entire form. The `{ ...form }` spread syntax ensures that the state object is replaced rather than mutated. +Trong ví dụ này, biến trạng thái `form` giữ một đối tượng. Mỗi input có một trình xử lý thay đổi gọi `setForm` với trạng thái tiếp theo của toàn bộ form. Cú pháp spread `{ ...form }` đảm bảo rằng đối tượng trạng thái được thay thế chứ không phải bị mutate. <Sandpack> @@ -514,9 +514,9 @@ input { margin-left: 5px; } <Solution /> -#### Form (nested object) {/*form-nested-object*/} +#### Form (đối tượng lồng nhau) {/*form-nested-object*/} -In this example, the state is more nested. When you update nested state, you need to create a copy of the object you're updating, as well as any objects "containing" it on the way upwards. Read [updating a nested object](/learn/updating-objects-in-state#updating-a-nested-object) to learn more. +Trong ví dụ này, trạng thái được lồng sâu hơn. Khi bạn cập nhật trạng thái lồng nhau, bạn cần tạo một bản sao của đối tượng bạn đang cập nhật, cũng như bất kỳ đối tượng nào "chứa" nó trên đường đi lên. Đọc [cập nhật một đối tượng lồng nhau](/learn/updating-objects-in-state#updating-a-nested-object) để tìm hiểu thêm. <Sandpack> @@ -626,9 +626,9 @@ img { width: 200px; height: 200px; } <Solution /> -#### List (array) {/*list-array*/} +#### Danh sách (mảng) {/*list-array*/} -In this example, the `todos` state variable holds an array. Each button handler calls `setTodos` with the next version of that array. The `[...todos]` spread syntax, `todos.map()` and `todos.filter()` ensure the state array is replaced rather than mutated. +Trong ví dụ này, biến trạng thái `todos` giữ một mảng. Mỗi trình xử lý nút gọi `setTodos` với phiên bản tiếp theo của mảng đó. Cú pháp spread `[...todos]`, `todos.map()` và `todos.filter()` đảm bảo mảng trạng thái được thay thế chứ không phải bị mutate. <Sandpack> @@ -793,9 +793,9 @@ ul, li { margin: 0; padding: 0; } <Solution /> -#### Writing concise update logic with Immer {/*writing-concise-update-logic-with-immer*/} +#### Viết logic cập nhật ngắn gọn với Immer {/*writing-concise-update-logic-with-immer*/} -If updating arrays and objects without mutation feels tedious, you can use a library like [Immer](https://github.com/immerjs/use-immer) to reduce repetitive code. Immer lets you write concise code as if you were mutating objects, but under the hood it performs immutable updates: +Nếu việc cập nhật các mảng và đối tượng mà không cần mutation cảm thấy tẻ nhạt, bạn có thể sử dụng một thư viện như [Immer](https://github.com/immerjs/use-immer) để giảm mã lặp đi lặp lại. Immer cho phép bạn viết mã ngắn gọn như thể bạn đang mutate các đối tượng, nhưng bên dưới nó thực hiện các cập nhật bất biến: <Sandpack> @@ -884,9 +884,9 @@ function ItemList({ artworks, onToggle }) { --- -### Avoiding recreating the initial state {/*avoiding-recreating-the-initial-state*/} +### Tránh tạo lại trạng thái ban đầu {/*avoiding-recreating-the-initial-state*/} -React saves the initial state once and ignores it on the next renders. +React lưu trạng thái ban đầu một lần và bỏ qua nó trong các lần render tiếp theo. ```js function TodoList() { @@ -894,9 +894,9 @@ function TodoList() { // ... ``` -Although the result of `createInitialTodos()` is only used for the initial render, you're still calling this function on every render. This can be wasteful if it's creating large arrays or performing expensive calculations. +Mặc dù kết quả của `createInitialTodos()` chỉ được sử dụng cho lần render ban đầu, bạn vẫn đang gọi hàm này trên mỗi lần render. Điều này có thể gây lãng phí nếu nó tạo ra các mảng lớn hoặc thực hiện các tính toán tốn kém. -To solve this, you may **pass it as an _initializer_ function** to `useState` instead: +Để giải quyết vấn đề này, bạn có thể **truyền nó như một hàm _khởi tạo_** cho `useState` thay thế: ```js function TodoList() { @@ -904,15 +904,15 @@ function TodoList() { // ... ``` -Notice that you’re passing `createInitialTodos`, which is the *function itself*, and not `createInitialTodos()`, which is the result of calling it. If you pass a function to `useState`, React will only call it during initialization. +Lưu ý rằng bạn đang truyền `createInitialTodos`, là *chính hàm*, chứ không phải `createInitialTodos()`, là kết quả của việc gọi nó. Nếu bạn truyền một hàm cho `useState`, React sẽ chỉ gọi nó trong quá trình khởi tạo. -React may [call your initializers twice](#my-initializer-or-updater-function-runs-twice) in development to verify that they are [pure.](/learn/keeping-components-pure) +React có thể [gọi các hàm khởi tạo của bạn hai lần](#my-initializer-or-updater-function-runs-twice) trong quá trình phát triển để xác minh rằng chúng [thuần khiết.](/learn/keeping-components-pure) -<Recipes titleText="The difference between passing an initializer and passing the initial state directly" titleId="examples-initializer"> +<Recipes titleText="Sự khác biệt giữa việc truyền một hàm khởi tạo và truyền trực tiếp trạng thái ban đầu" titleId="examples-initializer"> -#### Passing the initializer function {/*passing-the-initializer-function*/} +#### Truyền hàm khởi tạo {/*passing-the-initializer-function*/} -This example passes the initializer function, so the `createInitialTodos` function only runs during initialization. It does not run when component re-renders, such as when you type into the input. +Ví dụ này truyền hàm khởi tạo, vì vậy hàm `createInitialTodos` chỉ chạy trong quá trình khởi tạo. Nó không chạy khi component re-render, chẳng hạn như khi bạn nhập vào input. <Sandpack> @@ -963,9 +963,9 @@ export default function TodoList() { <Solution /> -#### Passing the initial state directly {/*passing-the-initial-state-directly*/} +#### Truyền trực tiếp trạng thái ban đầu {/*passing-the-initial-state-directly*/} -This example **does not** pass the initializer function, so the `createInitialTodos` function runs on every render, such as when you type into the input. There is no observable difference in behavior, but this code is less efficient. +Ví dụ này **không** truyền hàm khởi tạo, vì vậy hàm `createInitialTodos` chạy trên mỗi lần render, chẳng hạn như khi bạn nhập vào input. Không có sự khác biệt có thể quan sát được trong hành vi, nhưng mã này kém hiệu quả hơn. <Sandpack> @@ -1020,13 +1020,13 @@ export default function TodoList() { --- -### Resetting state with a key {/*resetting-state-with-a-key*/} +### Đặt lại trạng thái bằng một key {/*resetting-state-with-a-key*/} -You'll often encounter the `key` attribute when [rendering lists.](/learn/rendering-lists) However, it also serves another purpose. +Bạn sẽ thường gặp thuộc tính `key` khi [render danh sách.](/learn/rendering-lists) Tuy nhiên, nó cũng phục vụ một mục đích khác. -You can **reset a component's state by passing a different `key` to a component.** In this example, the Reset button changes the `version` state variable, which we pass as a `key` to the `Form`. When the `key` changes, React re-creates the `Form` component (and all of its children) from scratch, so its state gets reset. +Bạn có thể **đặt lại trạng thái của một component bằng cách truyền một `key` khác cho component đó.** Trong ví dụ này, nút Reset thay đổi biến trạng thái `version`, mà chúng ta truyền dưới dạng `key` cho `Form`. Khi `key` thay đổi, React sẽ tạo lại component `Form` (và tất cả các children của nó) từ đầu, vì vậy trạng thái của nó sẽ được đặt lại. -Read [preserving and resetting state](/learn/preserving-and-resetting-state) to learn more. +Đọc [duy trì và đặt lại trạng thái](/learn/preserving-and-resetting-state) để tìm hiểu thêm. <Sandpack> @@ -1071,19 +1071,19 @@ button { display: block; margin-bottom: 20px; } --- -### Storing information from previous renders {/*storing-information-from-previous-renders*/} +### Lưu trữ thông tin từ các lần render trước {/*storing-information-from-previous-renders*/} -Usually, you will update state in event handlers. However, in rare cases you might want to adjust state in response to rendering -- for example, you might want to change a state variable when a prop changes. +Thông thường, bạn sẽ cập nhật trạng thái trong các trình xử lý sự kiện. Tuy nhiên, trong một số trường hợp hiếm hoi, bạn có thể muốn điều chỉnh trạng thái để đáp ứng với việc render -- ví dụ: bạn có thể muốn thay đổi một biến trạng thái khi một prop thay đổi. -In most cases, you don't need this: +Trong hầu hết các trường hợp, bạn không cần điều này: -* **If the value you need can be computed entirely from the current props or other state, [remove that redundant state altogether.](/learn/choosing-the-state-structure#avoid-redundant-state)** If you're worried about recomputing too often, the [`useMemo` Hook](/reference/react/useMemo) can help. -* If you want to reset the entire component tree's state, [pass a different `key` to your component.](#resetting-state-with-a-key) -* If you can, update all the relevant state in the event handlers. +* **Nếu giá trị bạn cần có thể được tính toán hoàn toàn từ các prop hiện tại hoặc trạng thái khác, [hãy loại bỏ trạng thái dư thừa đó hoàn toàn.](/learn/choosing-the-state-structure#avoid-redundant-state)** Nếu bạn lo lắng về việc tính toán lại quá thường xuyên, thì [`useMemo Hook`](/reference/react/useMemo) có thể giúp ích. +* Nếu bạn muốn đặt lại trạng thái của toàn bộ cây component, [hãy truyền một `key` khác cho component của bạn.](#resetting-state-with-a-key) +* Nếu có thể, hãy cập nhật tất cả các trạng thái liên quan trong các trình xử lý sự kiện. -In the rare case that none of these apply, there is a pattern you can use to update state based on the values that have been rendered so far, by calling a `set` function while your component is rendering. +Trong trường hợp hiếm hoi mà không có điều nào trong số này áp dụng, có một mẫu bạn có thể sử dụng để cập nhật trạng thái dựa trên các giá trị đã được render cho đến nay, bằng cách gọi một hàm `set` trong khi component của bạn đang render. -Here's an example. This `CountLabel` component displays the `count` prop passed to it: +Đây là một ví dụ. Component `CountLabel` này hiển thị prop `count` được truyền cho nó: ```js src/CountLabel.js export default function CountLabel({ count }) { @@ -1091,7 +1091,8 @@ export default function CountLabel({ count }) { } ``` -Say you want to show whether the counter has *increased or decreased* since the last change. The `count` prop doesn't tell you this -- you need to keep track of its previous value. Add the `prevCount` state variable to track it. Add another state variable called `trend` to hold whether the count has increased or decreased. Compare `prevCount` with `count`, and if they're not equal, update both `prevCount` and `trend`. Now you can show both the current count prop and *how it has changed since the last render*. +Giả sử bạn muốn hiển thị xem bộ đếm đã *tăng hay giảm* kể từ lần thay đổi cuối cùng. Prop `count` không cho bạn biết điều này -- bạn cần theo dõi giá trị trước đó của nó. Thêm biến trạng thái `prevCount` để theo dõi nó. Thêm một biến trạng thái khác có tên là `trend` để giữ xem số lượng đã tăng hay giảm. So sánh `prevCount` với `count` và nếu chúng không bằng nhau, hãy cập nhật cả `prevCount` và `trend`. Bây giờ bạn có thể hiển thị cả prop số lượng hiện tại và *cách nó đã thay đổi kể từ lần render cuối cùng*. + <Sandpack> @@ -1139,35 +1140,34 @@ button { margin-bottom: 10px; } ``` </Sandpack> +Lưu ý rằng nếu bạn gọi một hàm `set` trong khi render, nó phải nằm trong một điều kiện như `prevCount !== count`, và phải có một lệnh gọi như `setPrevCount(count)` bên trong điều kiện đó. Nếu không, component của bạn sẽ re-render trong một vòng lặp cho đến khi nó bị crash. Ngoài ra, bạn chỉ có thể cập nhật trạng thái của component *đang render* theo cách này. Gọi hàm `set` của một component *khác* trong khi render là một lỗi. Cuối cùng, lệnh gọi `set` của bạn vẫn nên [cập nhật trạng thái mà không cần mutation](#updating-objects-and-arrays-in-state) -- điều này không có nghĩa là bạn có thể phá vỡ các quy tắc khác của [hàm thuần khiết.](/learn/keeping-components-pure) -Note that if you call a `set` function while rendering, it must be inside a condition like `prevCount !== count`, and there must be a call like `setPrevCount(count)` inside of the condition. Otherwise, your component would re-render in a loop until it crashes. Also, you can only update the state of the *currently rendering* component like this. Calling the `set` function of *another* component during rendering is an error. Finally, your `set` call should still [update state without mutation](#updating-objects-and-arrays-in-state) -- this doesn't mean you can break other rules of [pure functions.](/learn/keeping-components-pure) - -This pattern can be hard to understand and is usually best avoided. However, it's better than updating state in an effect. When you call the `set` function during render, React will re-render that component immediately after your component exits with a `return` statement, and before rendering the children. This way, children don't need to render twice. The rest of your component function will still execute (and the result will be thrown away). If your condition is below all the Hook calls, you may add an early `return;` to restart rendering earlier. +Mô hình này có thể khó hiểu và thường nên tránh. Tuy nhiên, nó tốt hơn là cập nhật trạng thái trong một effect. Khi bạn gọi hàm `set` trong quá trình render, React sẽ re-render component đó ngay sau khi component của bạn thoát bằng một câu lệnh `return` và trước khi render các children. Bằng cách này, children không cần phải render hai lần. Phần còn lại của hàm component của bạn vẫn sẽ thực thi (và kết quả sẽ bị loại bỏ). Nếu điều kiện của bạn nằm dưới tất cả các lệnh gọi Hook, bạn có thể thêm một `return;` sớm để khởi động lại quá trình render sớm hơn. --- -## Troubleshooting {/*troubleshooting*/} +## Khắc phục sự cố {/*troubleshooting*/} -### I've updated the state, but logging gives me the old value {/*ive-updated-the-state-but-logging-gives-me-the-old-value*/} +### Tôi đã cập nhật trạng thái, nhưng việc ghi log lại cho tôi giá trị cũ {/*ive-updated-the-state-but-logging-gives-me-the-old-value*/} -Calling the `set` function **does not change state in the running code**: +Gọi hàm `set` **không thay đổi trạng thái trong mã đang chạy**: ```js {4,5,8} function handleClick() { console.log(count); // 0 - setCount(count + 1); // Request a re-render with 1 - console.log(count); // Still 0! + setCount(count + 1); // Yêu cầu re-render với 1 + console.log(count); // Vẫn là 0! setTimeout(() => { - console.log(count); // Also 0! + console.log(count); // Cũng là 0! }, 5000); } ``` -This is because [states behaves like a snapshot.](/learn/state-as-a-snapshot) Updating state requests another render with the new state value, but does not affect the `count` JavaScript variable in your already-running event handler. +Điều này là do [trạng thái hoạt động như một snapshot.](/learn/state-as-a-snapshot) Cập nhật trạng thái yêu cầu một render khác với giá trị trạng thái mới, nhưng không ảnh hưởng đến biến JavaScript `count` trong trình xử lý sự kiện đang chạy của bạn. -If you need to use the next state, you can save it in a variable before passing it to the `set` function: +Nếu bạn cần sử dụng trạng thái tiếp theo, bạn có thể lưu nó trong một biến trước khi chuyển nó cho hàm `set`: ```js const nextCount = count + 1; @@ -1179,19 +1179,19 @@ console.log(nextCount); // 1 --- -### I've updated the state, but the screen doesn't update {/*ive-updated-the-state-but-the-screen-doesnt-update*/} +### Tôi đã cập nhật trạng thái, nhưng màn hình không cập nhật {/*ive-updated-the-state-but-the-screen-doesnt-update*/} -React will **ignore your update if the next state is equal to the previous state,** as determined by an [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. This usually happens when you change an object or an array in state directly: +React sẽ **bỏ qua bản cập nhật của bạn nếu trạng thái tiếp theo bằng với trạng thái trước đó,** như được xác định bởi so sánh [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Điều này thường xảy ra khi bạn thay đổi trực tiếp một đối tượng hoặc một mảng trong trạng thái: ```js -obj.x = 10; // 🚩 Wrong: mutating existing object -setObj(obj); // 🚩 Doesn't do anything +obj.x = 10; // 🚩 Sai: mutating đối tượng hiện có +setObj(obj); // 🚩 Không làm gì cả ``` -You mutated an existing `obj` object and passed it back to `setObj`, so React ignored the update. To fix this, you need to ensure that you're always [_replacing_ objects and arrays in state instead of _mutating_ them](#updating-objects-and-arrays-in-state): +Bạn đã mutate một đối tượng `obj` hiện có và chuyển nó trở lại `setObj`, vì vậy React đã bỏ qua bản cập nhật. Để khắc phục điều này, bạn cần đảm bảo rằng bạn luôn [_thay thế_ các đối tượng và mảng trong trạng thái thay vì _mutating_ chúng](#updating-objects-and-arrays-in-state): ```js -// ✅ Correct: creating a new object +// ✅ Đúng: tạo một đối tượng mới setObj({ ...obj, x: 10 @@ -1200,78 +1200,78 @@ setObj({ --- -### I'm getting an error: "Too many re-renders" {/*im-getting-an-error-too-many-re-renders*/} +### Tôi đang gặp lỗi: "Quá nhiều lần re-render" {/*im-getting-an-error-too-many-re-renders*/} -You might get an error that says: `Too many re-renders. React limits the number of renders to prevent an infinite loop.` Typically, this means that you're unconditionally setting state *during render*, so your component enters a loop: render, set state (which causes a render), render, set state (which causes a render), and so on. Very often, this is caused by a mistake in specifying an event handler: +Bạn có thể nhận được một lỗi cho biết: `Too many re-renders. React limits the number of renders to prevent an infinite loop.` (Quá nhiều lần re-render. React giới hạn số lần render để ngăn chặn một vòng lặp vô hạn.) Thông thường, điều này có nghĩa là bạn đang đặt trạng thái *trong quá trình render* một cách vô điều kiện, vì vậy component của bạn đi vào một vòng lặp: render, đặt trạng thái (gây ra một render), render, đặt trạng thái (gây ra một render), v.v. Rất thường xuyên, điều này là do một sai lầm trong việc chỉ định một trình xử lý sự kiện: ```js {1-2} -// 🚩 Wrong: calls the handler during render +// 🚩 Sai: gọi trình xử lý trong quá trình render return <button onClick={handleClick()}>Click me</button> -// ✅ Correct: passes down the event handler +// ✅ Đúng: chuyển trình xử lý sự kiện xuống return <button onClick={handleClick}>Click me</button> -// ✅ Correct: passes down an inline function +// ✅ Đúng: chuyển một hàm inline xuống return <button onClick={(e) => handleClick(e)}>Click me</button> ``` -If you can't find the cause of this error, click on the arrow next to the error in the console and look through the JavaScript stack to find the specific `set` function call responsible for the error. +Nếu bạn không thể tìm thấy nguyên nhân của lỗi này, hãy nhấp vào mũi tên bên cạnh lỗi trong bảng điều khiển và xem qua ngăn xếp JavaScript để tìm lệnh gọi hàm `set` cụ thể chịu trách nhiệm cho lỗi. --- -### My initializer or updater function runs twice {/*my-initializer-or-updater-function-runs-twice*/} +### Hàm khởi tạo hoặc hàm cập nhật của tôi chạy hai lần {/*my-initializer-or-updater-function-runs-twice*/} -In [Strict Mode](/reference/react/StrictMode), React will call some of your functions twice instead of once: +Trong [Strict Mode](/reference/react/StrictMode), React sẽ gọi một số hàm của bạn hai lần thay vì một lần: ```js {2,5-6,11-12} function TodoList() { - // This component function will run twice for every render. + // Hàm component này sẽ chạy hai lần cho mỗi lần render. const [todos, setTodos] = useState(() => { - // This initializer function will run twice during initialization. + // Hàm khởi tạo này sẽ chạy hai lần trong quá trình khởi tạo. return createTodos(); }); function handleClick() { setTodos(prevTodos => { - // This updater function will run twice for every click. + // Hàm cập nhật này sẽ chạy hai lần cho mỗi lần nhấp. return [...prevTodos, createTodo()]; }); } // ... ``` -This is expected and shouldn't break your code. +Điều này được mong đợi và không nên phá vỡ mã của bạn. -This **development-only** behavior helps you [keep components pure.](/learn/keeping-components-pure) React uses the result of one of the calls, and ignores the result of the other call. As long as your component, initializer, and updater functions are pure, this shouldn't affect your logic. However, if they are accidentally impure, this helps you notice the mistakes. +Hành vi **chỉ dành cho quá trình phát triển** này giúp bạn [giữ cho các component thuần khiết.](/learn/keeping-components-pure) React sử dụng kết quả của một trong các lệnh gọi và bỏ qua kết quả của lệnh gọi kia. Miễn là component, hàm khởi tạo và hàm cập nhật của bạn là thuần khiết, điều này sẽ không ảnh hưởng đến logic của bạn. Tuy nhiên, nếu chúng vô tình không thuần khiết, điều này sẽ giúp bạn nhận thấy những sai lầm. -For example, this impure updater function mutates an array in state: +Ví dụ: hàm cập nhật không thuần khiết này mutate một mảng trong trạng thái: ```js {2,3} setTodos(prevTodos => { - // 🚩 Mistake: mutating state + // 🚩 Sai lầm: mutating trạng thái prevTodos.push(createTodo()); }); ``` -Because React calls your updater function twice, you'll see the todo was added twice, so you'll know that there is a mistake. In this example, you can fix the mistake by [replacing the array instead of mutating it](#updating-objects-and-arrays-in-state): +Vì React gọi hàm cập nhật của bạn hai lần, bạn sẽ thấy todo đã được thêm hai lần, vì vậy bạn sẽ biết rằng có một sai lầm. Trong ví dụ này, bạn có thể sửa sai lầm bằng cách [thay thế mảng thay vì mutating nó](#updating-objects-and-arrays-in-state): ```js {2,3} setTodos(prevTodos => { - // ✅ Correct: replacing with new state + // ✅ Đúng: thay thế bằng trạng thái mới return [...prevTodos, createTodo()]; }); ``` -Now that this updater function is pure, calling it an extra time doesn't make a difference in behavior. This is why React calling it twice helps you find mistakes. **Only component, initializer, and updater functions need to be pure.** Event handlers don't need to be pure, so React will never call your event handlers twice. +Bây giờ hàm cập nhật này là thuần khiết, việc gọi nó thêm một lần không tạo ra sự khác biệt trong hành vi. Đây là lý do tại sao React gọi nó hai lần giúp bạn tìm thấy những sai lầm. **Chỉ các hàm component, hàm khởi tạo và hàm cập nhật cần phải thuần khiết.** Các trình xử lý sự kiện không cần phải thuần khiết, vì vậy React sẽ không bao giờ gọi các trình xử lý sự kiện của bạn hai lần. -Read [keeping components pure](/learn/keeping-components-pure) to learn more. +Đọc [giữ cho các component thuần khiết](/learn/keeping-components-pure) để tìm hiểu thêm. --- -### I'm trying to set state to a function, but it gets called instead {/*im-trying-to-set-state-to-a-function-but-it-gets-called-instead*/} +### Tôi đang cố gắng đặt trạng thái thành một hàm, nhưng nó lại được gọi thay vì được lưu trữ {/*im-trying-to-set-state-to-a-function-but-it-gets-called-instead*/} -You can't put a function into state like this: +Bạn không thể đặt một hàm vào trạng thái như thế này: ```js const [fn, setFn] = useState(someFunction); @@ -1281,7 +1281,7 @@ function handleClick() { } ``` -Because you're passing a function, React assumes that `someFunction` is an [initializer function](#avoiding-recreating-the-initial-state), and that `someOtherFunction` is an [updater function](#updating-state-based-on-the-previous-state), so it tries to call them and store the result. To actually *store* a function, you have to put `() =>` before them in both cases. Then React will store the functions you pass. +Vì bạn đang chuyển một hàm, React giả định rằng `someFunction` là một [hàm khởi tạo](#avoiding-recreating-the-initial-state) và `someOtherFunction` là một [hàm cập nhật](#updating-state-based-on-the-previous-state), vì vậy nó cố gắng gọi chúng và lưu trữ kết quả. Để thực sự *lưu trữ* một hàm, bạn phải đặt `() =>` trước chúng trong cả hai trường hợp. Sau đó, React sẽ lưu trữ các hàm bạn chuyển. ```js {1,4} const [fn, setFn] = useState(() => someFunction); diff --git a/src/content/reference/rsc/directives.md b/src/content/reference/rsc/directives.md index 42256de49..2a32600df 100644 --- a/src/content/reference/rsc/directives.md +++ b/src/content/reference/rsc/directives.md @@ -1,22 +1,22 @@ --- -title: Directives +title: Chỉ thị --- <RSC> -Directives are for use in [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). +Các chỉ thị được sử dụng trong [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). </RSC> <Intro> -Directives provide instructions to [bundlers compatible with React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). +Các chỉ thị cung cấp hướng dẫn cho [các trình đóng gói tương thích với React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). </Intro> --- -## Source code directives {/*source-code-directives*/} +## Chỉ thị mã nguồn {/*source-code-directives*/} -* [`'use client'`](/reference/rsc/use-client) lets you mark what code runs on the client. -* [`'use server'`](/reference/rsc/use-server) marks server-side functions that can be called from client-side code. +* [`'use client'`](/reference/rsc/use-client) cho phép bạn đánh dấu mã nào chạy trên máy khách. +* [`'use server'`](/reference/rsc/use-server) đánh dấu các hàm phía máy chủ có thể được gọi từ mã phía máy khách. diff --git a/src/content/reference/rsc/server-components.md b/src/content/reference/rsc/server-components.md index 9e6ab11eb..dbdf7b5cb 100644 --- a/src/content/reference/rsc/server-components.md +++ b/src/content/reference/rsc/server-components.md @@ -4,34 +4,34 @@ title: Server Components <RSC> -Server Components are for use in [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). +Server Components (Thành phần máy chủ) được sử dụng trong [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). </RSC> <Intro> -Server Components are a new type of Component that renders ahead of time, before bundling, in an environment separate from your client app or SSR server. +Server Components là một loại Component mới, được hiển thị trước, trước khi đóng gói, trong một môi trường tách biệt với ứng dụng client hoặc máy chủ SSR của bạn. </Intro> -This separate environment is the "server" in React Server Components. Server Components can run once at build time on your CI server, or they can be run for each request using a web server. +Môi trường riêng biệt này là "máy chủ" trong React Server Components. Server Components có thể chạy một lần tại thời điểm xây dựng trên máy chủ CI của bạn, hoặc chúng có thể được chạy cho mỗi yêu cầu bằng cách sử dụng một máy chủ web. <InlineToc /> <Note> -#### How do I build support for Server Components? {/*how-do-i-build-support-for-server-components*/} +#### Làm cách nào để xây dựng hỗ trợ cho Server Components? {/*how-do-i-build-support-for-server-components*/} -While React Server Components in React 19 are stable and will not break between minor versions, the underlying APIs used to implement a React Server Components bundler or framework do not follow semver and may break between minors in React 19.x. +Mặc dù React Server Components trong React 19 ổn định và sẽ không bị hỏng giữa các phiên bản nhỏ, nhưng các API cơ bản được sử dụng để triển khai trình đóng gói hoặc framework React Server Components không tuân theo semver và có thể bị hỏng giữa các phiên bản nhỏ trong React 19.x. -To support React Server Components as a bundler or framework, we recommend pinning to a specific React version, or using the Canary release. We will continue working with bundlers and frameworks to stabilize the APIs used to implement React Server Components in the future. +Để hỗ trợ React Server Components như một trình đóng gói hoặc framework, chúng tôi khuyên bạn nên ghim vào một phiên bản React cụ thể hoặc sử dụng bản phát hành Canary. Chúng tôi sẽ tiếp tục làm việc với các trình đóng gói và framework để ổn định các API được sử dụng để triển khai React Server Components trong tương lai. </Note> -### Server Components without a Server {/*server-components-without-a-server*/} -Server components can run at build time to read from the filesystem or fetch static content, so a web server is not required. For example, you may want to read static data from a content management system. +### Server Components không cần máy chủ {/*server-components-without-a-server*/} +Server components có thể chạy tại thời điểm xây dựng để đọc từ hệ thống tệp hoặc tìm nạp nội dung tĩnh, vì vậy không cần máy chủ web. Ví dụ: bạn có thể muốn đọc dữ liệu tĩnh từ hệ thống quản lý nội dung. -Without Server Components, it's common to fetch static data on the client with an Effect: +Nếu không có Server Components, thông thường bạn sẽ tìm nạp dữ liệu tĩnh trên máy khách bằng Effect: ```js // bundle.js import marked from 'marked'; // 35.9K (11.2K gzipped) @@ -58,13 +58,13 @@ app.get(`/api/content/:page`, async (req, res) => { }); ``` -This pattern means users need to download and parse an additional 75K (gzipped) of libraries, and wait for a second request to fetch the data after the page loads, just to render static content that will not change for the lifetime of the page. +Mô hình này có nghĩa là người dùng cần tải xuống và phân tích cú pháp thêm 75K (đã nén gzip) thư viện và đợi yêu cầu thứ hai để tìm nạp dữ liệu sau khi trang tải, chỉ để hiển thị nội dung tĩnh sẽ không thay đổi trong suốt thời gian tồn tại của trang. -With Server Components, you can render these components once at build time: +Với Server Components, bạn có thể hiển thị các component này một lần tại thời điểm xây dựng: ```js -import marked from 'marked'; // Not included in bundle -import sanitizeHtml from 'sanitize-html'; // Not included in bundle +import marked from 'marked'; // Không được bao gồm trong bundle +import sanitizeHtml from 'sanitize-html'; // Không được bao gồm trong bundle async function Page({page}) { // NOTE: loads *during* render, when the app is built. @@ -74,17 +74,17 @@ async function Page({page}) { } ``` -The rendered output can then be server-side rendered (SSR) to HTML and uploaded to a CDN. When the app loads, the client will not see the original `Page` component, or the expensive libraries for rendering the markdown. The client will only see the rendered output: +Đầu ra được hiển thị sau đó có thể được hiển thị phía máy chủ (SSR) thành HTML và tải lên CDN. Khi ứng dụng tải, máy khách sẽ không thấy component `Page` ban đầu hoặc các thư viện tốn kém để hiển thị markdown. Máy khách sẽ chỉ thấy đầu ra được hiển thị: ```js -<div><!-- html for markdown --></div> +<div>{/* html for markdown */}</div> ``` -This means the content is visible during first page load, and the bundle does not include the expensive libraries needed to render the static content. +Điều này có nghĩa là nội dung hiển thị trong lần tải trang đầu tiên và bundle không bao gồm các thư viện tốn kém cần thiết để hiển thị nội dung tĩnh. <Note> -You may notice that the Server Component above is an async function: +Bạn có thể nhận thấy rằng Server Component ở trên là một hàm async: ```js async function Page({page}) { @@ -92,16 +92,16 @@ async function Page({page}) { } ``` -Async Components are a new feature of Server Components that allow you to `await` in render. +Async Components là một tính năng mới của Server Components cho phép bạn `await` trong quá trình render. -See [Async components with Server Components](#async-components-with-server-components) below. +Xem [Async components with Server Components](#async-components-with-server-components) bên dưới. </Note> -### Server Components with a Server {/*server-components-with-a-server*/} -Server Components can also run on a web server during a request for a page, letting you access your data layer without having to build an API. They are rendered before your application is bundled, and can pass data and JSX as props to Client Components. +### Server Components với máy chủ {/*server-components-with-a-server*/} +Server Components cũng có thể chạy trên máy chủ web trong quá trình yêu cầu một trang, cho phép bạn truy cập lớp dữ liệu của mình mà không cần xây dựng API. Chúng được hiển thị trước khi ứng dụng của bạn được đóng gói và có thể chuyển dữ liệu và JSX làm đạo cụ cho Client Components. -Without Server Components, it's common to fetch dynamic data on the client in an Effect: +Nếu không có Server Components, thông thường bạn sẽ tìm nạp dữ liệu động trên máy khách trong Effect: ```js // bundle.js @@ -150,7 +150,7 @@ app.get(`/api/authors/:id`, async (req, res) => { }); ``` -With Server Components, you can read the data and render it in the component: +Với Server Components, bạn có thể đọc dữ liệu và hiển thị nó trong component: ```js import db from './database'; @@ -174,7 +174,7 @@ async function Author({id}) { } ``` -The bundler then combines the data, rendered Server Components and dynamic Client Components into a bundle. Optionally, that bundle can then be server-side rendered (SSR) to create the initial HTML for the page. When the page loads, the browser does not see the original `Note` and `Author` components; only the rendered output is sent to the client: +Sau đó, trình đóng gói kết hợp dữ liệu, Server Components được hiển thị và Client Components động thành một bundle. Tùy chọn, bundle đó sau đó có thể được hiển thị phía máy chủ (SSR) để tạo HTML ban đầu cho trang. Khi trang tải, trình duyệt không thấy các component `Note` và `Author` ban đầu; chỉ đầu ra được hiển thị được gửi đến máy khách: ```js <div> @@ -183,24 +183,24 @@ The bundler then combines the data, rendered Server Components and dynamic Clien </div> ``` -Server Components can be made dynamic by re-fetching them from a server, where they can access the data and render again. This new application architecture combines the simple “request/response” mental model of server-centric Multi-Page Apps with the seamless interactivity of client-centric Single-Page Apps, giving you the best of both worlds. +Server Components có thể được tạo động bằng cách tìm nạp lại chúng từ máy chủ, nơi chúng có thể truy cập dữ liệu và hiển thị lại. Kiến trúc ứng dụng mới này kết hợp mô hình tinh thần "yêu cầu/phản hồi" đơn giản của các Ứng dụng nhiều trang tập trung vào máy chủ với tính tương tác liền mạch của các Ứng dụng một trang tập trung vào máy khách, mang đến cho bạn những điều tốt nhất của cả hai thế giới. -### Adding interactivity to Server Components {/*adding-interactivity-to-server-components*/} +### Thêm tính tương tác vào Server Components {/*adding-interactivity-to-server-components*/} -Server Components are not sent to the browser, so they cannot use interactive APIs like `useState`. To add interactivity to Server Components, you can compose them with Client Component using the `"use client"` directive. +Server Components không được gửi đến trình duyệt, vì vậy chúng không thể sử dụng các API tương tác như `useState`. Để thêm tính tương tác vào Server Components, bạn có thể kết hợp chúng với Client Component bằng chỉ thị `"use client"`. <Note> -#### There is no directive for Server Components. {/*there-is-no-directive-for-server-components*/} +#### Không có chỉ thị cho Server Components. {/*there-is-no-directive-for-server-components*/} -A common misunderstanding is that Server Components are denoted by `"use server"`, but there is no directive for Server Components. The `"use server"` directive is used for Server Functions. +Một sự hiểu lầm phổ biến là Server Components được biểu thị bằng `"use server"`, nhưng không có chỉ thị nào cho Server Components. Chỉ thị `"use server"` được sử dụng cho Server Functions. -For more info, see the docs for [Directives](/reference/rsc/directives). +Để biết thêm thông tin, hãy xem tài liệu về [Directives](/reference/rsc/directives). </Note> -In the following example, the `Notes` Server Component imports an `Expandable` Client Component that uses state to toggle its `expanded` state: +Trong ví dụ sau, Server Component `Notes` nhập một Client Component `Expandable` sử dụng trạng thái để chuyển đổi trạng thái `expanded` của nó: ```js // Server Component import Expandable from './Expandable'; @@ -237,11 +237,11 @@ export default function Expandable({children}) { } ``` -This works by first rendering `Notes` as a Server Component, and then instructing the bundler to create a bundle for the Client Component `Expandable`. In the browser, the Client Components will see output of the Server Components passed as props: +Điều này hoạt động bằng cách hiển thị `Notes` dưới dạng Server Component trước, sau đó hướng dẫn trình đóng gói tạo một bundle cho Client Component `Expandable`. Trong trình duyệt, Client Components sẽ thấy đầu ra của Server Components được chuyển dưới dạng đạo cụ: ```js <head> - <!-- the bundle for Client Components --> + {/* the bundle for Client Components */} <script src="bundle.js" /> </head> <body> @@ -252,16 +252,16 @@ This works by first rendering `Notes` as a Server Component, and then instructin <Expandable key={2}> <p>this is the second note</p> </Expandable> - <!--...--> + {/*...*/} </div> </body> ``` -### Async components with Server Components {/*async-components-with-server-components*/} +### Async components với Server Components {/*async-components-with-server-components*/} -Server Components introduce a new way to write Components using async/await. When you `await` in an async component, React will suspend and wait for the promise to resolve before resuming rendering. This works across server/client boundaries with streaming support for Suspense. +Server Components giới thiệu một cách mới để viết Components bằng async/await. Khi bạn `await` trong một component async, React sẽ tạm dừng và đợi promise được giải quyết trước khi tiếp tục hiển thị. Điều này hoạt động trên các ranh giới máy chủ/máy khách với hỗ trợ phát trực tuyến cho Suspense. -You can even create a promise on the server, and await it on the client: +Bạn thậm chí có thể tạo một promise trên máy chủ và đợi nó trên máy khách: ```js // Server Component @@ -297,6 +297,6 @@ function Comments({commentsPromise}) { } ``` -The `note` content is important data for the page to render, so we `await` it on the server. The comments are below the fold and lower-priority, so we start the promise on the server, and wait for it on the client with the `use` API. This will Suspend on the client, without blocking the `note` content from rendering. +Nội dung `note` là dữ liệu quan trọng để trang hiển thị, vì vậy chúng tôi `await` nó trên máy chủ. Các comment nằm bên dưới phần hiển thị đầu tiên và có mức độ ưu tiên thấp hơn, vì vậy chúng tôi bắt đầu promise trên máy chủ và đợi nó trên máy khách bằng API `use`. Điều này sẽ tạm dừng trên máy khách mà không chặn nội dung `note` hiển thị. -Since async components are not supported on the client, we await the promise with `use`. +Vì các component async không được hỗ trợ trên máy khách, chúng tôi đợi promise bằng `use`. diff --git a/src/content/reference/rsc/server-functions.md b/src/content/reference/rsc/server-functions.md index d29693894..fca9d3911 100644 --- a/src/content/reference/rsc/server-functions.md +++ b/src/content/reference/rsc/server-functions.md @@ -4,15 +4,15 @@ title: Server Functions <RSC> -Server Functions are for use in [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). +Server Functions (Hàm máy chủ) dùng cho [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). -**Note:** Until September 2024, we referred to all Server Functions as "Server Actions". If a Server Function is passed to an action prop or called from inside an action then it is a Server Action, but not all Server Functions are Server Actions. The naming in this documentation has been updated to reflect that Server Functions can be used for multiple purposes. +**Lưu ý:** Đến tháng 9 năm 2024, chúng tôi gọi tất cả Server Functions là "Server Actions". Nếu một Server Function được truyền cho một thuộc tính action hoặc được gọi từ bên trong một action thì nó là một Server Action, nhưng không phải tất cả Server Functions đều là Server Actions. Tên gọi trong tài liệu này đã được cập nhật để phản ánh rằng Server Functions có thể được sử dụng cho nhiều mục đích. </RSC> <Intro> -Server Functions allow Client Components to call async functions executed on the server. +Server Functions cho phép Client Components gọi các hàm async được thực thi trên máy chủ. </Intro> @@ -20,23 +20,23 @@ Server Functions allow Client Components to call async functions executed on the <Note> -#### How do I build support for Server Functions? {/*how-do-i-build-support-for-server-functions*/} +#### Làm cách nào để xây dựng hỗ trợ cho Server Functions? {/*how-do-i-build-support-for-server-functions*/} -While Server Functions in React 19 are stable and will not break between minor versions, the underlying APIs used to implement Server Functions in a React Server Components bundler or framework do not follow semver and may break between minors in React 19.x. +Mặc dù Server Functions trong React 19 ổn định và sẽ không bị hỏng giữa các phiên bản nhỏ, nhưng các API cơ bản được sử dụng để triển khai Server Functions trong một bundler hoặc framework React Server Components không tuân theo semver và có thể bị hỏng giữa các phiên bản nhỏ trong React 19.x. -To support Server Functions as a bundler or framework, we recommend pinning to a specific React version, or using the Canary release. We will continue working with bundlers and frameworks to stabilize the APIs used to implement Server Functions in the future. +Để hỗ trợ Server Functions như một bundler hoặc framework, chúng tôi khuyên bạn nên ghim vào một phiên bản React cụ thể hoặc sử dụng bản phát hành Canary. Chúng tôi sẽ tiếp tục làm việc với các bundler và framework để ổn định các API được sử dụng để triển khai Server Functions trong tương lai. </Note> -When a Server Functions is defined with the [`"use server"`](/reference/rsc/use-server) directive, your framework will automatically create a reference to the server function, and pass that reference to the Client Component. When that function is called on the client, React will send a request to the server to execute the function, and return the result. +Khi một Server Function được định nghĩa với directive [`"use server"`](/reference/rsc/use-server), framework của bạn sẽ tự động tạo một tham chiếu đến server function và chuyển tham chiếu đó đến Client Component. Khi hàm đó được gọi trên client, React sẽ gửi một yêu cầu đến máy chủ để thực thi hàm và trả về kết quả. -Server Functions can be created in Server Components and passed as props to Client Components, or they can be imported and used in Client Components. +Server Functions có thể được tạo trong Server Components và được truyền dưới dạng props cho Client Components, hoặc chúng có thể được import và sử dụng trong Client Components. ## Usage {/*usage*/} -### Creating a Server Function from a Server Component {/*creating-a-server-function-from-a-server-component*/} +### Tạo Server Function từ Server Component {/*creating-a-server-function-from-a-server-component*/} -Server Components can define Server Functions with the `"use server"` directive: +Server Components có thể định nghĩa Server Functions với directive `”use server”`: ```js [[2, 7, "'use server'"], [1, 5, "createNoteAction"], [1, 12, "createNoteAction"]] // Server Component @@ -54,7 +54,7 @@ function EmptyNote () { } ``` -When React renders the `EmptyNote` Server Function, it will create a reference to the `createNoteAction` function, and pass that reference to the `Button` Client Component. When the button is clicked, React will send a request to the server to execute the `createNoteAction` function with the reference provided: +Khi React render Server Function `EmptyNote`, nó sẽ tạo một tham chiếu đến hàm `createNoteAction` và chuyển tham chiếu đó đến Client Component `Button`. Khi nút được nhấp, React sẽ gửi một yêu cầu đến máy chủ để thực thi hàm `createNoteAction` với tham chiếu được cung cấp: ```js {5} "use client"; @@ -66,12 +66,11 @@ export default function Button({onClick}) { } ``` -For more, see the docs for [`"use server"`](/reference/rsc/use-server). +Để biết thêm, hãy xem tài liệu cho [`"use server"`](/reference/rsc/use-server). +### Import Server Functions từ Client Components {/*importing-server-functions-from-client-components*/} -### Importing Server Functions from Client Components {/*importing-server-functions-from-client-components*/} - -Client Components can import Server Functions from files that use the `"use server"` directive: +Client Components có thể import Server Functions từ các file sử dụng directive `”use server”`: ```js [[1, 3, "createNote"]] "use server"; @@ -82,7 +81,7 @@ export async function createNote() { ``` -When the bundler builds the `EmptyNote` Client Component, it will create a reference to the `createNote` function in the bundle. When the `button` is clicked, React will send a request to the server to execute the `createNote` function using the reference provided: +Khi bundler xây dựng Client Component `EmptyNote`, nó sẽ tạo một tham chiếu đến hàm `createNote` trong bundle. Khi `button` được nhấp, React sẽ gửi một yêu cầu đến máy chủ để thực thi hàm `createNote` bằng tham chiếu được cung cấp: ```js [[1, 2, "createNote"], [1, 5, "createNote"], [1, 7, "createNote"]] "use client"; @@ -95,11 +94,11 @@ function EmptyNote() { } ``` -For more, see the docs for [`"use server"`](/reference/rsc/use-server). +Để biết thêm, hãy xem tài liệu cho [`"use server"`](/reference/rsc/use-server). -### Server Functions with Actions {/*server-functions-with-actions*/} +### Server Functions với Actions {/*server-functions-with-actions*/} -Server Functions can be called from Actions on the client: +Server Functions có thể được gọi từ Actions trên client: ```js [[1, 3, "updateName"]] "use server"; @@ -143,16 +142,15 @@ function UpdateName() { } ``` -This allows you to access the `isPending` state of the Server Function by wrapping it in an Action on the client. - -For more, see the docs for [Calling a Server Function outside of `<form>`](/reference/rsc/use-server#calling-a-server-function-outside-of-form) +Điều này cho phép bạn truy cập trạng thái `isPending` của Server Function bằng cách bọc nó trong một Action trên client. -### Server Functions with Form Actions {/*using-server-functions-with-form-actions*/} +Để biết thêm, hãy xem tài liệu cho [Gọi Server Function bên ngoài `<form>`](/reference/rsc/use-server#calling-a-server-function-outside-of-form) -Server Functions work with the new Form features in React 19. +### Server Functions với Form Actions {/*using-server-functions-with-form-actions*/} -You can pass a Server Function to a Form to automatically submit the form to the server: +Server Functions hoạt động với các tính năng Form mới trong React 19. +Bạn có thể chuyển một Server Function cho một Form để tự động gửi form đến máy chủ: ```js [[1, 3, "updateName"], [1, 7, "updateName"]] "use client"; @@ -168,13 +166,13 @@ function UpdateName() { } ``` -When the Form submission succeeds, React will automatically reset the form. You can add `useActionState` to access the pending state, last response, or to support progressive enhancement. +Khi quá trình gửi Form thành công, React sẽ tự động reset form. Bạn có thể thêm `useActionState` để truy cập trạng thái pending, phản hồi cuối cùng hoặc để hỗ trợ progressive enhancement. -For more, see the docs for [Server Functions in Forms](/reference/rsc/use-server#server-functions-in-forms). +Để biết thêm, hãy xem tài liệu cho [Server Functions trong Forms](/reference/rsc/use-server#server-functions-in-forms). -### Server Functions with `useActionState` {/*server-functions-with-use-action-state*/} +### Server Functions với `useActionState` {/*server-functions-with-use-action-state*/} -You can call Server Functions with `useActionState` for the common case where you just need access to the action pending state and last returned response: +Bạn có thể gọi Server Functions với `useActionState` cho trường hợp phổ biến khi bạn chỉ cần truy cập vào trạng thái pending của action và phản hồi cuối cùng được trả về: ```js [[1, 3, "updateName"], [1, 6, "updateName"], [2, 6, "submitAction"], [2, 9, "submitAction"]] "use client"; @@ -193,13 +191,13 @@ function UpdateName() { } ``` -When using `useActionState` with Server Functions, React will also automatically replay form submissions entered before hydration finishes. This means users can interact with your app even before the app has hydrated. +Khi sử dụng `useActionState` với Server Functions, React cũng sẽ tự động phát lại các lần gửi form được nhập trước khi quá trình hydration hoàn tất. Điều này có nghĩa là người dùng có thể tương tác với ứng dụng của bạn ngay cả trước khi ứng dụng được hydrate. -For more, see the docs for [`useActionState`](/reference/react-dom/hooks/useFormState). +Để biết thêm, hãy xem tài liệu cho [`useActionState`](/reference/react-dom/hooks/useFormState). -### Progressive enhancement with `useActionState` {/*progressive-enhancement-with-useactionstate*/} +### Progressive enhancement với `useActionState` {/*progressive-enhancement-with-useactionstate*/} -Server Functions also support progressive enhancement with the third argument of `useActionState`. +Server Functions cũng hỗ trợ progressive enhancement với đối số thứ ba của `useActionState`. ```js [[1, 3, "updateName"], [1, 6, "updateName"], [2, 6, "/name/update"], [3, 6, "submitAction"], [3, 9, "submitAction"]] "use client"; @@ -217,6 +215,6 @@ function UpdateName() { } ``` -When the <CodeStep step={2}>permalink</CodeStep> is provided to `useActionState`, React will redirect to the provided URL if the form is submitted before the JavaScript bundle loads. +Khi <CodeStep step={2}>permalink</CodeStep> được cung cấp cho `useActionState`, React sẽ chuyển hướng đến URL được cung cấp nếu form được gửi trước khi bundle JavaScript tải. -For more, see the docs for [`useActionState`](/reference/react-dom/hooks/useFormState). +Để biết thêm, hãy xem tài liệu cho [`useActionState`](/reference/react-dom/hooks/useFormState). diff --git a/src/content/reference/rsc/use-client.md b/src/content/reference/rsc/use-client.md index fe6f5b1ed..b1017daa0 100644 --- a/src/content/reference/rsc/use-client.md +++ b/src/content/reference/rsc/use-client.md @@ -1,18 +1,17 @@ --- title: "'use client'" -titleForTitleTag: "'use client' directive" +titleForTitleTag: "Chỉ thị `'use client'`" --- <RSC> -`'use client'` is for use with [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). +`'use client'` dùng để sử dụng với [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). </RSC> - <Intro> -`'use client'` lets you mark what code runs on the client. +`'use client'` cho phép bạn đánh dấu đoạn code nào chạy trên máy khách. </Intro> @@ -20,11 +19,11 @@ titleForTitleTag: "'use client' directive" --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `'use client'` {/*use-client*/} -Add `'use client'` at the top of a file to mark the module and its transitive dependencies as client code. +Thêm `'use client'` ở đầu tệp để đánh dấu mô-đun và các phụ thuộc bắc cầu của nó là mã máy khách. ```js {1} 'use client'; @@ -41,26 +40,26 @@ export default function RichTextEditor({ timestamp, text }) { } ``` -When a file marked with `'use client'` is imported from a Server Component, [compatible bundlers](/learn/start-a-new-react-project#bleeding-edge-react-frameworks) will treat the module import as a boundary between server-run and client-run code. +Khi một tệp được đánh dấu bằng `'use client'` được nhập từ một Server Component, [các trình đóng gói tương thích](/learn/start-a-new-react-project#bleeding-edge-react-frameworks) sẽ coi việc nhập mô-đun như một ranh giới giữa mã chạy trên máy chủ và mã chạy trên máy khách. -As dependencies of `RichTextEditor`, `formatDate` and `Button` will also be evaluated on the client regardless of whether their modules contain a `'use client'` directive. Note that a single module may be evaluated on the server when imported from server code and on the client when imported from client code. +Là các phụ thuộc của `RichTextEditor`, `formatDate` và `Button` cũng sẽ được đánh giá trên máy khách bất kể các mô-đun của chúng có chứa chỉ thị `'use client'` hay không. Lưu ý rằng một mô-đun duy nhất có thể được đánh giá trên máy chủ khi được nhập từ mã máy chủ và trên máy khách khi được nhập từ mã máy khách. -#### Caveats {/*caveats*/} +#### Lưu ý {/*caveats*/} -* `'use client'` must be at the very beginning of a file, above any imports or other code (comments are OK). They must be written with single or double quotes, but not backticks. -* When a `'use client'` module is imported from another client-rendered module, the directive has no effect. -* When a component module contains a `'use client'` directive, any usage of that component is guaranteed to be a Client Component. However, a component can still be evaluated on the client even if it does not have a `'use client'` directive. - * A component usage is considered a Client Component if it is defined in module with `'use client'` directive or when it is a transitive dependency of a module that contains a `'use client'` directive. Otherwise, it is a Server Component. -* Code that is marked for client evaluation is not limited to components. All code that is a part of the Client module sub-tree is sent to and run by the client. -* When a server evaluated module imports values from a `'use client'` module, the values must either be a React component or [supported serializable prop values](#passing-props-from-server-to-client-components) to be passed to a Client Component. Any other use case will throw an exception. +* `'use client'` phải ở ngay đầu tệp, phía trên mọi nhập hoặc mã khác (nhận xét thì OK). Chúng phải được viết bằng dấu nháy đơn hoặc dấu nháy kép, nhưng không phải dấu backtick. +* Khi một mô-đun `'use client'` được nhập từ một mô-đun được hiển thị phía máy khách khác, chỉ thị này không có hiệu lực. +* Khi một mô-đun thành phần chứa chỉ thị `'use client'`, mọi cách sử dụng thành phần đó đều được đảm bảo là một Client Component. Tuy nhiên, một thành phần vẫn có thể được đánh giá trên máy khách ngay cả khi nó không có chỉ thị `'use client'`. + * Một cách sử dụng thành phần được coi là một Client Component nếu nó được định nghĩa trong mô-đun có chỉ thị `'use client'` hoặc khi nó là một phụ thuộc bắc cầu của một mô-đun có chứa chỉ thị `'use client'`. Nếu không, nó là một Server Component. +* Mã được đánh dấu để đánh giá phía máy khách không giới hạn ở các thành phần. Tất cả mã là một phần của cây con mô-đun Client được gửi đến và chạy bởi máy khách. +* Khi một mô-đun được đánh giá trên máy chủ nhập các giá trị từ một mô-đun `'use client'`, các giá trị phải là một thành phần React hoặc [các giá trị prop tuần tự hóa được hỗ trợ](#passing-props-from-server-to-client-components) để được chuyển đến một Client Component. Bất kỳ trường hợp sử dụng nào khác sẽ gây ra một ngoại lệ. -### How `'use client'` marks client code {/*how-use-client-marks-client-code*/} +### Cách `'use client'` đánh dấu mã máy khách {/*how-use-client-marks-client-code*/} -In a React app, components are often split into separate files, or [modules](/learn/importing-and-exporting-components#exporting-and-importing-a-component). +Trong một ứng dụng React, các thành phần thường được chia thành các tệp riêng biệt, hoặc [mô-đun](/learn/importing-and-exporting-components#exporting-and-importing-a-component). -For apps that use React Server Components, the app is server-rendered by default. `'use client'` introduces a server-client boundary in the [module dependency tree](/learn/understanding-your-ui-as-a-tree#the-module-dependency-tree), effectively creating a subtree of Client modules. +Đối với các ứng dụng sử dụng React Server Components, ứng dụng được hiển thị phía máy chủ theo mặc định. `'use client'` giới thiệu một ranh giới máy chủ-máy khách trong [cây phụ thuộc mô-đun](/learn/understanding-your-ui-as-a-tree#the-module-dependency-tree), tạo ra một cây con các mô-đun Client một cách hiệu quả. -To better illustrate this, consider the following React Server Components app. +Để minh họa rõ hơn điều này, hãy xem xét ứng dụng React Server Components sau. <Sandpack> @@ -145,144 +144,142 @@ export default [ </Sandpack> -In the module dependency tree of this example app, the `'use client'` directive in `InspirationGenerator.js` marks that module and all of its transitive dependencies as Client modules. The subtree starting at `InspirationGenerator.js` is now marked as Client modules. +Trong cây phụ thuộc mô-đun của ứng dụng ví dụ này, chỉ thị `'use client'` trong `InspirationGenerator.js` đánh dấu mô-đun đó và tất cả các phụ thuộc bắc cầu của nó là các mô-đun Client. Cây con bắt đầu từ `InspirationGenerator.js` hiện được đánh dấu là các mô-đun Client. -<Diagram name="use_client_module_dependency" height={250} width={545} alt="A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'."> -`'use client'` segments the module dependency tree of the React Server Components app, marking `InspirationGenerator.js` and all of its dependencies as client-rendered. +<Diagram name="use_client_module_dependency" height={250} width={545} alt="Một biểu đồ cây với nút trên cùng đại diện cho mô-đun 'App.js'. 'App.js' có ba con: 'Copyright.js', 'FancyText.js' và 'InspirationGenerator.js'. 'InspirationGenerator.js' có hai con: 'FancyText.js' và 'inspirations.js'. Các nút bên dưới và bao gồm 'InspirationGenerator.js' có màu nền vàng để biểu thị rằng đồ thị con này được hiển thị phía máy khách do chỉ thị 'use client' trong 'InspirationGenerator.js'."> +`'use client'` phân đoạn cây phụ thuộc mô-đun của ứng dụng React Server Components, đánh dấu `InspirationGenerator.js` và tất cả các phụ thuộc của nó là được hiển thị phía máy khách. </Diagram> -During render, the framework will server-render the root component and continue through the [render tree](/learn/understanding-your-ui-as-a-tree#the-render-tree), opting-out of evaluating any code imported from client-marked code. +Trong quá trình hiển thị, framework sẽ hiển thị phía máy chủ thành phần gốc và tiếp tục thông qua [cây hiển thị](/learn/understanding-your-ui-as-a-tree#the-render-tree), chọn không đánh giá bất kỳ mã nào được nhập từ mã được đánh dấu là máy khách. -The server-rendered portion of the render tree is then sent to the client. The client, with its client code downloaded, then completes rendering the rest of the tree. +Phần được hiển thị phía máy chủ của cây hiển thị sau đó được gửi đến máy khách. Máy khách, với mã máy khách đã tải xuống, sau đó hoàn thành việc hiển thị phần còn lại của cây. -<Diagram name="use_client_render_tree" height={250} width={500} alt="A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered."> -The render tree for the React Server Components app. `InspirationGenerator` and its child component `FancyText` are components exported from client-marked code and considered Client Components. +<Diagram name="use_client_render_tree" height={250} width={500} alt="Một biểu đồ cây trong đó mỗi nút đại diện cho một thành phần và các con của nó là các thành phần con. Nút cấp cao nhất được gắn nhãn 'App' và nó có hai thành phần con 'InspirationGenerator' và 'FancyText'. 'InspirationGenerator' có hai thành phần con, 'FancyText' và 'Copyright'. Cả 'InspirationGenerator' và thành phần con của nó 'FancyText' đều được đánh dấu để được hiển thị phía máy khách."> +Cây hiển thị cho ứng dụng React Server Components. `InspirationGenerator` và thành phần con của nó `FancyText` là các thành phần được xuất từ mã được đánh dấu là máy khách và được coi là Client Components. </Diagram> -We introduce the following definitions: +Chúng tôi giới thiệu các định nghĩa sau: -* **Client Components** are components in a render tree that are rendered on the client. -* **Server Components** are components in a render tree that are rendered on the server. +* **Client Components** là các thành phần trong một cây hiển thị được hiển thị trên máy khách. +* **Server Components** là các thành phần trong một cây hiển thị được hiển thị trên máy chủ. -Working through the example app, `App`, `FancyText` and `Copyright` are all server-rendered and considered Server Components. As `InspirationGenerator.js` and its transitive dependencies are marked as client code, the component `InspirationGenerator` and its child component `FancyText` are Client Components. +Làm việc thông qua ứng dụng ví dụ, `App`, `FancyText` và `Copyright` đều được hiển thị phía máy chủ và được coi là Server Components. Vì `InspirationGenerator.js` và các phụ thuộc bắc cầu của nó được đánh dấu là mã máy khách, thành phần `InspirationGenerator` và thành phần con của nó `FancyText` là Client Components. <DeepDive> -#### How is `FancyText` both a Server and a Client Component? {/*how-is-fancytext-both-a-server-and-a-client-component*/} +#### Làm thế nào `FancyText` vừa là Server Component vừa là Client Component? {/*how-is-fancytext-both-a-server-and-a-client-component*/} -By the above definitions, the component `FancyText` is both a Server and Client Component, how can that be? +Theo các định nghĩa trên, thành phần `FancyText` vừa là Server Component vừa là Client Component, làm thế nào có thể như vậy? -First, let's clarify that the term "component" is not very precise. Here are just two ways "component" can be understood: +Đầu tiên, hãy làm rõ rằng thuật ngữ "thành phần" không chính xác lắm. Dưới đây chỉ là hai cách "thành phần" có thể được hiểu: -1. A "component" can refer to a **component definition**. In most cases this will be a function. +1. Một "thành phần" có thể đề cập đến một **định nghĩa thành phần**. Trong hầu hết các trường hợp, đây sẽ là một hàm. ```js -// This is a definition of a component +// Đây là một định nghĩa của một thành phần function MyComponent() { return <p>My Component</p> } ``` -2. A "component" can also refer to a **component usage** of its definition. +2. Một "thành phần" cũng có thể đề cập đến một **cách sử dụng thành phần** của định nghĩa của nó. ```js import MyComponent from './MyComponent'; function App() { - // This is a usage of a component + // Đây là một cách sử dụng của một thành phần return <MyComponent />; } ``` -Often, the imprecision is not important when explaining concepts, but in this case it is. - -When we talk about Server or Client Components, we are referring to component usages. +Thông thường, sự không chính xác không quan trọng khi giải thích các khái niệm, nhưng trong trường hợp này thì có. -* If the component is defined in a module with a `'use client'` directive, or the component is imported and called in a Client Component, then the component usage is a Client Component. -* Otherwise, the component usage is a Server Component. +Khi chúng ta nói về Server hoặc Client Components, chúng ta đang đề cập đến cách sử dụng thành phần. +* Nếu thành phần được định nghĩa trong một mô-đun có chỉ thị `'use client'`, hoặc thành phần được nhập và gọi trong một Client Component, thì cách sử dụng thành phần là một Client Component. +* Nếu không, cách sử dụng thành phần là một Server Component. -<Diagram name="use_client_render_tree" height={150} width={450} alt="A tree graph where each node represents a component and its children as child components. The top-level node is labelled 'App' and it has two child components 'InspirationGenerator' and 'FancyText'. 'InspirationGenerator' has two child components, 'FancyText' and 'Copyright'. Both 'InspirationGenerator' and its child component 'FancyText' are marked to be client-rendered.">A render tree illustrates component usages.</Diagram> +<Diagram name="use_client_render_tree" height={150} width={450} alt="Một biểu đồ cây trong đó mỗi nút đại diện cho một thành phần và các con của nó là các thành phần con. Nút cấp cao nhất được gắn nhãn 'App' và nó có hai thành phần con 'InspirationGenerator' và 'FancyText'. 'InspirationGenerator' có hai thành phần con, 'FancyText' và 'Copyright'. Cả 'InspirationGenerator' và thành phần con của nó 'FancyText' đều được đánh dấu để được hiển thị phía máy khách.">Một cây hiển thị minh họa cách sử dụng thành phần.</Diagram> -Back to the question of `FancyText`, we see that the component definition does _not_ have a `'use client'` directive and it has two usages. +Quay lại câu hỏi về `FancyText`, chúng ta thấy rằng định nghĩa thành phần _không_ có chỉ thị `'use client'` và nó có hai cách sử dụng. -The usage of `FancyText` as a child of `App`, marks that usage as a Server Component. When `FancyText` is imported and called under `InspirationGenerator`, that usage of `FancyText` is a Client Component as `InspirationGenerator` contains a `'use client'` directive. +Cách sử dụng `FancyText` như một con của `App`, đánh dấu cách sử dụng đó là một Server Component. Khi `FancyText` được nhập và gọi dưới `InspirationGenerator`, cách sử dụng `FancyText` đó là một Client Component vì `InspirationGenerator` chứa một chỉ thị `'use client'`. -This means that the component definition for `FancyText` will both be evaluated on the server and also downloaded by the client to render its Client Component usage. +Điều này có nghĩa là định nghĩa thành phần cho `FancyText` sẽ vừa được đánh giá trên máy chủ vừa được máy khách tải xuống để hiển thị cách sử dụng Client Component của nó. </DeepDive> <DeepDive> -#### Why is `Copyright` a Server Component? {/*why-is-copyright-a-server-component*/} +#### Tại sao `Copyright` là một Server Component? {/*why-is-copyright-a-server-component*/} -Because `Copyright` is rendered as a child of the Client Component `InspirationGenerator`, you might be surprised that it is a Server Component. +Vì `Copyright` được hiển thị như một con của Client Component `InspirationGenerator`, bạn có thể ngạc nhiên khi nó là một Server Component. -Recall that `'use client'` defines the boundary between server and client code on the _module dependency tree_, not the render tree. +Hãy nhớ lại rằng `'use client'` xác định ranh giới giữa mã máy chủ và mã máy khách trên _cây phụ thuộc mô-đun_, không phải cây hiển thị. -<Diagram name="use_client_module_dependency" height={200} width={500} alt="A tree graph with the top node representing the module 'App.js'. 'App.js' has three children: 'Copyright.js', 'FancyText.js', and 'InspirationGenerator.js'. 'InspirationGenerator.js' has two children: 'FancyText.js' and 'inspirations.js'. The nodes under and including 'InspirationGenerator.js' have a yellow background color to signify that this sub-graph is client-rendered due to the 'use client' directive in 'InspirationGenerator.js'."> -`'use client'` defines the boundary between server and client code on the module dependency tree. +<Diagram name="use_client_module_dependency" height={200} width={500} alt="Một biểu đồ cây với nút trên cùng đại diện cho mô-đun 'App.js'. 'App.js' có ba con: 'Copyright.js', 'FancyText.js' và 'InspirationGenerator.js'. 'InspirationGenerator.js' có hai con: 'FancyText.js' và 'inspirations.js'. Các nút bên dưới và bao gồm 'InspirationGenerator.js' có màu nền vàng để biểu thị rằng đồ thị con này được hiển thị phía máy khách do chỉ thị 'use client' trong 'InspirationGenerator.js'."> +`'use client'` xác định ranh giới giữa mã máy chủ và mã máy khách trên cây phụ thuộc mô-đun. </Diagram> -In the module dependency tree, we see that `App.js` imports and calls `Copyright` from the `Copyright.js` module. As `Copyright.js` does not contain a `'use client'` directive, the component usage is rendered on the server. `App` is rendered on the server as it is the root component. +Trong cây phụ thuộc mô-đun, chúng ta thấy rằng `App.js` nhập và gọi `Copyright` từ mô-đun `Copyright.js`. Vì `Copyright.js` không chứa chỉ thị `'use client'`, cách sử dụng thành phần được hiển thị trên máy chủ. `App` được hiển thị trên máy chủ vì nó là thành phần gốc. -Client Components can render Server Components because you can pass JSX as props. In this case, `InspirationGenerator` receives `Copyright` as [children](/learn/passing-props-to-a-component#passing-jsx-as-children). However, the `InspirationGenerator` module never directly imports the `Copyright` module nor calls the component, all of that is done by `App`. In fact, the `Copyright` component is fully executed before `InspirationGenerator` starts rendering. +Client Components có thể hiển thị Server Components vì bạn có thể chuyển JSX làm props. Trong trường hợp này, `InspirationGenerator` nhận `Copyright` làm [children](/learn/passing-props-to-a-component#passing-jsx-as-children). Tuy nhiên, mô-đun `InspirationGenerator` không bao giờ nhập trực tiếp mô-đun `Copyright` cũng như gọi thành phần, tất cả những điều đó được thực hiện bởi `App`. Trên thực tế, thành phần `Copyright` được thực thi đầy đủ trước khi `InspirationGenerator` bắt đầu hiển thị. -The takeaway is that a parent-child render relationship between components does not guarantee the same render environment. +Điều quan trọng là mối quan hệ hiển thị cha-con giữa các thành phần không đảm bảo cùng một môi trường hiển thị. </DeepDive> -### When to use `'use client'` {/*when-to-use-use-client*/} +### Khi nào nên sử dụng `'use client'` {/*when-to-use-use-client*/} -With `'use client'`, you can determine when components are Client Components. As Server Components are default, here is a brief overview of the advantages and limitations to Server Components to determine when you need to mark something as client rendered. +Với `'use client'`, bạn có thể xác định khi nào các thành phần là Client Components. Vì Server Components là mặc định, đây là một tổng quan ngắn gọn về những ưu điểm và hạn chế của Server Components để xác định khi nào bạn cần đánh dấu một cái gì đó là được hiển thị phía máy khách. -For simplicity, we talk about Server Components, but the same principles apply to all code in your app that is server run. +Để đơn giản, chúng ta nói về Server Components, nhưng các nguyên tắc tương tự áp dụng cho tất cả mã trong ứng dụng của bạn chạy trên máy chủ. -#### Advantages of Server Components {/*advantages*/} -* Server Components can reduce the amount of code sent and run by the client. Only Client modules are bundled and evaluated by the client. -* Server Components benefit from running on the server. They can access the local filesystem and may experience low latency for data fetches and network requests. +#### Ưu điểm của Server Components {/*advantages*/} +* Server Components có thể giảm lượng mã được gửi và chạy bởi máy khách. Chỉ các mô-đun Client được đóng gói và đánh giá bởi máy khách. +* Server Components được hưởng lợi từ việc chạy trên máy chủ. Chúng có thể truy cập hệ thống tệp cục bộ và có thể trải nghiệm độ trễ thấp cho các tìm nạp dữ liệu và yêu cầu mạng. -#### Limitations of Server Components {/*limitations*/} -* Server Components cannot support interaction as event handlers must be registered and triggered by a client. - * For example, event handlers like `onClick` can only be defined in Client Components. -* Server Components cannot use most Hooks. - * When Server Components are rendered, their output is essentially a list of components for the client to render. Server Components do not persist in memory after render and cannot have their own state. +#### Hạn chế của Server Components {/*limitations*/} +* Server Components không thể hỗ trợ tương tác vì các trình xử lý sự kiện phải được đăng ký và kích hoạt bởi một máy khách. + * Ví dụ: các trình xử lý sự kiện như `onClick` chỉ có thể được xác định trong Client Components. +* Server Components không thể sử dụng hầu hết các Hook. + * Khi Server Components được hiển thị, đầu ra của chúng về cơ bản là một danh sách các thành phần để máy khách hiển thị. Server Components không tồn tại trong bộ nhớ sau khi hiển thị và không thể có trạng thái riêng của chúng. -### Serializable types returned by Server Components {/*serializable-types*/} +### Các kiểu tuần tự hóa được trả về bởi Server Components {/*serializable-types*/} -As in any React app, parent components pass data to child components. As they are rendered in different environments, passing data from a Server Component to a Client Component requires extra consideration. +Như trong bất kỳ ứng dụng React nào, các thành phần cha chuyển dữ liệu cho các thành phần con. Vì chúng được hiển thị trong các môi trường khác nhau, việc chuyển dữ liệu từ một Server Component sang một Client Component đòi hỏi phải xem xét thêm. -Prop values passed from a Server Component to Client Component must be serializable. +Các giá trị prop được chuyển từ một Server Component sang Client Component phải có thể tuần tự hóa. -Serializable props include: -* Primitives - * [string](https://developer.mozilla.org/en-US/docs/Glossary/String) - * [number](https://developer.mozilla.org/en-US/docs/Glossary/Number) - * [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) - * [boolean](https://developer.mozilla.org/en-US/docs/Glossary/Boolean) - * [undefined](https://developer.mozilla.org/en-US/docs/Glossary/Undefined) - * [null](https://developer.mozilla.org/en-US/docs/Glossary/Null) - * [symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), only symbols registered in the global Symbol registry via [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) -* Iterables containing serializable values - * [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) - * [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) - * [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) - * [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) - * [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) and [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) +Các prop có thể tuần tự hóa bao gồm: +* Các kiểu nguyên thủy + * [string](https://developer.mozilla.org/en-US/docs/Glossary/String) + * [number](https://developer.mozilla.org/en-US/docs/Glossary/Number) + * [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) + * [boolean](https://developer.mozilla.org/en-US/docs/Glossary/Boolean) + * [undefined](https://developer.mozilla.org/en-US/docs/Glossary/Undefined) + * [null](https://developer.mozilla.org/en-US/docs/Glossary/Null) + * [symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), chỉ các symbol được đăng ký trong sổ đăng ký Symbol toàn cục thông qua [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) +* Các iterable chứa các giá trị có thể tuần tự hóa + * [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) + * [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) + * [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) + * [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) + * [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) và [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) * [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) -* Plain [objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object): those created with [object initializers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer), with serializable properties -* Functions that are [Server Functions](/reference/rsc/server-functions) -* Client or Server Component elements (JSX) +* Các [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) thuần túy: những object được tạo bằng [trình khởi tạo object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer), với các thuộc tính có thể tuần tự hóa +* Các hàm là [Server Functions](/reference/rsc/server-functions) +* Các phần tử Client hoặc Server Component (JSX) * [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) -Notably, these are not supported: -* [Functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) that are not exported from client-marked modules or marked with [`'use server'`](/reference/rsc/use-server) -* [Classes](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript) -* Objects that are instances of any class (other than the built-ins mentioned) or objects with [a null prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects) -* Symbols not registered globally, ex. `Symbol('my new symbol')` - +Đáng chú ý, những điều này không được hỗ trợ: +* [Các hàm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) không được xuất từ các mô-đun được đánh dấu là máy khách hoặc được đánh dấu bằng [`'use server'`](/reference/rsc/use-server) +* [Các lớp](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript) +* Các object là các thể hiện của bất kỳ lớp nào (ngoài các lớp dựng sẵn đã đề cập) hoặc các object có [prototype null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects) +* Các symbol không được đăng ký trên toàn cục, ví dụ: `Symbol('my new symbol')` -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Building with interactivity and state {/*building-with-interactivity-and-state*/} +### Xây dựng với tính tương tác và trạng thái {/*building-with-interactivity-and-state*/} <Sandpack> @@ -307,9 +304,9 @@ export default function Counter({initialValue = 0}) { </Sandpack> -As `Counter` requires both the `useState` Hook and event handlers to increment or decrement the value, this component must be a Client Component and will require a `'use client'` directive at the top. +Vì `Counter` yêu cầu cả Hook `useState` và các trình xử lý sự kiện để tăng hoặc giảm giá trị, thành phần này phải là một Client Component và sẽ yêu cầu chỉ thị `'use client'` ở trên cùng. -In contrast, a component that renders UI without interaction will not need to be a Client Component. +Ngược lại, một thành phần hiển thị UI mà không cần tương tác sẽ không cần phải là một Client Component. ```js import { readFile } from 'node:fs/promises'; @@ -321,9 +318,9 @@ export default async function CounterContainer() { } ``` -For example, `Counter`'s parent component, `CounterContainer`, does not require `'use client'` as it is not interactive and does not use state. In addition, `CounterContainer` must be a Server Component as it reads from the local file system on the server, which is possible only in a Server Component. +Ví dụ: thành phần cha của `Counter`, `CounterContainer`, không yêu cầu `'use client'` vì nó không tương tác và không sử dụng trạng thái. Ngoài ra, `CounterContainer` phải là một Server Component vì nó đọc từ hệ thống tệp cục bộ trên máy chủ, điều này chỉ có thể thực hiện được trong một Server Component. -There are also components that don't use any server or client-only features and can be agnostic to where they render. In our earlier example, `FancyText` is one such component. +Ngoài ra còn có các thành phần không sử dụng bất kỳ tính năng chỉ dành cho máy chủ hoặc máy khách nào và có thể không quan trọng nơi chúng hiển thị. Trong ví dụ trước của chúng ta, `FancyText` là một thành phần như vậy. ```js export default function FancyText({title, text}) { @@ -333,15 +330,15 @@ export default function FancyText({title, text}) { } ``` -In this case, we don't add the `'use client'` directive, resulting in `FancyText`'s _output_ (rather than its source code) to be sent to the browser when referenced from a Server Component. As demonstrated in the earlier Inspirations app example, `FancyText` is used as both a Server or Client Component, depending on where it is imported and used. +Trong trường hợp này, chúng ta không thêm chỉ thị `'use client'`, dẫn đến _đầu ra_ của `FancyText` (chứ không phải mã nguồn của nó) được gửi đến trình duyệt khi được tham chiếu từ một Server Component. Như đã trình bày trong ví dụ ứng dụng Inspirations trước đó, `FancyText` được sử dụng làm cả Server hoặc Client Component, tùy thuộc vào nơi nó được nhập và sử dụng. -But if `FancyText`'s HTML output was large relative to its source code (including dependencies), it might be more efficient to force it to always be a Client Component. Components that return a long SVG path string are one case where it may be more efficient to force a component to be a Client Component. +Nhưng nếu đầu ra HTML của `FancyText` lớn so với mã nguồn của nó (bao gồm cả các phụ thuộc), có thể hiệu quả hơn nếu buộc nó luôn là một Client Component. Các thành phần trả về một chuỗi đường dẫn SVG dài là một trường hợp mà có thể hiệu quả hơn nếu buộc một thành phần phải là một Client Component. -### Using client APIs {/*using-client-apis*/} +### Sử dụng API máy khách {/*using-client-apis*/} -Your React app may use client-specific APIs, such as the browser's APIs for web storage, audio and video manipulation, and device hardware, among [others](https://developer.mozilla.org/en-US/docs/Web/API). +Ứng dụng React của bạn có thể sử dụng các API dành riêng cho máy khách, chẳng hạn như API của trình duyệt để lưu trữ web, thao tác âm thanh và video và phần cứng thiết bị, cùng với [những API khác](https://developer.mozilla.org/en-US/docs/Web/API). -In this example, the component uses [DOM APIs](https://developer.mozilla.org/en-US/docs/Glossary/DOM) to manipulate a [`canvas`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas) element. Since those APIs are only available in the browser, it must be marked as a Client Component. +Trong ví dụ này, thành phần sử dụng [DOM API](https://developer.mozilla.org/en-US/docs/Glossary/DOM) để thao tác một phần tử [`canvas`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas). Vì các API đó chỉ khả dụng trong trình duyệt, nó phải được đánh dấu là một Client Component. ```js 'use client'; @@ -362,18 +359,18 @@ export default function Circle() { } ``` -### Using third-party libraries {/*using-third-party-libraries*/} +### Sử dụng thư viện của bên thứ ba {/*using-third-party-libraries*/} -Often in a React app, you'll leverage third-party libraries to handle common UI patterns or logic. +Thông thường trong một ứng dụng React, bạn sẽ tận dụng các thư viện của bên thứ ba để xử lý các mẫu hoặc logic UI phổ biến. -These libraries may rely on component Hooks or client APIs. Third-party components that use any of the following React APIs must run on the client: +Các thư viện này có thể dựa vào các Hook thành phần hoặc API máy khách. Các thành phần của bên thứ ba sử dụng bất kỳ API React nào sau đây phải chạy trên máy khách: * [createContext](/reference/react/createContext) -* [`react`](/reference/react/hooks) and [`react-dom`](/reference/react-dom/hooks) Hooks, excluding [`use`](/reference/react/use) and [`useId`](/reference/react/useId) +* Các Hook [`react`](/reference/react/hooks) và [`react-dom`](/reference/react-dom/hooks), ngoại trừ [`use`](/reference/react/use) và [`useId`](/reference/react/useId) * [forwardRef](/reference/react/forwardRef) * [memo](/reference/react/memo) * [startTransition](/reference/react/startTransition) -* If they use client APIs, ex. DOM insertion or native platform views +* Nếu chúng sử dụng API máy khách, ví dụ: chèn DOM hoặc chế độ xem nền tảng gốc -If these libraries have been updated to be compatible with React Server Components, then they will already include `'use client'` markers of their own, allowing you to use them directly from your Server Components. If a library hasn't been updated, or if a component needs props like event handlers that can only be specified on the client, you may need to add your own Client Component file in between the third-party Client Component and your Server Component where you'd like to use it. +Nếu các thư viện này đã được cập nhật để tương thích với React Server Components, thì chúng sẽ bao gồm các dấu hiệu `'use client'` của riêng chúng, cho phép bạn sử dụng chúng trực tiếp từ Server Components của mình. Nếu một thư viện chưa được cập nhật hoặc nếu một thành phần cần các prop như trình xử lý sự kiện chỉ có thể được chỉ định trên máy khách, bạn có thể cần thêm tệp Client Component của riêng mình ở giữa Client Component của bên thứ ba và Server Component của bạn nơi bạn muốn sử dụng nó. -[TODO]: <> (Troubleshooting - need use-cases) +[TODO]: <> (Khắc phục sự cố - cần các trường hợp sử dụng) diff --git a/src/content/reference/rsc/use-server.md b/src/content/reference/rsc/use-server.md index 4d6fb4639..d5722e90b 100644 --- a/src/content/reference/rsc/use-server.md +++ b/src/content/reference/rsc/use-server.md @@ -1,18 +1,17 @@ --- title: "'use server'" -titleForTitleTag: "'use server' directive" +titleForTitleTag: "Chỉ thị `'use server'`" --- <RSC> -`'use server'` is for use with [using React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). +`'use server'` dùng để sử dụng với [React Server Components](/learn/start-a-new-react-project#bleeding-edge-react-frameworks). </RSC> - <Intro> -`'use server'` marks server-side functions that can be called from client-side code. +`'use server'` đánh dấu các hàm phía máy chủ có thể được gọi từ mã phía máy khách. </Intro> @@ -20,11 +19,11 @@ titleForTitleTag: "'use server' directive" --- -## Reference {/*reference*/} +## Tham khảo {/*reference*/} ### `'use server'` {/*use-server*/} -Add `'use server'` at the top of an async function body to mark the function as callable by the client. We call these functions [_Server Functions_](/reference/rsc/server-functions). +Thêm `'use server'` vào đầu phần thân hàm async để đánh dấu hàm đó có thể được gọi bởi máy khách. Chúng ta gọi những hàm này là [_Server Functions_](/reference/rsc/server-functions). ```js {2} async function addToCart(data) { @@ -33,78 +32,78 @@ async function addToCart(data) { } ``` -When calling a Server Function on the client, it will make a network request to the server that includes a serialized copy of any arguments passed. If the Server Function returns a value, that value will be serialized and returned to the client. +Khi gọi một Server Function trên máy khách, nó sẽ tạo một yêu cầu mạng đến máy chủ bao gồm một bản sao được tuần tự hóa của bất kỳ đối số nào được truyền. Nếu Server Function trả về một giá trị, giá trị đó sẽ được tuần tự hóa và trả về cho máy khách. -Instead of individually marking functions with `'use server'`, you can add the directive to the top of a file to mark all exports within that file as Server Functions that can be used anywhere, including imported in client code. +Thay vì đánh dấu riêng lẻ các hàm bằng `'use server'`, bạn có thể thêm chỉ thị vào đầu tệp để đánh dấu tất cả các exports trong tệp đó là Server Functions có thể được sử dụng ở bất kỳ đâu, kể cả được nhập trong mã máy khách. -#### Caveats {/*caveats*/} -* `'use server'` must be at the very beginning of their function or module; above any other code including imports (comments above directives are OK). They must be written with single or double quotes, not backticks. -* `'use server'` can only be used in server-side files. The resulting Server Functions can be passed to Client Components through props. See supported [types for serialization](#serializable-parameters-and-return-values). -* To import a Server Functions from [client code](/reference/rsc/use-client), the directive must be used on a module level. -* Because the underlying network calls are always asynchronous, `'use server'` can only be used on async functions. -* Always treat arguments to Server Functions as untrusted input and authorize any mutations. See [security considerations](#security). -* Server Functions should be called in a [Transition](/reference/react/useTransition). Server Functions passed to [`<form action>`](/reference/react-dom/components/form#props) or [`formAction`](/reference/react-dom/components/input#props) will automatically be called in a transition. -* Server Functions are designed for mutations that update server-side state; they are not recommended for data fetching. Accordingly, frameworks implementing Server Functions typically process one action at a time and do not have a way to cache the return value. +#### Lưu ý {/*caveats*/} +* `'use server'` phải ở ngay đầu hàm hoặc mô-đun của chúng; phía trên bất kỳ mã nào khác bao gồm cả imports (các comment phía trên các chỉ thị đều OK). Chúng phải được viết bằng dấu nháy đơn hoặc dấu nháy kép, không phải dấu backtick. +* `'use server'` chỉ có thể được sử dụng trong các tệp phía máy chủ. Các Server Functions kết quả có thể được truyền đến Client Components thông qua props. Xem các [kiểu được hỗ trợ để tuần tự hóa](#serializable-parameters-and-return-values). +* Để nhập một Server Functions từ [mã máy khách](/reference/rsc/use-client), chỉ thị phải được sử dụng ở cấp độ mô-đun. +* Vì các lệnh gọi mạng cơ bản luôn không đồng bộ, `'use server'` chỉ có thể được sử dụng trên các hàm async. +* Luôn coi các đối số cho Server Functions là đầu vào không đáng tin cậy và ủy quyền cho bất kỳ thay đổi nào. Xem [các cân nhắc về bảo mật](#security). +* Server Functions nên được gọi trong một [Transition](/reference/react/useTransition). Server Functions được truyền đến [`<form action>`](/reference/react-dom/components/form#props) hoặc [`formAction`](/reference/react-dom/components/input#props) sẽ tự động được gọi trong một transition. +* Server Functions được thiết kế cho các thay đổi cập nhật trạng thái phía máy chủ; chúng không được khuyến nghị để tìm nạp dữ liệu. Theo đó, các framework triển khai Server Functions thường xử lý một hành động tại một thời điểm và không có cách nào để lưu vào bộ nhớ cache giá trị trả về. -### Security considerations {/*security*/} +### Cân nhắc về bảo mật {/*security*/} -Arguments to Server Functions are fully client-controlled. For security, always treat them as untrusted input, and make sure to validate and escape arguments as appropriate. +Các đối số cho Server Functions hoàn toàn do máy khách kiểm soát. Vì lý do bảo mật, hãy luôn coi chúng là đầu vào không đáng tin cậy và đảm bảo xác thực và thoát các đối số khi thích hợp. -In any Server Function, make sure to validate that the logged-in user is allowed to perform that action. +Trong bất kỳ Server Function nào, hãy đảm bảo xác thực rằng người dùng đã đăng nhập được phép thực hiện hành động đó. <Wip> -To prevent sending sensitive data from a Server Function, there are experimental taint APIs to prevent unique values and objects from being passed to client code. +Để ngăn chặn việc gửi dữ liệu nhạy cảm từ một Server Function, có các API taint thử nghiệm để ngăn chặn các giá trị và đối tượng duy nhất được truyền đến mã máy khách. -See [experimental_taintUniqueValue](/reference/react/experimental_taintUniqueValue) and [experimental_taintObjectReference](/reference/react/experimental_taintObjectReference). +Xem [experimental_taintUniqueValue](/reference/react/experimental_taintUniqueValue) và [experimental_taintObjectReference](/reference/react/experimental_taintObjectReference). </Wip> -### Serializable arguments and return values {/*serializable-parameters-and-return-values*/} - -Since client code calls the Server Function over the network, any arguments passed will need to be serializable. - -Here are supported types for Server Function arguments: - -* Primitives - * [string](https://developer.mozilla.org/en-US/docs/Glossary/String) - * [number](https://developer.mozilla.org/en-US/docs/Glossary/Number) - * [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) - * [boolean](https://developer.mozilla.org/en-US/docs/Glossary/Boolean) - * [undefined](https://developer.mozilla.org/en-US/docs/Glossary/Undefined) - * [null](https://developer.mozilla.org/en-US/docs/Glossary/Null) - * [symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), only symbols registered in the global Symbol registry via [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) -* Iterables containing serializable values - * [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) - * [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) - * [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) - * [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) - * [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) and [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) +### Các đối số và giá trị trả về có thể tuần tự hóa {/*serializable-parameters-and-return-values*/} + +Vì mã máy khách gọi Server Function qua mạng, bất kỳ đối số nào được truyền sẽ cần phải được tuần tự hóa. + +Dưới đây là các kiểu được hỗ trợ cho các đối số của Server Function: + +* Các kiểu nguyên thủy + * [string](https://developer.mozilla.org/en-US/docs/Glossary/String) + * [number](https://developer.mozilla.org/en-US/docs/Glossary/Number) + * [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) + * [boolean](https://developer.mozilla.org/en-US/docs/Glossary/Boolean) + * [undefined](https://developer.mozilla.org/en-US/docs/Glossary/Undefined) + * [null](https://developer.mozilla.org/en-US/docs/Glossary/Null) + * [symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), chỉ các symbol được đăng ký trong registry Symbol toàn cục thông qua [`Symbol.for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for) +* Các iterable chứa các giá trị có thể tuần tự hóa + * [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) + * [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) + * [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) + * [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) + * [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) và [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) * [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) -* [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) instances -* Plain [objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object): those created with [object initializers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer), with serializable properties -* Functions that are Server Functions +* Các instance của [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) +* Các [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) thuần túy: những object được tạo bằng [object initializers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer), với các thuộc tính có thể tuần tự hóa +* Các function là Server Functions * [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) -Notably, these are not supported: -* React elements, or [JSX](/learn/writing-markup-with-jsx) -* Functions, including component functions or any other function that is not a Server Function +Đáng chú ý, những điều này không được hỗ trợ: +* Các phần tử React, hoặc [JSX](/learn/writing-markup-with-jsx) +* Các function, bao gồm các function component hoặc bất kỳ function nào khác không phải là Server Function * [Classes](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Classes_in_JavaScript) -* Objects that are instances of any class (other than the built-ins mentioned) or objects with [a null prototype](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects) -* Symbols not registered globally, ex. `Symbol('my new symbol')` -* Events from event handlers +* Các object là instance của bất kỳ class nào (ngoài các built-in đã đề cập) hoặc các object có [prototype null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects) +* Các Symbol không được đăng ký trên toàn cục, ví dụ: `Symbol('my new symbol')` +* Các Event từ trình xử lý sự kiện -Supported serializable return values are the same as [serializable props](/reference/rsc/use-client#passing-props-from-server-to-client-components) for a boundary Client Component. +Các giá trị trả về có thể tuần tự hóa được hỗ trợ giống như [các prop có thể tuần tự hóa](/reference/rsc/use-client#passing-props-from-server-to-client-components) cho một Client Component boundary. -## Usage {/*usage*/} +## Cách sử dụng {/*usage*/} -### Server Functions in forms {/*server-functions-in-forms*/} +### Server Functions trong các form {/*server-functions-in-forms*/} -The most common use case of Server Functions will be calling functions that mutate data. On the browser, the [HTML form element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) is the traditional approach for a user to submit a mutation. With React Server Components, React introduces first-class support for Server Functions as Actions in [forms](/reference/react-dom/components/form). +Trường hợp sử dụng phổ biến nhất của Server Functions sẽ là gọi các function làm thay đổi dữ liệu. Trên trình duyệt, [phần tử form HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) là phương pháp truyền thống để người dùng gửi một thay đổi. Với React Server Components, React giới thiệu hỗ trợ hạng nhất cho Server Functions dưới dạng Actions trong [các form](/reference/react-dom/components/form). -Here is a form that allows a user to request a username. +Đây là một form cho phép người dùng yêu cầu tên người dùng. ```js [[1, 3, "formData"]] // App.js @@ -119,21 +118,21 @@ export default function App() { return ( <form action={requestUsername}> <input type="text" name="username" /> - <button type="submit">Request</button> + <button type="submit">Yêu cầu</button> </form> ); } ``` -In this example `requestUsername` is a Server Function passed to a `<form>`. When a user submits this form, there is a network request to the server function `requestUsername`. When calling a Server Function in a form, React will supply the form's <CodeStep step={1}>[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)</CodeStep> as the first argument to the Server Function. +Trong ví dụ này, `requestUsername` là một Server Function được truyền cho một `<form>`. Khi người dùng gửi form này, có một yêu cầu mạng đến server function `requestUsername`. Khi gọi một Server Function trong một form, React sẽ cung cấp <CodeStep step={1}>[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)</CodeStep> của form làm đối số đầu tiên cho Server Function. -By passing a Server Function to the form `action`, React can [progressively enhance](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement) the form. This means that forms can be submitted before the JavaScript bundle is loaded. +Bằng cách truyền một Server Function cho form `action`, React có thể [cải thiện dần](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement) form. Điều này có nghĩa là các form có thể được gửi trước khi bundle JavaScript được tải. -#### Handling return values in forms {/*handling-return-values*/} +#### Xử lý các giá trị trả về trong các form {/*handling-return-values*/} -In the username request form, there might be the chance that a username is not available. `requestUsername` should tell us if it fails or not. +Trong form yêu cầu tên người dùng, có thể có trường hợp tên người dùng không khả dụng. `requestUsername` sẽ cho chúng ta biết nếu nó thất bại hay không. -To update the UI based on the result of a Server Function while supporting progressive enhancement, use [`useActionState`](/reference/react/useActionState). +Để cập nhật UI dựa trên kết quả của một Server Function trong khi hỗ trợ cải thiện dần, hãy sử dụng [`useActionState`](/reference/react/useActionState). ```js // requestUsername.js @@ -163,21 +162,21 @@ function UsernameForm() { <> <form action={action}> <input type="text" name="username" /> - <button type="submit">Request</button> + <button type="submit">Yêu cầu</button> </form> - <p>Last submission request returned: {state}</p> + <p>Yêu cầu gửi gần nhất trả về: {state}</p> </> ); } ``` -Note that like most Hooks, `useActionState` can only be called in <CodeStep step={1}>[client code](/reference/rsc/use-client)</CodeStep>. +Lưu ý rằng giống như hầu hết các Hook, `useActionState` chỉ có thể được gọi trong <CodeStep step={1}>[mã máy khách](/reference/rsc/use-client)</CodeStep>. -### Calling a Server Function outside of `<form>` {/*calling-a-server-function-outside-of-form*/} +### Gọi một Server Function bên ngoài `<form>` {/*calling-a-server-function-outside-of-form*/} -Server Functions are exposed server endpoints and can be called anywhere in client code. +Server Functions được hiển thị các endpoint máy chủ và có thể được gọi ở bất kỳ đâu trong mã máy khách. -When using a Server Function outside a [form](/reference/react-dom/components/form), call the Server Function in a [Transition](/reference/react/useTransition), which allows you to display a loading indicator, show [optimistic state updates](/reference/react/useOptimistic), and handle unexpected errors. Forms will automatically wrap Server Functions in transitions. +Khi sử dụng một Server Function bên ngoài một [form](/reference/react-dom/components/form), hãy gọi Server Function trong một [Transition](/reference/react/useTransition), cho phép bạn hiển thị chỉ báo tải, hiển thị [cập nhật trạng thái lạc quan](/reference/react/useOptimistic) và xử lý các lỗi không mong muốn. Các form sẽ tự động bọc Server Functions trong các transition. ```js {9-12} import incrementLike from './actions'; @@ -196,8 +195,8 @@ function LikeButton() { return ( <> - <p>Total Likes: {likeCount}</p> - <button onClick={onClick} disabled={isPending}>Like</button>; + <p>Tổng số lượt thích: {likeCount}</p> + <button onClick={onClick} disabled={isPending}>Thích</button>; </> ); } @@ -214,4 +213,4 @@ export default async function incrementLike() { } ``` -To read a Server Function return value, you'll need to `await` the promise returned. +Để đọc giá trị trả về của Server Function, bạn sẽ cần `await` promise được trả về. diff --git a/src/content/reference/rules/components-and-hooks-must-be-pure.md b/src/content/reference/rules/components-and-hooks-must-be-pure.md index d3d54560e..797f1790e 100644 --- a/src/content/reference/rules/components-and-hooks-must-be-pure.md +++ b/src/content/reference/rules/components-and-hooks-must-be-pure.md @@ -1,55 +1,55 @@ --- -title: Components and Hooks must be pure +title: Các thành phần và Hook phải thuần khiết --- <Intro> -Pure functions only perform a calculation and nothing more. It makes your code easier to understand, debug, and allows React to automatically optimize your components and Hooks correctly. +Các hàm thuần khiết chỉ thực hiện một phép tính và không làm gì khác. Nó giúp mã của bạn dễ hiểu, gỡ lỗi hơn và cho phép React tự động tối ưu hóa các thành phần và Hook của bạn một cách chính xác. </Intro> <Note> -This reference page covers advanced topics and requires familiarity with the concepts covered in the [Keeping Components Pure](/learn/keeping-components-pure) page. +Trang tham khảo này bao gồm các chủ đề nâng cao và yêu cầu bạn phải làm quen với các khái niệm được đề cập trong trang [Giữ cho các thành phần thuần khiết](/learn/keeping-components-pure). </Note> <InlineToc /> -### Why does purity matter? {/*why-does-purity-matter*/} +### Tại sao tính thuần khiết lại quan trọng? {/*why-does-purity-matter*/} -One of the key concepts that makes React, _React_ is _purity_. A pure component or hook is one that is: +Một trong những khái niệm chính làm nên React, _React_ là _tính thuần khiết_. Một thành phần hoặc hook thuần khiết là một thành phần: -* **Idempotent** – You [always get the same result every time](/learn/keeping-components-pure#purity-components-as-formulas) you run it with the same inputs – props, state, context for component inputs; and arguments for hook inputs. -* **Has no side effects in render** – Code with side effects should run [**separately from rendering**](#how-does-react-run-your-code). For example as an [event handler](/learn/responding-to-events) – where the user interacts with the UI and causes it to update; or as an [Effect](/reference/react/useEffect) – which runs after render. -* **Does not mutate non-local values**: Components and Hooks should [never modify values that aren't created locally](#mutation) in render. +* **Idempotent (Tính lũy đẳng)** – Bạn [luôn nhận được kết quả giống nhau mỗi khi](/learn/keeping-components-pure#purity-components-as-formulas) bạn chạy nó với cùng một đầu vào – props, state, context cho đầu vào thành phần; và các đối số cho đầu vào hook. +* **Không có tác dụng phụ trong quá trình render** – Mã có tác dụng phụ nên chạy [**tách biệt với quá trình render**](#how-does-react-run-your-code). Ví dụ: như một [trình xử lý sự kiện](/learn/responding-to-events) – nơi người dùng tương tác với giao diện người dùng và khiến nó cập nhật; hoặc như một [Effect](/reference/react/useEffect) – chạy sau khi render. +* **Không làm thay đổi các giá trị không cục bộ**: Các thành phần và Hook không bao giờ được [sửa đổi các giá trị không được tạo cục bộ](#mutation) trong quá trình render. -When render is kept pure, React can understand how to prioritize which updates are most important for the user to see first. This is made possible because of render purity: since components don't have side effects [in render](#how-does-react-run-your-code), React can pause rendering components that aren't as important to update, and only come back to them later when it's needed. +Khi quá trình render được giữ thuần khiết, React có thể hiểu cách ưu tiên các bản cập nhật nào là quan trọng nhất để người dùng thấy trước. Điều này có thể thực hiện được nhờ tính thuần khiết của quá trình render: vì các thành phần không có tác dụng phụ [trong quá trình render](#how-does-react-run-your-code), React có thể tạm dừng render các thành phần không quan trọng bằng cách cập nhật và chỉ quay lại chúng sau khi cần. -Concretely, this means that rendering logic can be run multiple times in a way that allows React to give your user a pleasant user experience. However, if your component has an untracked side effect – like modifying the value of a global variable [during render](#how-does-react-run-your-code) – when React runs your rendering code again, your side effects will be triggered in a way that won't match what you want. This often leads to unexpected bugs that can degrade how your users experience your app. You can see an [example of this in the Keeping Components Pure page](/learn/keeping-components-pure#side-effects-unintended-consequences). +Cụ thể, điều này có nghĩa là logic render có thể được chạy nhiều lần theo cách cho phép React mang lại cho người dùng của bạn trải nghiệm người dùng dễ chịu. Tuy nhiên, nếu thành phần của bạn có một tác dụng phụ không được theo dõi – chẳng hạn như sửa đổi giá trị của một biến toàn cục [trong quá trình render](#how-does-react-run-your-code) – khi React chạy lại mã render của bạn, các tác dụng phụ của bạn sẽ được kích hoạt theo cách không khớp với những gì bạn muốn. Điều này thường dẫn đến các lỗi không mong muốn có thể làm giảm trải nghiệm ứng dụng của người dùng. Bạn có thể xem [ví dụ về điều này trong trang Giữ cho các thành phần thuần khiết](/learn/keeping-components-pure#side-effects-unintended-consequences). -#### How does React run your code? {/*how-does-react-run-your-code*/} +#### React chạy mã của bạn như thế nào? {/*how-does-react-run-your-code*/} -React is declarative: you tell React _what_ to render, and React will figure out _how_ best to display it to your user. To do this, React has a few phases where it runs your code. You don't need to know about all of these phases to use React well. But at a high level, you should know about what code runs in _render_, and what runs outside of it. +React là khai báo: bạn cho React biết _cái gì_ để render và React sẽ tìm ra _cách_ tốt nhất để hiển thị nó cho người dùng của bạn. Để làm điều này, React có một vài giai đoạn để chạy mã của bạn. Bạn không cần phải biết về tất cả các giai đoạn này để sử dụng React tốt. Nhưng ở cấp độ cao, bạn nên biết về mã nào chạy trong _render_ và mã nào chạy bên ngoài nó. -_Rendering_ refers to calculating what the next version of your UI should look like. After rendering, [Effects](/reference/react/useEffect) are _flushed_ (meaning they are run until there are no more left) and may update the calculation if the Effects have impacts on layout. React takes this new calculation and compares it to the calculation used to create the previous version of your UI, then _commits_ just the minimum changes needed to the [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) (what your user actually sees) to catch it up to the latest version. +_Rendering_ đề cập đến việc tính toán giao diện của phiên bản tiếp theo của giao diện người dùng của bạn. Sau khi render, [Effects](/reference/react/useEffect) được _flush_ (có nghĩa là chúng được chạy cho đến khi không còn cái nào nữa) và có thể cập nhật phép tính nếu Effects có tác động đến bố cục. React lấy phép tính mới này và so sánh nó với phép tính được sử dụng để tạo phiên bản trước của giao diện người dùng của bạn, sau đó _commit_ chỉ những thay đổi tối thiểu cần thiết cho [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) (những gì người dùng của bạn thực sự thấy) để bắt kịp phiên bản mới nhất. <DeepDive> -#### How to tell if code runs in render {/*how-to-tell-if-code-runs-in-render*/} +#### Làm thế nào để biết mã có chạy trong quá trình render hay không {/*how-to-tell-if-code-runs-in-render*/} -One quick heuristic to tell if code runs during render is to examine where it is: if it's written at the top level like in the example below, there's a good chance it runs during render. +Một phương pháp heuristic nhanh chóng để biết liệu mã có chạy trong quá trình render hay không là kiểm tra vị trí của nó: nếu nó được viết ở cấp cao nhất như trong ví dụ bên dưới, thì rất có thể nó chạy trong quá trình render. ```js {2} function Dropdown() { - const selectedItems = new Set(); // created during render + const selectedItems = new Set(); // được tạo trong quá trình render // ... } ``` -Event handlers and Effects don't run in render: +Trình xử lý sự kiện và Effects không chạy trong quá trình render: ```js {4} function Dropdown() { const selectedItems = new Set(); const onSelect = (item) => { - // this code is in an event handler, so it's only run when the user triggers this + // mã này nằm trong trình xử lý sự kiện, vì vậy nó chỉ được chạy khi người dùng kích hoạt nó selectedItems.add(item); } } @@ -59,31 +59,32 @@ function Dropdown() { function Dropdown() { const selectedItems = new Set(); useEffect(() => { - // this code is inside of an Effect, so it only runs after rendering + // mã này nằm bên trong Effect, vì vậy nó chỉ chạy sau khi render logForAnalytics(selectedItems); }, [selectedItems]); } ``` + </DeepDive> --- -## Components and Hooks must be idempotent {/*components-and-hooks-must-be-idempotent*/} +## Các thành phần và Hook phải là idempotent {/*components-and-hooks-must-be-idempotent*/} -Components must always return the same output with respect to their inputs – props, state, and context. This is known as _idempotency_. [Idempotency](https://en.wikipedia.org/wiki/Idempotence) is a term popularized in functional programming. It refers to the idea that you [always get the same result every time](learn/keeping-components-pure) you run that piece of code with the same inputs. +Các thành phần phải luôn trả về cùng một đầu ra đối với đầu vào của chúng – props, state và context. Điều này được gọi là _idempotency_ (tính lũy đẳng). [Idempotency](https://en.wikipedia.org/wiki/Idempotence) là một thuật ngữ được phổ biến trong lập trình hàm. Nó đề cập đến ý tưởng rằng bạn [luôn nhận được kết quả giống nhau mỗi khi](learn/keeping-components-pure) bạn chạy đoạn mã đó với cùng một đầu vào. -This means that _all_ code that runs [during render](#how-does-react-run-your-code) must also be idempotent in order for this rule to hold. For example, this line of code is not idempotent (and therefore, neither is the component): +Điều này có nghĩa là _tất cả_ mã chạy [trong quá trình render](#how-does-react-run-your-code) cũng phải là idempotent để quy tắc này có hiệu lực. Ví dụ: dòng mã này không phải là idempotent (và do đó, thành phần cũng không phải): ```js {2} function Clock() { - const time = new Date(); // 🔴 Bad: always returns a different result! + const time = new Date(); // 🔴 Sai: luôn trả về một kết quả khác! return <span>{time.toLocaleString()}</span> } ``` -`new Date()` is not idempotent as it always returns the current date and changes its result every time it's called. When you render the above component, the time displayed on the screen will stay stuck on the time that the component was rendered. Similarly, functions like `Math.random()` also aren't idempotent, because they return different results every time they're called, even when the inputs are the same. +`new Date()` không phải là idempotent vì nó luôn trả về ngày hiện tại và thay đổi kết quả của nó mỗi khi nó được gọi. Khi bạn render thành phần trên, thời gian hiển thị trên màn hình sẽ bị kẹt vào thời điểm thành phần được render. Tương tự, các hàm như `Math.random()` cũng không phải là idempotent, vì chúng trả về các kết quả khác nhau mỗi khi chúng được gọi, ngay cả khi đầu vào giống nhau. -This doesn't mean you shouldn't use non-idempotent functions like `new Date()` _at all_ – you should just avoid using them [during render](#how-does-react-run-your-code). In this case, we can _synchronize_ the latest date to this component using an [Effect](/reference/react/useEffect): +Điều này không có nghĩa là bạn không nên sử dụng các hàm không idempotent như `new Date()` _hoàn toàn_ – bạn chỉ nên tránh sử dụng chúng [trong quá trình render](#how-does-react-run-your-code). Trong trường hợp này, chúng ta có thể _đồng bộ hóa_ ngày mới nhất với thành phần này bằng cách sử dụng [Effect](/reference/react/useEffect): <Sandpack> @@ -91,17 +92,16 @@ This doesn't mean you shouldn't use non-idempotent functions like `new Date()` _ import { useState, useEffect } from 'react'; function useTime() { - // 1. Keep track of the current date's state. `useState` receives an initializer function as its - // initial state. It only runs once when the hook is called, so only the current date at the - // time the hook is called is set first. + // 1. Theo dõi trạng thái của ngày hiện tại. `useState` nhận một hàm khởi tạo làm trạng thái ban đầu của nó. + // Nó chỉ chạy một lần khi hook được gọi, vì vậy chỉ ngày hiện tại tại thời điểm hook được gọi được đặt trước. const [time, setTime] = useState(() => new Date()); useEffect(() => { - // 2. Update the current date every second using `setInterval`. + // 2. Cập nhật ngày hiện tại mỗi giây bằng cách sử dụng `setInterval`. const id = setInterval(() => { - setTime(new Date()); // ✅ Good: non-idempotent code no longer runs in render + setTime(new Date()); // ✅ Tốt: mã không idempotent không còn chạy trong quá trình render }, 1000); - // 3. Return a cleanup function so we don't leak the `setInterval` timer. + // 3. Trả về một hàm dọn dẹp để chúng ta không làm rò rỉ bộ hẹn giờ `setInterval`. return () => clearInterval(id); }, []); @@ -116,128 +116,131 @@ export default function Clock() { </Sandpack> -By wrapping the non-idempotent `new Date()` call in an Effect, it moves that calculation [outside of rendering](#how-does-react-run-your-code). +Bằng cách gói lệnh gọi `new Date()` không idempotent trong một Effect, nó sẽ di chuyển phép tính đó [ra khỏi quá trình render](#how-does-react-run-your-code). -If you don't need to synchronize some external state with React, you can also consider using an [event handler](/learn/responding-to-events) if it only needs to be updated in response to a user interaction. +Nếu bạn không cần đồng bộ hóa một số trạng thái bên ngoài với React, bạn cũng có thể cân nhắc sử dụng [trình xử lý sự kiện](/learn/responding-to-events) nếu nó chỉ cần được cập nhật để đáp ứng với tương tác của người dùng. --- -## Side effects must run outside of render {/*side-effects-must-run-outside-of-render*/} +## Các tác dụng phụ phải chạy bên ngoài quá trình render {/*side-effects-must-run-outside-of-render*/} -[Side effects](/learn/keeping-components-pure#side-effects-unintended-consequences) should not run [in render](#how-does-react-run-your-code), as React can render components multiple times to create the best possible user experience. +[Các tác dụng phụ](/learn/keeping-components-pure#side-effects-unintended-consequences) không nên chạy [trong quá trình render](#how-does-react-run-your-code), vì React có thể render các thành phần nhiều lần để tạo ra trải nghiệm người dùng tốt nhất có thể. <Note> -Side effects are a broader term than Effects. Effects specifically refer to code that's wrapped in `useEffect`, while a side effect is a general term for code that has any observable effect other than its primary result of returning a value to the caller. +Tác dụng phụ là một thuật ngữ rộng hơn Effects. Effects đặc biệt đề cập đến mã được gói trong `useEffect`, trong khi tác dụng phụ là một thuật ngữ chung cho mã có bất kỳ tác dụng quan sát được nào khác ngoài kết quả chính của nó là trả về một giá trị cho người gọi. -Side effects are typically written inside of [event handlers](/learn/responding-to-events) or Effects. But never during render. +Tác dụng phụ thường được viết bên trong [trình xử lý sự kiện](/learn/responding-to-events) hoặc Effects. Nhưng không bao giờ trong quá trình render. </Note> -While render must be kept pure, side effects are necessary at some point in order for your app to do anything interesting, like showing something on the screen! The key point of this rule is that side effects should not run [in render](#how-does-react-run-your-code), as React can render components multiple times. In most cases, you'll use [event handlers](learn/responding-to-events) to handle side effects. Using an event handler explicitly tells React that this code doesn't need to run during render, keeping render pure. If you've exhausted all options – and only as a last resort – you can also handle side effects using `useEffect`. +Mặc dù quá trình render phải được giữ thuần khiết, nhưng các tác dụng phụ là cần thiết tại một thời điểm nào đó để ứng dụng của bạn có thể làm bất cứ điều gì thú vị, như hiển thị thứ gì đó trên màn hình! Điểm mấu chốt của quy tắc này là các tác dụng phụ không nên chạy [trong quá trình render](#how-does-react-run-your-code), vì React có thể render các thành phần nhiều lần. Trong hầu hết các trường hợp, bạn sẽ sử dụng [trình xử lý sự kiện](learn/responding-to-events) để xử lý các tác dụng phụ. Sử dụng trình xử lý sự kiện sẽ cho React biết một cách rõ ràng rằng mã này không cần chạy trong quá trình render, giữ cho quá trình render thuần khiết. Nếu bạn đã sử dụng hết tất cả các tùy chọn – và chỉ là phương sách cuối cùng – bạn cũng có thể xử lý các tác dụng phụ bằng cách sử dụng `useEffect`. + +### Khi nào thì được phép có mutation? {/*mutation*/} -### When is it okay to have mutation? {/*mutation*/} +#### Mutation cục bộ {/*local-mutation*/} -#### Local mutation {/*local-mutation*/} -One common example of a side effect is mutation, which in JavaScript refers to changing the value of a non-[primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) value. In general, while mutation is not idiomatic in React, _local_ mutation is absolutely fine: +Một ví dụ phổ biến về tác dụng phụ là mutation, trong JavaScript đề cập đến việc thay đổi giá trị của một giá trị không phải là [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). Nói chung, mặc dù mutation không phải là thành ngữ trong React, nhưng mutation _cục bộ_ hoàn toàn ổn: ```js {2,7} function FriendList({ friends }) { - const items = []; // ✅ Good: locally created + const items = []; // ✅ Tốt: được tạo cục bộ for (let i = 0; i < friends.length; i++) { const friend = friends[i]; items.push( <Friend key={friend.id} friend={friend} /> - ); // ✅ Good: local mutation is okay + ); // ✅ Tốt: mutation cục bộ là ổn } return <section>{items}</section>; } ``` -There is no need to contort your code to avoid local mutation. [`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) could also be used here for brevity, but there is nothing wrong with creating a local array and then pushing items into it [during render](#how-does-react-run-your-code). +Không cần phải bóp méo mã của bạn để tránh mutation cục bộ. [`Array.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) cũng có thể được sử dụng ở đây để ngắn gọn, nhưng không có gì sai khi tạo một mảng cục bộ và sau đó đẩy các mục vào đó [trong quá trình render](#how-does-react-run-your-code). -Even though it looks like we are mutating `items`, the key point to note is that this code only does so _locally_ – the mutation isn't "remembered" when the component is rendered again. In other words, `items` only stays around as long as the component does. Because `items` is always _recreated_ every time `<FriendList />` is rendered, the component will always return the same result. +Mặc dù có vẻ như chúng ta đang thay đổi `items`, nhưng điểm mấu chốt cần lưu ý là mã này chỉ thực hiện _cục bộ_ – mutation không được "ghi nhớ" khi thành phần được render lại. Nói cách khác, `items` chỉ tồn tại chừng nào thành phần còn tồn tại. Vì `items` luôn được _tạo lại_ mỗi khi `<FriendList />` được render, thành phần sẽ luôn trả về cùng một kết quả. -On the other hand, if `items` was created outside of the component, it holds on to its previous values and remembers changes: +Mặt khác, nếu `items` được tạo bên ngoài thành phần, nó sẽ giữ lại các giá trị trước đó và ghi nhớ các thay đổi: ```js {1,7} -const items = []; // 🔴 Bad: created outside of the component +const items = []; // 🔴 Sai: được tạo bên ngoài thành phần function FriendList({ friends }) { for (let i = 0; i < friends.length; i++) { const friend = friends[i]; items.push( <Friend key={friend.id} friend={friend} /> - ); // 🔴 Bad: mutates a value created outside of render + ); // 🔴 Sai: thay đổi một giá trị được tạo bên ngoài quá trình render } return <section>{items}</section>; } ``` -When `<FriendList />` runs again, we will continue appending `friends` to `items` every time that component is run, leading to multiple duplicated results. This version of `<FriendList />` has observable side effects [during render](#how-does-react-run-your-code) and **breaks the rule**. +Khi `<FriendList />` chạy lại, chúng ta sẽ tiếp tục nối `friends` vào `items` mỗi khi thành phần đó được chạy, dẫn đến nhiều kết quả trùng lặp. Phiên bản `<FriendList />` này có các tác dụng phụ có thể quan sát được [trong quá trình render](#how-does-react-run-your-code) và **phá vỡ quy tắc**. -#### Lazy initialization {/*lazy-initialization*/} +#### Khởi tạo lazy {/*lazy-initialization*/} -Lazy initialization is also fine despite not being fully "pure": +Khởi tạo lazy cũng ổn mặc dù không hoàn toàn "thuần khiết": ```js {2} function ExpenseForm() { - SuperCalculator.initializeIfNotReady(); // ✅ Good: if it doesn't affect other components - // Continue rendering... + SuperCalculator.initializeIfNotReady(); // ✅ Tốt: nếu nó không ảnh hưởng đến các thành phần khác + // Tiếp tục render... } ``` -#### Changing the DOM {/*changing-the-dom*/} +#### Thay đổi DOM {/*changing-the-dom*/} -Side effects that are directly visible to the user are not allowed in the render logic of React components. In other words, merely calling a component function shouldn’t by itself produce a change on the screen. +Các tác dụng phụ hiển thị trực tiếp cho người dùng không được phép trong logic render của các thành phần React. Nói cách khác, chỉ cần gọi một hàm thành phần không được tự nó tạo ra một thay đổi trên màn hình. ```js {2} function ProductDetailPage({ product }) { - document.title = product.title; // 🔴 Bad: Changes the DOM + document.title = product.title; // 🔴 Sai: Thay đổi DOM } ``` -One way to achieve the desired result of updating `document.title` outside of render is to [synchronize the component with `document`](/learn/synchronizing-with-effects). +Một cách để đạt được kết quả mong muốn là cập nhật `document.title` bên ngoài quá trình render là [đồng bộ hóa thành phần với `document`](/learn/synchronizing-with-effects). -As long as calling a component multiple times is safe and doesn’t affect the rendering of other components, React doesn’t care if it’s 100% pure in the strict functional programming sense of the word. It is more important that [components must be idempotent](/reference/rules/components-and-hooks-must-be-pure). +Miễn là việc gọi một thành phần nhiều lần là an toàn và không ảnh hưởng đến quá trình render của các thành phần khác, React không quan tâm nếu nó thuần khiết 100% theo nghĩa lập trình hàm nghiêm ngặt của từ này. Điều quan trọng hơn là [các thành phần phải là idempotent](/reference/rules/components-and-hooks-must-be-pure). --- -## Props and state are immutable {/*props-and-state-are-immutable*/} +## Props và state là bất biến {/*props-and-state-are-immutable*/} -A component's props and state are immutable [snapshots](learn/state-as-a-snapshot). Never mutate them directly. Instead, pass new props down, and use the setter function from `useState`. +Props và state của một thành phần là [ảnh chụp nhanh](learn/state-as-a-snapshot) bất biến. Không bao giờ thay đổi chúng trực tiếp. Thay vào đó, hãy truyền các props mới xuống và sử dụng hàm setter từ `useState`. -You can think of the props and state values as snapshots that are updated after rendering. For this reason, you don't modify the props or state variables directly: instead you pass new props, or use the setter function provided to you to tell React that state needs to update the next time the component is rendered. +Bạn có thể coi các giá trị props và state là ảnh chụp nhanh được cập nhật sau khi render. Vì lý do này, bạn không sửa đổi trực tiếp các biến props hoặc state: thay vào đó, bạn truyền các props mới hoặc sử dụng hàm setter được cung cấp cho bạn để cho React biết rằng state cần cập nhật vào lần thành phần được render tiếp theo. -### Don't mutate Props {/*props*/} -Props are immutable because if you mutate them, the application will produce inconsistent output, which can be hard to debug since it may or may not work depending on the circumstance. +### Không thay đổi Props {/*props*/} + +Props là bất biến vì nếu bạn thay đổi chúng, ứng dụng sẽ tạo ra đầu ra không nhất quán, điều này có thể khó gỡ lỗi vì nó có thể hoạt động hoặc không hoạt động tùy thuộc vào hoàn cảnh. ```js {2} function Post({ item }) { - item.url = new Url(item.url, base); // 🔴 Bad: never mutate props directly + item.url = new Url(item.url, base); // 🔴 Sai: không bao giờ thay đổi props trực tiếp return <Link url={item.url}>{item.title}</Link>; } ``` ```js {2} function Post({ item }) { - const url = new Url(item.url, base); // ✅ Good: make a copy instead + const url = new Url(item.url, base); // ✅ Tốt: thay vào đó hãy tạo một bản sao return <Link url={url}>{item.title}</Link>; } ``` -### Don't mutate State {/*state*/} -`useState` returns the state variable and a setter to update that state. +### Không thay đổi State {/*state*/} + +`useState` trả về biến state và một setter để cập nhật state đó. ```js const [stateVariable, setter] = useState(0); ``` -Rather than updating the state variable in-place, we need to update it using the setter function that is returned by `useState`. Changing values on the state variable doesn't cause the component to update, leaving your users with an outdated UI. Using the setter function informs React that the state has changed, and that we need to queue a re-render to update the UI. +Thay vì cập nhật biến state tại chỗ, chúng ta cần cập nhật nó bằng hàm setter được trả về bởi `useState`. Thay đổi các giá trị trên biến state không khiến thành phần cập nhật, khiến người dùng của bạn có một giao diện người dùng lỗi thời. Sử dụng hàm setter thông báo cho React rằng state đã thay đổi và chúng ta cần xếp hàng đợi render lại để cập nhật giao diện người dùng. ```js {5} function Counter() { const [count, setCount] = useState(0); function handleClick() { - count = count + 1; // 🔴 Bad: never mutate state directly + count = count + 1; // 🔴 Sai: không bao giờ thay đổi state trực tiếp } return ( @@ -253,7 +256,7 @@ function Counter() { const [count, setCount] = useState(0); function handleClick() { - setCount(count + 1); // ✅ Good: use the setter function returned by useState + setCount(count + 1); // ✅ Tốt: sử dụng hàm setter được trả về bởi useState } return ( @@ -266,15 +269,15 @@ function Counter() { --- -## Return values and arguments to Hooks are immutable {/*return-values-and-arguments-to-hooks-are-immutable*/} +## Các giá trị trả về và đối số cho Hook là bất biến {/*return-values-and-arguments-to-hooks-are-immutable*/} -Once values are passed to a hook, you should not modify them. Like props in JSX, values become immutable when passed to a hook. +Sau khi các giá trị được truyền cho một hook, bạn không nên sửa đổi chúng. Giống như props trong JSX, các giá trị trở nên bất biến khi được truyền cho một hook. ```js {4} function useIconStyle(icon) { const theme = useContext(ThemeContext); if (icon.enabled) { - icon.className = computeStyle(icon, theme); // 🔴 Bad: never mutate hook arguments directly + icon.className = computeStyle(icon, theme); // 🔴 Sai: không bao giờ thay đổi trực tiếp các đối số hook } return icon; } @@ -283,7 +286,7 @@ function useIconStyle(icon) { ```js {3} function useIconStyle(icon) { const theme = useContext(ThemeContext); - const newIcon = { ...icon }; // ✅ Good: make a copy instead + const newIcon = { ...icon }; // ✅ Tốt: thay vào đó hãy tạo một bản sao if (icon.enabled) { newIcon.className = computeStyle(icon, theme); } @@ -291,7 +294,7 @@ function useIconStyle(icon) { } ``` -One important principle in React is _local reasoning_: the ability to understand what a component or hook does by looking at its code in isolation. Hooks should be treated like "black boxes" when they are called. For example, a custom hook might have used its arguments as dependencies to memoize values inside it: +Một nguyên tắc quan trọng trong React là _lý luận cục bộ_: khả năng hiểu những gì một thành phần hoặc hook làm bằng cách xem xét mã của nó một cách riêng biệt. Các hook nên được coi là "hộp đen" khi chúng được gọi. Ví dụ: một hook tùy chỉnh có thể đã sử dụng các đối số của nó làm phần phụ thuộc để ghi nhớ các giá trị bên trong nó: ```js {4} function useIconStyle(icon) { @@ -307,35 +310,35 @@ function useIconStyle(icon) { } ``` -If you were to mutate the Hooks arguments, the custom hook's memoization will become incorrect, so it's important to avoid doing that. +Nếu bạn thay đổi các đối số của Hook, quá trình ghi nhớ của hook tùy chỉnh sẽ trở nên không chính xác, vì vậy điều quan trọng là phải tránh làm điều đó. ```js {4} -style = useIconStyle(icon); // `style` is memoized based on `icon` -icon.enabled = false; // Bad: 🔴 never mutate hook arguments directly -style = useIconStyle(icon); // previously memoized result is returned +style = useIconStyle(icon); // `style` được ghi nhớ dựa trên `icon` +icon.enabled = false; // Sai: 🔴 không bao giờ thay đổi trực tiếp các đối số hook +style = useIconStyle(icon); // kết quả được ghi nhớ trước đó được trả về ``` ```js {4} -style = useIconStyle(icon); // `style` is memoized based on `icon` -icon = { ...icon, enabled: false }; // Good: ✅ make a copy instead -style = useIconStyle(icon); // new value of `style` is calculated +style = useIconStyle(icon); // `style` được ghi nhớ dựa trên `icon` +icon = { ...icon, enabled: false }; // Tốt: ✅ thay vào đó hãy tạo một bản sao +style = useIconStyle(icon); // giá trị mới của `style` được tính toán ``` -Similarly, it's important to not modify the return values of Hooks, as they may have been memoized. +Tương tự, điều quan trọng là không sửa đổi các giá trị trả về của Hook, vì chúng có thể đã được ghi nhớ. --- -## Values are immutable after being passed to JSX {/*values-are-immutable-after-being-passed-to-jsx*/} +## Các giá trị là bất biến sau khi được truyền cho JSX {/*values-are-immutable-after-being-passed-to-jsx*/} -Don't mutate values after they've been used in JSX. Move the mutation before the JSX is created. +Không thay đổi các giá trị sau khi chúng đã được sử dụng trong JSX. Di chuyển mutation trước khi JSX được tạo. -When you use JSX in an expression, React may eagerly evaluate the JSX before the component finishes rendering. This means that mutating values after they've been passed to JSX can lead to outdated UIs, as React won't know to update the component's output. +Khi bạn sử dụng JSX trong một biểu thức, React có thể đánh giá JSX một cách háo hức trước khi thành phần kết thúc render. Điều này có nghĩa là việc thay đổi các giá trị sau khi chúng đã được truyền cho JSX có thể dẫn đến giao diện người dùng lỗi thời, vì React sẽ không biết cập nhật đầu ra của thành phần. ```js {4} function Page({ colour }) { const styles = { colour, size: "large" }; const header = <Header styles={styles} />; - styles.size = "small"; // 🔴 Bad: styles was already used in the JSX above + styles.size = "small"; // 🔴 Sai: styles đã được sử dụng trong JSX ở trên const footer = <Footer styles={styles} />; return ( <> @@ -351,7 +354,7 @@ function Page({ colour }) { function Page({ colour }) { const headerStyles = { colour, size: "large" }; const header = <Header styles={headerStyles} />; - const footerStyles = { colour, size: "small" }; // ✅ Good: we created a new value + const footerStyles = { colour, size: "small" }; // ✅ Tốt: chúng ta đã tạo một giá trị mới const footer = <Footer styles={footerStyles} />; return ( <> diff --git a/src/content/reference/rules/index.md b/src/content/reference/rules/index.md index dd5f7456c..035c2c0e6 100644 --- a/src/content/reference/rules/index.md +++ b/src/content/reference/rules/index.md @@ -1,9 +1,9 @@ --- -title: Rules of React +title: Các quy tắc của React --- <Intro> -Just as different programming languages have their own ways of expressing concepts, React has its own idioms — or rules — for how to express patterns in a way that is easy to understand and yields high-quality applications. +Giống như các ngôn ngữ lập trình khác nhau có cách riêng để diễn đạt các khái niệm, React có các thành ngữ riêng — hay quy tắc — để diễn đạt các mẫu theo cách dễ hiểu và mang lại các ứng dụng chất lượng cao. </Intro> <InlineToc /> @@ -11,42 +11,41 @@ Just as different programming languages have their own ways of expressing concep --- <Note> -To learn more about expressing UIs with React, we recommend reading [Thinking in React](/learn/thinking-in-react). +Để tìm hiểu thêm về cách thể hiện giao diện người dùng với React, chúng tôi khuyên bạn nên đọc [Tư duy trong React](/learn/thinking-in-react). </Note> -This section describes the rules you need to follow to write idiomatic React code. Writing idiomatic React code can help you write well organized, safe, and composable applications. These properties make your app more resilient to changes and makes it easier to work with other developers, libraries, and tools. +Phần này mô tả các quy tắc bạn cần tuân theo để viết mã React thành ngữ. Viết mã React thành ngữ có thể giúp bạn viết các ứng dụng được tổ chức tốt, an toàn và có khả năng kết hợp. Các thuộc tính này làm cho ứng dụng của bạn có khả năng phục hồi tốt hơn trước những thay đổi và giúp bạn dễ dàng làm việc với các nhà phát triển, thư viện và công cụ khác. -These rules are known as the **Rules of React**. They are rules – and not just guidelines – in the sense that if they are broken, your app likely has bugs. Your code also becomes unidiomatic and harder to understand and reason about. +Các quy tắc này được gọi là **Các quy tắc của React**. Chúng là các quy tắc – chứ không chỉ là hướng dẫn – theo nghĩa là nếu chúng bị phá vỡ, ứng dụng của bạn có thể có lỗi. Mã của bạn cũng trở nên không thành ngữ và khó hiểu và suy luận hơn. -We strongly recommend using [Strict Mode](/reference/react/StrictMode) alongside React's [ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) to help your codebase follow the Rules of React. By following the Rules of React, you'll be able to find and address these bugs and keep your application maintainable. +Chúng tôi đặc biệt khuyên bạn nên sử dụng [Chế độ nghiêm ngặt](/reference/react/StrictMode) cùng với [plugin ESLint](https://www.npmjs.com/package/eslint-plugin-react-hooks) của React để giúp cơ sở mã của bạn tuân theo Các quy tắc của React. Bằng cách tuân theo Các quy tắc của React, bạn sẽ có thể tìm và giải quyết các lỗi này và giữ cho ứng dụng của bạn có thể bảo trì được. --- -## Components and Hooks must be pure {/*components-and-hooks-must-be-pure*/} +## Các thành phần và Hook phải thuần khiết {/*components-and-hooks-must-be-pure*/} -[Purity in Components and Hooks](/reference/rules/components-and-hooks-must-be-pure) is a key rule of React that makes your app predictable, easy to debug, and allows React to automatically optimize your code. +[Tính thuần khiết trong các thành phần và Hook](/reference/rules/components-and-hooks-must-be-pure) là một quy tắc quan trọng của React giúp ứng dụng của bạn dễ đoán, dễ gỡ lỗi và cho phép React tự động tối ưu hóa mã của bạn. -* [Components must be idempotent](/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent) – React components are assumed to always return the same output with respect to their inputs – props, state, and context. -* [Side effects must run outside of render](/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) – Side effects should not run in render, as React can render components multiple times to create the best possible user experience. -* [Props and state are immutable](/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable) – A component’s props and state are immutable snapshots with respect to a single render. Never mutate them directly. -* [Return values and arguments to Hooks are immutable](/reference/rules/components-and-hooks-must-be-pure#return-values-and-arguments-to-hooks-are-immutable) – Once values are passed to a Hook, you should not modify them. Like props in JSX, values become immutable when passed to a Hook. -* [Values are immutable after being passed to JSX](/reference/rules/components-and-hooks-must-be-pure#values-are-immutable-after-being-passed-to-jsx) – Don’t mutate values after they’ve been used in JSX. Move the mutation before the JSX is created. +* [Các thành phần phải là idempotent](/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent) – Các thành phần React được cho là luôn trả về cùng một đầu ra đối với các đầu vào của chúng – props, state và context. +* [Các side effect phải chạy bên ngoài render](/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) – Các side effect không được chạy trong render, vì React có thể render các thành phần nhiều lần để tạo ra trải nghiệm người dùng tốt nhất có thể. +* [Props và state là bất biến](/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable) – Props và state của một thành phần là các ảnh chụp bất biến đối với một lần render duy nhất. Không bao giờ thay đổi chúng trực tiếp. +* [Giá trị trả về và đối số cho Hook là bất biến](/reference/rules/components-and-hooks-must-be-pure#return-values-and-arguments-to-hooks-are-immutable) – Sau khi các giá trị được truyền cho một Hook, bạn không nên sửa đổi chúng. Giống như props trong JSX, các giá trị trở nên bất biến khi được truyền cho một Hook. +* [Các giá trị là bất biến sau khi được truyền cho JSX](/reference/rules/components-and-hooks-must-be-pure#values-are-immutable-after-being-passed-to-jsx) – Không thay đổi các giá trị sau khi chúng đã được sử dụng trong JSX. Di chuyển đột biến trước khi JSX được tạo. --- -## React calls Components and Hooks {/*react-calls-components-and-hooks*/} +## React gọi các thành phần và Hook {/*react-calls-components-and-hooks*/} -[React is responsible for rendering components and hooks when necessary to optimize the user experience.](/reference/rules/react-calls-components-and-hooks) It is declarative: you tell React what to render in your component’s logic, and React will figure out how best to display it to your user. +[React chịu trách nhiệm render các thành phần và hook khi cần thiết để tối ưu hóa trải nghiệm người dùng.](/reference/rules/react-calls-components-and-hooks) Nó mang tính khai báo: bạn cho React biết những gì cần render trong logic của thành phần của bạn và React sẽ tìm ra cách hiển thị nó cho người dùng của bạn tốt nhất. -* [Never call component functions directly](/reference/rules/react-calls-components-and-hooks#never-call-component-functions-directly) – Components should only be used in JSX. Don’t call them as regular functions. -* [Never pass around hooks as regular values](/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values) – Hooks should only be called inside of components. Never pass it around as a regular value. +* [Không bao giờ gọi trực tiếp các hàm thành phần](/reference/rules/react-calls-components-and-hooks#never-call-component-functions-directly) – Các thành phần chỉ nên được sử dụng trong JSX. Không gọi chúng như các hàm thông thường. +* [Không bao giờ truyền hook như các giá trị thông thường](/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values) – Hook chỉ nên được gọi bên trong các thành phần. Không bao giờ truyền nó như một giá trị thông thường. --- -## Rules of Hooks {/*rules-of-hooks*/} +## Các quy tắc của Hook {/*rules-of-hooks*/} -Hooks are defined using JavaScript functions, but they represent a special type of reusable UI logic with restrictions on where they can be called. You need to follow the [Rules of Hooks](/reference/rules/rules-of-hooks) when using them. - -* [Only call Hooks at the top level](/reference/rules/rules-of-hooks#only-call-hooks-at-the-top-level) – Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. -* [Only call Hooks from React functions](/reference/rules/rules-of-hooks#only-call-hooks-from-react-functions) – Don’t call Hooks from regular JavaScript functions. +Hook được định nghĩa bằng các hàm JavaScript, nhưng chúng đại diện cho một loại logic UI có thể tái sử dụng đặc biệt với các hạn chế về nơi chúng có thể được gọi. Bạn cần tuân theo [Các quy tắc của Hook](/reference/rules/rules-of-hooks) khi sử dụng chúng. +* [Chỉ gọi Hook ở cấp cao nhất](/reference/rules/rules-of-hooks#only-call-hooks-at-the-top-level) – Không gọi Hook bên trong vòng lặp, điều kiện hoặc hàm lồng nhau. Thay vào đó, hãy luôn sử dụng Hook ở cấp cao nhất của hàm React của bạn, trước bất kỳ lệnh trả về sớm nào. +* [Chỉ gọi Hook từ các hàm React](/reference/rules/rules-of-hooks#only-call-hooks-from-react-functions) – Không gọi Hook từ các hàm JavaScript thông thường. diff --git a/src/content/reference/rules/react-calls-components-and-hooks.md b/src/content/reference/rules/react-calls-components-and-hooks.md index 3d865b4f2..57d028acb 100644 --- a/src/content/reference/rules/react-calls-components-and-hooks.md +++ b/src/content/reference/rules/react-calls-components-and-hooks.md @@ -1,86 +1,86 @@ --- -title: React calls Components and Hooks +title: React gọi Components và Hooks --- <Intro> -React is responsible for rendering components and Hooks when necessary to optimize the user experience. It is declarative: you tell React what to render in your component’s logic, and React will figure out how best to display it to your user. +React chịu trách nhiệm hiển thị các component và Hook khi cần thiết để tối ưu hóa trải nghiệm người dùng. Nó mang tính khai báo: bạn cho React biết những gì cần hiển thị trong logic của component và React sẽ tìm ra cách tốt nhất để hiển thị nó cho người dùng của bạn. </Intro> <InlineToc /> --- -## Never call component functions directly {/*never-call-component-functions-directly*/} -Components should only be used in JSX. Don't call them as regular functions. React should call it. +## Không bao giờ gọi trực tiếp các hàm component {/*never-call-component-functions-directly*/} +Các component chỉ nên được sử dụng trong JSX. Không gọi chúng như các hàm thông thường. React nên gọi nó. -React must decide when your component function is called [during rendering](/reference/rules/components-and-hooks-must-be-pure#how-does-react-run-your-code). In React, you do this using JSX. +React phải quyết định khi nào hàm component của bạn được gọi [trong quá trình render](/reference/rules/components-and-hooks-must-be-pure#how-does-react-run-your-code). Trong React, bạn thực hiện điều này bằng JSX. ```js {2} function BlogPost() { - return <Layout><Article /></Layout>; // ✅ Good: Only use components in JSX + return <Layout><Article /></Layout>; // ✅ Tốt: Chỉ sử dụng component trong JSX } ``` ```js {2} function BlogPost() { - return <Layout>{Article()}</Layout>; // 🔴 Bad: Never call them directly + return <Layout>{Article()}</Layout>; // 🔴 Sai: Không bao giờ gọi chúng trực tiếp } ``` -If a component contains Hooks, it's easy to violate the [Rules of Hooks](/reference/rules/rules-of-hooks) when components are called directly in a loop or conditionally. +Nếu một component chứa Hook, rất dễ vi phạm [Quy tắc của Hook](/reference/rules/rules-of-hooks) khi các component được gọi trực tiếp trong một vòng lặp hoặc có điều kiện. -Letting React orchestrate rendering also allows a number of benefits: +Việc để React điều phối quá trình render cũng mang lại một số lợi ích: -* **Components become more than functions.** React can augment them with features like _local state_ through Hooks that are tied to the component's identity in the tree. -* **Component types participate in reconciliation.** By letting React call your components, you also tell it more about the conceptual structure of your tree. For example, when you move from rendering `<Feed>` to the `<Profile>` page, React won’t attempt to re-use them. -* **React can enhance your user experience.** For example, it can let the browser do some work between component calls so that re-rendering a large component tree doesn’t block the main thread. -* **A better debugging story.** If components are first-class citizens that the library is aware of, we can build rich developer tools for introspection in development. -* **More efficient reconciliation.** React can decide exactly which components in the tree need re-rendering and skip over the ones that don't. That makes your app faster and more snappy. +* **Các component trở nên nhiều hơn là các hàm.** React có thể tăng cường chúng bằng các tính năng như _trạng thái cục bộ_ thông qua Hook được gắn với định danh của component trong cây. +* **Các loại component tham gia vào quá trình hòa giải.** Bằng cách cho phép React gọi các component của bạn, bạn cũng cho nó biết thêm về cấu trúc khái niệm của cây của bạn. Ví dụ: khi bạn chuyển từ hiển thị `<Feed>` sang trang `<Profile>`, React sẽ không cố gắng sử dụng lại chúng. +* **React có thể nâng cao trải nghiệm người dùng của bạn.** Ví dụ: nó có thể cho phép trình duyệt thực hiện một số công việc giữa các lệnh gọi component để việc render lại một cây component lớn không chặn luồng chính. +* **Câu chuyện gỡ lỗi tốt hơn.** Nếu các component là các phần tử hạng nhất mà thư viện nhận biết được, chúng ta có thể xây dựng các công cụ dành cho nhà phát triển phong phú để xem xét nội bộ trong quá trình phát triển. +* **Hòa giải hiệu quả hơn.** React có thể quyết định chính xác những component nào trong cây cần render lại và bỏ qua những component không cần. Điều đó làm cho ứng dụng của bạn nhanh hơn và nhạy hơn. --- -## Never pass around Hooks as regular values {/*never-pass-around-hooks-as-regular-values*/} +## Không bao giờ truyền Hook như các giá trị thông thường {/*never-pass-around-hooks-as-regular-values*/} -Hooks should only be called inside of components or Hooks. Never pass it around as a regular value. +Hook chỉ nên được gọi bên trong các component hoặc Hook. Không bao giờ truyền nó như một giá trị thông thường. -Hooks allow you to augment a component with React features. They should always be called as a function, and never passed around as a regular value. This enables _local reasoning_, or the ability for developers to understand everything a component can do by looking at that component in isolation. +Hook cho phép bạn tăng cường một component với các tính năng của React. Chúng phải luôn được gọi như một hàm và không bao giờ được truyền như một giá trị thông thường. Điều này cho phép _lý luận cục bộ_, hoặc khả năng các nhà phát triển hiểu mọi thứ mà một component có thể làm bằng cách xem xét component đó một cách riêng biệt. -Breaking this rule will cause React to not automatically optimize your component. +Vi phạm quy tắc này sẽ khiến React không tự động tối ưu hóa component của bạn. -### Don't dynamically mutate a Hook {/*dont-dynamically-mutate-a-hook*/} +### Không thay đổi Hook một cách động {/*dont-dynamically-mutate-a-hook*/} -Hooks should be as "static" as possible. This means you shouldn't dynamically mutate them. For example, this means you shouldn't write higher order Hooks: +Hook phải "tĩnh" nhất có thể. Điều này có nghĩa là bạn không nên thay đổi chúng một cách động. Ví dụ: điều này có nghĩa là bạn không nên viết Hook bậc cao hơn: ```js {2} function ChatInput() { - const useDataWithLogging = withLogging(useData); // 🔴 Bad: don't write higher order Hooks + const useDataWithLogging = withLogging(useData); // 🔴 Sai: không viết Hook bậc cao hơn const data = useDataWithLogging(); } ``` -Hooks should be immutable and not be mutated. Instead of mutating a Hook dynamically, create a static version of the Hook with the desired functionality. +Hook phải là bất biến và không được thay đổi. Thay vì thay đổi Hook một cách động, hãy tạo một phiên bản tĩnh của Hook với chức năng mong muốn. ```js {2,6} function ChatInput() { - const data = useDataWithLogging(); // ✅ Good: Create a new version of the Hook + const data = useDataWithLogging(); // ✅ Tốt: Tạo một phiên bản mới của Hook } function useDataWithLogging() { - // ... Create a new version of the Hook and inline the logic here + // ... Tạo một phiên bản mới của Hook và nội tuyến logic ở đây } ``` -### Don't dynamically use Hooks {/*dont-dynamically-use-hooks*/} +### Không sử dụng Hook một cách động {/*dont-dynamically-use-hooks*/} -Hooks should also not be dynamically used: for example, instead of doing dependency injection in a component by passing a Hook as a value: +Hook cũng không nên được sử dụng một cách động: ví dụ: thay vì thực hiện dependency injection trong một component bằng cách truyền một Hook làm giá trị: ```js {2} function ChatInput() { - return <Button useData={useDataWithLogging} /> // 🔴 Bad: don't pass Hooks as props + return <Button useData={useDataWithLogging} /> // 🔴 Sai: không truyền Hook làm đạo cụ } ``` -You should always inline the call of the Hook into that component and handle any logic in there. +Bạn nên luôn nội tuyến lệnh gọi Hook vào component đó và xử lý mọi logic ở đó. ```js {6} function ChatInput() { @@ -88,14 +88,13 @@ function ChatInput() { } function Button() { - const data = useDataWithLogging(); // ✅ Good: Use the Hook directly + const data = useDataWithLogging(); // ✅ Tốt: Sử dụng Hook trực tiếp } function useDataWithLogging() { - // If there's any conditional logic to change the Hook's behavior, it should be inlined into - // the Hook + // Nếu có bất kỳ logic có điều kiện nào để thay đổi hành vi của Hook, nó sẽ được nội tuyến vào + // Hook } ``` -This way, `<Button />` is much easier to understand and debug. When Hooks are used in dynamic ways, it increases the complexity of your app greatly and inhibits local reasoning, making your team less productive in the long term. It also makes it easier to accidentally break the [Rules of Hooks](/reference/rules/rules-of-hooks) that Hooks should not be called conditionally. If you find yourself needing to mock components for tests, it's better to mock the server instead to respond with canned data. If possible, it's also usually more effective to test your app with end-to-end tests. - +Bằng cách này, `<Button />` dễ hiểu và gỡ lỗi hơn nhiều. Khi Hook được sử dụng theo những cách động, nó làm tăng đáng kể độ phức tạp của ứng dụng của bạn và ức chế lý luận cục bộ, khiến nhóm của bạn kém năng suất hơn về lâu dài. Nó cũng giúp bạn dễ dàng vô tình phá vỡ [Quy tắc của Hook](/reference/rules/rules-of-hooks) rằng Hook không được gọi có điều kiện. Nếu bạn thấy mình cần mô phỏng các component để kiểm tra, tốt hơn là mô phỏng máy chủ thay vì phản hồi bằng dữ liệu đóng hộp. Nếu có thể, việc kiểm tra ứng dụng của bạn bằng các bài kiểm tra end-to-end thường hiệu quả hơn. diff --git a/src/content/reference/rules/rules-of-hooks.md b/src/content/reference/rules/rules-of-hooks.md index ecaef7c60..4252f960c 100644 --- a/src/content/reference/rules/rules-of-hooks.md +++ b/src/content/reference/rules/rules-of-hooks.md @@ -1,53 +1,53 @@ --- -title: Rules of Hooks +title: Các quy tắc của Hook --- <Intro> -Hooks are defined using JavaScript functions, but they represent a special type of reusable UI logic with restrictions on where they can be called. +Hook được định nghĩa bằng các hàm JavaScript, nhưng chúng đại diện cho một loại logic UI có thể tái sử dụng đặc biệt với các hạn chế về nơi chúng có thể được gọi. </Intro> <InlineToc /> --- -## Only call Hooks at the top level {/*only-call-hooks-at-the-top-level*/} +## Chỉ gọi Hook ở cấp cao nhất {/*only-call-hooks-at-the-top-level*/} -Functions whose names start with `use` are called [*Hooks*](/reference/react) in React. +Các hàm có tên bắt đầu bằng `use` được gọi là [*Hook*](/reference/react) trong React. -**Don’t call Hooks inside loops, conditions, nested functions, or `try`/`catch`/`finally` blocks.** Instead, always use Hooks at the top level of your React function, before any early returns. You can only call Hooks while React is rendering a function component: +**Không gọi Hook bên trong vòng lặp, điều kiện, hàm lồng nhau hoặc khối `try`/`catch`/`finally`.** Thay vào đó, luôn sử dụng Hook ở cấp cao nhất của hàm React, trước bất kỳ lệnh trả về sớm nào. Bạn chỉ có thể gọi Hook khi React đang hiển thị một component hàm: -* ✅ Call them at the top level in the body of a [function component](/learn/your-first-component). -* ✅ Call them at the top level in the body of a [custom Hook](/learn/reusing-logic-with-custom-hooks). +* ✅ Gọi chúng ở cấp cao nhất trong phần thân của [component hàm](/learn/your-first-component). +* ✅ Gọi chúng ở cấp cao nhất trong phần thân của [Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks). ```js{2-3,8-9} function Counter() { - // ✅ Good: top-level in a function component + // ✅ Tốt: cấp cao nhất trong một component hàm const [count, setCount] = useState(0); // ... } function useWindowWidth() { - // ✅ Good: top-level in a custom Hook + // ✅ Tốt: cấp cao nhất trong một Hook tùy chỉnh const [width, setWidth] = useState(window.innerWidth); // ... } ``` -It’s **not** supported to call Hooks (functions starting with `use`) in any other cases, for example: +Không được hỗ trợ việc gọi Hook (các hàm bắt đầu bằng `use`) trong bất kỳ trường hợp nào khác, ví dụ: -* 🔴 Do not call Hooks inside conditions or loops. -* 🔴 Do not call Hooks after a conditional `return` statement. -* 🔴 Do not call Hooks in event handlers. -* 🔴 Do not call Hooks in class components. -* 🔴 Do not call Hooks inside functions passed to `useMemo`, `useReducer`, or `useEffect`. -* 🔴 Do not call Hooks inside `try`/`catch`/`finally` blocks. +* 🔴 Không gọi Hook bên trong điều kiện hoặc vòng lặp. +* 🔴 Không gọi Hook sau câu lệnh `return` có điều kiện. +* 🔴 Không gọi Hook trong trình xử lý sự kiện. +* 🔴 Không gọi Hook trong component class. +* 🔴 Không gọi Hook bên trong các hàm được truyền cho `useMemo`, `useReducer` hoặc `useEffect`. +* 🔴 Không gọi Hook bên trong khối `try`/`catch`/`finally`. -If you break these rules, you might see this error. +Nếu bạn vi phạm các quy tắc này, bạn có thể thấy lỗi này. ```js{3-4,11-12,20-21} function Bad({ cond }) { if (cond) { - // 🔴 Bad: inside a condition (to fix, move it outside!) + // 🔴 Sai: bên trong một điều kiện (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); } // ... @@ -55,7 +55,7 @@ function Bad({ cond }) { function Bad() { for (let i = 0; i < 10; i++) { - // 🔴 Bad: inside a loop (to fix, move it outside!) + // 🔴 Sai: bên trong một vòng lặp (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); } // ... @@ -65,14 +65,14 @@ function Bad({ cond }) { if (cond) { return; } - // 🔴 Bad: after a conditional return (to fix, move it before the return!) + // 🔴 Sai: sau một lệnh return có điều kiện (để sửa, hãy di chuyển nó trước lệnh return!) const theme = useContext(ThemeContext); // ... } function Bad() { function handleClick() { - // 🔴 Bad: inside an event handler (to fix, move it outside!) + // 🔴 Sai: bên trong một trình xử lý sự kiện (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); } // ... @@ -80,7 +80,7 @@ function Bad() { function Bad() { const style = useMemo(() => { - // 🔴 Bad: inside useMemo (to fix, move it outside!) + // 🔴 Sai: bên trong useMemo (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); return createStyle(theme); }); @@ -89,7 +89,7 @@ function Bad() { class Bad extends React.Component { render() { - // 🔴 Bad: inside a class component (to fix, write a function component instead of a class!) + // 🔴 Sai: bên trong một component class (để sửa, hãy viết một component hàm thay vì một class!) useEffect(() => {}) // ... } @@ -97,7 +97,7 @@ class Bad extends React.Component { function Bad() { try { - // 🔴 Bad: inside try/catch/finally block (to fix, move it outside!) + // 🔴 Sai: bên trong khối try/catch/finally (để sửa, hãy di chuyển nó ra ngoài!) const [x, setX] = useState(0); } catch { const [x, setX] = useState(1); @@ -105,31 +105,31 @@ function Bad() { } ``` -You can use the [`eslint-plugin-react-hooks` plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) to catch these mistakes. +Bạn có thể sử dụng [`eslint-plugin-react-hooks` plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) để bắt các lỗi này. <Note> -[Custom Hooks](/learn/reusing-logic-with-custom-hooks) *may* call other Hooks (that's their whole purpose). This works because custom Hooks are also supposed to only be called while a function component is rendering. +[Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks) *có thể* gọi các Hook khác (đó là toàn bộ mục đích của chúng). Điều này hoạt động vì Hook tùy chỉnh cũng chỉ được gọi khi một component hàm đang hiển thị. </Note> --- -## Only call Hooks from React functions {/*only-call-hooks-from-react-functions*/} +## Chỉ gọi Hook từ các hàm React {/*only-call-hooks-from-react-functions*/} -Don’t call Hooks from regular JavaScript functions. Instead, you can: +Không gọi Hook từ các hàm JavaScript thông thường. Thay vào đó, bạn có thể: -✅ Call Hooks from React function components. -✅ Call Hooks from [custom Hooks](/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component). +✅ Gọi Hook từ các component hàm React. +✅ Gọi Hook từ [Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component). -By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code. +Bằng cách tuân theo quy tắc này, bạn đảm bảo rằng tất cả logic có trạng thái trong một component đều hiển thị rõ ràng từ mã nguồn của nó. ```js {2,5} function FriendList() { const [onlineStatus, setOnlineStatus] = useOnlineStatus(); // ✅ } -function setOnlineStatus() { // ❌ Not a component or custom Hook! +function setOnlineStatus() { // ❌ Không phải là một component hoặc Hook tùy chỉnh! const [onlineStatus, setOnlineStatus] = useOnlineStatus(); } ``` diff --git a/src/content/warnings/invalid-aria-prop.md b/src/content/warnings/invalid-aria-prop.md index 2d3b4253e..5e0c12b60 100644 --- a/src/content/warnings/invalid-aria-prop.md +++ b/src/content/warnings/invalid-aria-prop.md @@ -1,11 +1,11 @@ --- -title: Invalid ARIA Prop Warning +title: Cảnh báo thuộc tính ARIA không hợp lệ --- -This warning will fire if you attempt to render a DOM element with an `aria-*` prop that does not exist in the Web Accessibility Initiative (WAI) Accessible Rich Internet Application (ARIA) [specification](https://www.w3.org/TR/wai-aria-1.1/#states_and_properties). +Cảnh báo này sẽ xuất hiện nếu bạn cố gắng hiển thị một phần tử DOM với một thuộc tính `aria-*` không tồn tại trong [đặc tả](https://www.w3.org/TR/wai-aria-1.1/#states_and_properties) của Web Accessibility Initiative (WAI) Accessible Rich Internet Application (ARIA). -1. If you feel that you are using a valid prop, check the spelling carefully. `aria-labelledby` and `aria-activedescendant` are often misspelled. +1. Nếu bạn cảm thấy rằng bạn đang sử dụng một thuộc tính hợp lệ, hãy kiểm tra cẩn thận chính tả. `aria-labelledby` và `aria-activedescendant` thường bị viết sai chính tả. -2. If you wrote `aria-role`, you may have meant `role`. +2. Nếu bạn viết `aria-role`, có thể bạn muốn viết `role`. -3. Otherwise, if you're on the latest version of React DOM and verified that you're using a valid property name listed in the ARIA specification, please [report a bug](https://github.com/facebook/react/issues/new/choose). +3. Nếu không, nếu bạn đang sử dụng phiên bản React DOM mới nhất và đã xác minh rằng bạn đang sử dụng một tên thuộc tính hợp lệ được liệt kê trong đặc tả ARIA, vui lòng [báo cáo lỗi](https://github.com/facebook/react/issues/new/choose). diff --git a/src/content/warnings/invalid-hook-call-warning.md b/src/content/warnings/invalid-hook-call-warning.md index 5bbc2bbaa..b083b459d 100644 --- a/src/content/warnings/invalid-hook-call-warning.md +++ b/src/content/warnings/invalid-hook-call-warning.md @@ -1,60 +1,60 @@ --- -title: Rules of Hooks +title: Các quy tắc của Hook --- -You are probably here because you got the following error message: +Có thể bạn đang ở đây vì bạn gặp thông báo lỗi sau: <ConsoleBlock level="error"> -Hooks can only be called inside the body of a function component. +Hooks chỉ có thể được gọi bên trong phần thân của một component hàm. </ConsoleBlock> -There are three common reasons you might be seeing it: +Có ba lý do phổ biến bạn có thể gặp phải lỗi này: -1. You might be **breaking the Rules of Hooks**. -2. You might have **mismatching versions** of React and React DOM. -3. You might have **more than one copy of React** in the same app. +1. Bạn có thể đang **phá vỡ Các quy tắc của Hook**. +2. Bạn có thể có **các phiên bản không khớp** của React và React DOM. +3. Bạn có thể có **nhiều hơn một bản sao của React** trong cùng một ứng dụng. -Let's look at each of these cases. +Hãy xem xét từng trường hợp này. -## Breaking Rules of Hooks {/*breaking-rules-of-hooks*/} +## Phá vỡ các quy tắc của Hook {/*breaking-rules-of-hooks*/} -Functions whose names start with `use` are called [*Hooks*](/reference/react) in React. +Các hàm có tên bắt đầu bằng `use` được gọi là [*Hook*](/reference/react) trong React. -**Don’t call Hooks inside loops, conditions, or nested functions.** Instead, always use Hooks at the top level of your React function, before any early returns. You can only call Hooks while React is rendering a function component: +**Không gọi Hook bên trong vòng lặp, điều kiện hoặc hàm lồng nhau.** Thay vào đó, luôn sử dụng Hook ở cấp cao nhất của hàm React, trước bất kỳ lệnh `return` sớm nào. Bạn chỉ có thể gọi Hook khi React đang render một component hàm: -* ✅ Call them at the top level in the body of a [function component](/learn/your-first-component). -* ✅ Call them at the top level in the body of a [custom Hook](/learn/reusing-logic-with-custom-hooks). +* ✅ Gọi chúng ở cấp cao nhất trong phần thân của một [component hàm](/learn/your-first-component). +* ✅ Gọi chúng ở cấp cao nhất trong phần thân của một [Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks). ```js{2-3,8-9} function Counter() { - // ✅ Good: top-level in a function component + // ✅ Tốt: cấp cao nhất trong một component hàm const [count, setCount] = useState(0); // ... } function useWindowWidth() { - // ✅ Good: top-level in a custom Hook + // ✅ Tốt: cấp cao nhất trong một Hook tùy chỉnh const [width, setWidth] = useState(window.innerWidth); // ... } ``` -It’s **not** supported to call Hooks (functions starting with `use`) in any other cases, for example: +Không được hỗ trợ gọi Hook (các hàm bắt đầu bằng `use`) trong bất kỳ trường hợp nào khác, ví dụ: -* 🔴 Do not call Hooks inside conditions or loops. -* 🔴 Do not call Hooks after a conditional `return` statement. -* 🔴 Do not call Hooks in event handlers. -* 🔴 Do not call Hooks in class components. -* 🔴 Do not call Hooks inside functions passed to `useMemo`, `useReducer`, or `useEffect`. +* 🔴 Không gọi Hook bên trong điều kiện hoặc vòng lặp. +* 🔴 Không gọi Hook sau một câu lệnh `return` có điều kiện. +* 🔴 Không gọi Hook trong trình xử lý sự kiện. +* 🔴 Không gọi Hook trong component lớp. +* 🔴 Không gọi Hook bên trong các hàm được truyền cho `useMemo`, `useReducer` hoặc `useEffect`. -If you break these rules, you might see this error. +Nếu bạn phá vỡ các quy tắc này, bạn có thể thấy lỗi này. ```js{3-4,11-12,20-21} function Bad({ cond }) { if (cond) { - // 🔴 Bad: inside a condition (to fix, move it outside!) + // 🔴 Sai: bên trong một điều kiện (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); } // ... @@ -62,7 +62,7 @@ function Bad({ cond }) { function Bad() { for (let i = 0; i < 10; i++) { - // 🔴 Bad: inside a loop (to fix, move it outside!) + // 🔴 Sai: bên trong một vòng lặp (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); } // ... @@ -72,14 +72,14 @@ function Bad({ cond }) { if (cond) { return; } - // 🔴 Bad: after a conditional return (to fix, move it before the return!) + // 🔴 Sai: sau một return có điều kiện (để sửa, hãy di chuyển nó trước return!) const theme = useContext(ThemeContext); // ... } function Bad() { function handleClick() { - // 🔴 Bad: inside an event handler (to fix, move it outside!) + // 🔴 Sai: bên trong một trình xử lý sự kiện (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); } // ... @@ -87,7 +87,7 @@ function Bad() { function Bad() { const style = useMemo(() => { - // 🔴 Bad: inside useMemo (to fix, move it outside!) + // 🔴 Sai: bên trong useMemo (để sửa, hãy di chuyển nó ra ngoài!) const theme = useContext(ThemeContext); return createStyle(theme); }); @@ -96,32 +96,32 @@ function Bad() { class Bad extends React.Component { render() { - // 🔴 Bad: inside a class component (to fix, write a function component instead of a class!) + // 🔴 Sai: bên trong một component lớp (để sửa, hãy viết một component hàm thay vì một lớp!) useEffect(() => {}) // ... } } ``` -You can use the [`eslint-plugin-react-hooks` plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) to catch these mistakes. +Bạn có thể sử dụng [`eslint-plugin-react-hooks` plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks) để bắt các lỗi này. <Note> -[Custom Hooks](/learn/reusing-logic-with-custom-hooks) *may* call other Hooks (that's their whole purpose). This works because custom Hooks are also supposed to only be called while a function component is rendering. +[Hook tùy chỉnh](/learn/reusing-logic-with-custom-hooks) *có thể* gọi các Hook khác (đó là mục đích của chúng). Điều này hoạt động vì Hook tùy chỉnh cũng chỉ được gọi khi một component hàm đang render. </Note> -## Mismatching Versions of React and React DOM {/*mismatching-versions-of-react-and-react-dom*/} +## Các phiên bản không khớp của React và React DOM {/*mismatching-versions-of-react-and-react-dom*/} -You might be using a version of `react-dom` (< 16.8.0) or `react-native` (< 0.59) that doesn't yet support Hooks. You can run `npm ls react-dom` or `npm ls react-native` in your application folder to check which version you're using. If you find more than one of them, this might also create problems (more on that below). +Bạn có thể đang sử dụng một phiên bản của `react-dom` (< 16.8.0) hoặc `react-native` (< 0.59) chưa hỗ trợ Hook. Bạn có thể chạy `npm ls react-dom` hoặc `npm ls react-native` trong thư mục ứng dụng của bạn để kiểm tra phiên bản bạn đang sử dụng. Nếu bạn tìm thấy nhiều hơn một trong số chúng, điều này cũng có thể tạo ra vấn đề (thêm về điều đó bên dưới). -## Duplicate React {/*duplicate-react*/} +## React trùng lặp {/*duplicate-react*/} -In order for Hooks to work, the `react` import from your application code needs to resolve to the same module as the `react` import from inside the `react-dom` package. +Để Hook hoạt động, import `react` từ mã ứng dụng của bạn cần phân giải thành cùng một module với import `react` từ bên trong package `react-dom`. -If these `react` imports resolve to two different exports objects, you will see this warning. This may happen if you **accidentally end up with two copies** of the `react` package. +Nếu các import `react` này phân giải thành hai đối tượng export khác nhau, bạn sẽ thấy cảnh báo này. Điều này có thể xảy ra nếu bạn **vô tình có hai bản sao** của package `react`. -If you use Node for package management, you can run this check in your project folder: +Nếu bạn sử dụng Node để quản lý package, bạn có thể chạy kiểm tra này trong thư mục dự án của bạn: <TerminalBlock> @@ -129,30 +129,30 @@ npm ls react </TerminalBlock> -If you see more than one React, you'll need to figure out why this happens and fix your dependency tree. For example, maybe a library you're using incorrectly specifies `react` as a dependency (rather than a peer dependency). Until that library is fixed, [Yarn resolutions](https://yarnpkg.com/lang/en/docs/selective-version-resolutions/) is one possible workaround. +Nếu bạn thấy nhiều hơn một React, bạn sẽ cần tìm ra lý do tại sao điều này xảy ra và sửa cây phụ thuộc của bạn. Ví dụ: có thể một thư viện bạn đang sử dụng chỉ định không chính xác `react` là một dependency (thay vì một peer dependency). Cho đến khi thư viện đó được sửa, [Yarn resolutions](https://yarnpkg.com/lang/en/docs/selective-version-resolutions/) là một giải pháp thay thế khả thi. -You can also try to debug this problem by adding some logs and restarting your development server: +Bạn cũng có thể thử gỡ lỗi vấn đề này bằng cách thêm một số log và khởi động lại máy chủ phát triển của bạn: ```js -// Add this in node_modules/react-dom/index.js +// Thêm dòng này vào node_modules/react-dom/index.js window.React1 = require('react'); -// Add this in your component file +// Thêm dòng này vào file component của bạn require('react-dom'); window.React2 = require('react'); console.log(window.React1 === window.React2); ``` -If it prints `false` then you might have two Reacts and need to figure out why that happened. [This issue](https://github.com/facebook/react/issues/13991) includes some common reasons encountered by the community. +Nếu nó in ra `false` thì bạn có thể có hai React và cần tìm ra lý do tại sao điều đó xảy ra. [Vấn đề này](https://github.com/facebook/react/issues/13991) bao gồm một số lý do phổ biến mà cộng đồng gặp phải. -This problem can also come up when you use `npm link` or an equivalent. In that case, your bundler might "see" two Reacts — one in application folder and one in your library folder. Assuming `myapp` and `mylib` are sibling folders, one possible fix is to run `npm link ../myapp/node_modules/react` from `mylib`. This should make the library use the application's React copy. +Vấn đề này cũng có thể xảy ra khi bạn sử dụng `npm link` hoặc một lệnh tương đương. Trong trường hợp đó, trình đóng gói của bạn có thể "nhìn thấy" hai React — một trong thư mục ứng dụng và một trong thư mục thư viện của bạn. Giả sử `myapp` và `mylib` là các thư mục cùng cấp, một cách khắc phục có thể là chạy `npm link ../myapp/node_modules/react` từ `mylib`. Điều này sẽ làm cho thư viện sử dụng bản sao React của ứng dụng. <Note> -In general, React supports using multiple independent copies on one page (for example, if an app and a third-party widget both use it). It only breaks if `require('react')` resolves differently between the component and the `react-dom` copy it was rendered with. +Nói chung, React hỗ trợ sử dụng nhiều bản sao độc lập trên một trang (ví dụ: nếu một ứng dụng và một widget của bên thứ ba đều sử dụng nó). Nó chỉ bị hỏng nếu `require('react')` phân giải khác nhau giữa component và bản sao `react-dom` mà nó được render cùng. </Note> -## Other Causes {/*other-causes*/} +## Các nguyên nhân khác {/*other-causes*/} -If none of this worked, please comment in [this issue](https://github.com/facebook/react/issues/13991) and we'll try to help. Try to create a small reproducing example — you might discover the problem as you're doing it. +Nếu không có cách nào trong số này hiệu quả, vui lòng bình luận trong [vấn đề này](https://github.com/facebook/react/issues/13991) và chúng tôi sẽ cố gắng trợ giúp. Hãy thử tạo một ví dụ tái hiện nhỏ — bạn có thể khám phá ra vấn đề khi bạn đang thực hiện nó. diff --git a/src/content/warnings/react-dom-test-utils.md b/src/content/warnings/react-dom-test-utils.md index 2dc33e5b2..1a589d2a7 100644 --- a/src/content/warnings/react-dom-test-utils.md +++ b/src/content/warnings/react-dom-test-utils.md @@ -1,36 +1,36 @@ --- -title: react-dom/test-utils Deprecation Warnings +title: Cảnh báo ngừng sử dụng react-dom/test-utils --- -TODO: update for 19? +TODO: update cho 19? -## ReactDOMTestUtils.act() warning {/*reactdomtestutilsact-warning*/} +## Cảnh báo ReactDOMTestUtils.act() {/*reactdomtestutilsact-warning*/} -`act` from `react-dom/test-utils` has been deprecated in favor of `act` from `react`. +`act` từ `react-dom/test-utils` đã bị ngừng sử dụng và thay thế bằng `act` từ `react`. -Before: +Trước: ```js import {act} from 'react-dom/test-utils'; ``` -After: +Sau: ```js import {act} from 'react'; ``` -## Rest of ReactDOMTestUtils APIS {/*rest-of-reactdomtestutils-apis*/} +## Các API còn lại của ReactDOMTestUtils {/*rest-of-reactdomtestutils-apis*/} -All APIs except `act` have been removed. +Tất cả các API ngoại trừ `act` đã bị xóa. -The React Team recommends migrating your tests to [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) for a modern and well supported testing experience. +React Team khuyến nghị bạn nên chuyển các thử nghiệm của mình sang [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) để có trải nghiệm kiểm thử hiện đại và được hỗ trợ tốt. ### ReactDOMTestUtils.renderIntoDocument {/*reactdomtestutilsrenderintodocument*/} -`renderIntoDocument` can be replaced with `render` from `@testing-library/react`. +`renderIntoDocument` có thể được thay thế bằng `render` từ `@testing-library/react`. -Before: +Trước: ```js import {renderIntoDocument} from 'react-dom/test-utils'; @@ -38,7 +38,7 @@ import {renderIntoDocument} from 'react-dom/test-utils'; renderIntoDocument(<Component />); ``` -After: +Sau: ```js import {render} from '@testing-library/react'; @@ -48,9 +48,9 @@ render(<Component />); ### ReactDOMTestUtils.Simulate {/*reactdomtestutilssimulate*/} -`Simulate` can be replaced with `fireEvent` from `@testing-library/react`. +`Simulate` có thể được thay thế bằng `fireEvent` từ `@testing-library/react`. -Before: +Trước: ```js import {Simulate} from 'react-dom/test-utils'; @@ -59,7 +59,7 @@ const element = document.querySelector('button'); Simulate.click(element); ``` -After: +Sau: ```js import {fireEvent} from '@testing-library/react'; @@ -68,9 +68,9 @@ const element = document.querySelector('button'); fireEvent.click(element); ``` -Be aware that `fireEvent` dispatches an actual event on the element and doesn't just synthetically call the event handler. +Lưu ý rằng `fireEvent` sẽ kích hoạt một sự kiện thực tế trên phần tử và không chỉ gọi trình xử lý sự kiện một cách tổng hợp. -### List of all removed APIs {/*list-of-all-removed-apis-list-of-all-removed-apis*/} +### Danh sách tất cả các API đã bị xóa {/*list-of-all-removed-apis-list-of-all-removed-apis*/} - `mockComponent()` - `isElement()` diff --git a/src/content/warnings/react-test-renderer.md b/src/content/warnings/react-test-renderer.md index da7623fe2..c5cfb508a 100644 --- a/src/content/warnings/react-test-renderer.md +++ b/src/content/warnings/react-test-renderer.md @@ -1,16 +1,15 @@ --- -title: react-test-renderer Deprecation Warnings +title: Cảnh báo ngừng sử dụng react-test-renderer --- -TODO: Update this for 19? +TODO: Cập nhật cho bản 19? -## ReactTestRenderer.create() warning {/*reacttestrenderercreate-warning*/} +## Cảnh báo ReactTestRenderer.create() {/*reacttestrenderercreate-warning*/} -react-test-renderer is deprecated. A warning will fire whenever calling ReactTestRenderer.create() or ReactShallowRender.render(). The react-test-renderer package will remain available on NPM but will not be maintained and may break with new React features or changes to React's internals. +react-test-renderer đã bị ngừng sử dụng. Một cảnh báo sẽ xuất hiện bất cứ khi nào gọi ReactTestRenderer.create() hoặc ReactShallowRender.render(). Gói react-test-renderer sẽ vẫn có sẵn trên NPM nhưng sẽ không được bảo trì và có thể bị hỏng với các tính năng mới của React hoặc các thay đổi đối với các thành phần bên trong của React. -The React Team recommends migrating your tests to [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) or [@testing-library/react-native](https://callstack.github.io/react-native-testing-library/docs/start/intro) for a modern and well supported testing experience. +Nhóm React khuyên bạn nên di chuyển các thử nghiệm của mình sang [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/) hoặc [@testing-library/react-native](https://callstack.github.io/react-native-testing-library/docs/start/intro/) để có trải nghiệm kiểm tra hiện đại và được hỗ trợ tốt. +## Cảnh báo new ShallowRenderer() {/*new-shallowrenderer-warning*/} -## new ShallowRenderer() warning {/*new-shallowrenderer-warning*/} - -The react-test-renderer package no longer exports a shallow renderer at `react-test-renderer/shallow`. This was simply a repackaging of a previously extracted separate package: `react-shallow-renderer`. Therefore you can continue using the shallow renderer in the same way by installing it directly. See [Github](https://github.com/enzymejs/react-shallow-renderer) / [NPM](https://www.npmjs.com/package/react-shallow-renderer). +Gói react-test-renderer không còn xuất trình kết xuất nông tại `react-test-renderer/shallow`. Đây chỉ đơn giản là việc đóng gói lại một gói riêng biệt đã được trích xuất trước đó: `react-shallow-renderer`. Do đó, bạn có thể tiếp tục sử dụng trình kết xuất nông theo cách tương tự bằng cách cài đặt trực tiếp. Xem [Github](https://github.com/enzymejs/react-shallow-renderer) / [NPM](https://www.npmjs.com/package/react-shallow-renderer). diff --git a/src/content/warnings/special-props.md b/src/content/warnings/special-props.md index 1646b531a..e9a1dff9f 100644 --- a/src/content/warnings/special-props.md +++ b/src/content/warnings/special-props.md @@ -1,7 +1,7 @@ --- -title: Special Props Warning +title: Cảnh báo về các Props đặc biệt --- -Most props on a JSX element are passed on to the component, however, there are two special props (`ref` and `key`) which are used by React, and are thus not forwarded to the component. +Hầu hết các props trên một phần tử JSX được chuyển cho component, tuy nhiên, có hai props đặc biệt (`ref` và `key`) được React sử dụng và do đó không được chuyển tiếp đến component. -For instance, you can't read `props.key` from a component. If you need to access the same value within the child component, you should pass it as a different prop (ex: `<ListItemWrapper key={result.id} id={result.id} />` and read `props.id`). While this may seem redundant, it's important to separate app logic from hints to React. +Ví dụ: bạn không thể đọc `props.key` từ một component. Nếu bạn cần truy cập cùng một giá trị bên trong component con, bạn nên chuyển nó như một prop khác (ví dụ: `<ListItemWrapper key={result.id} id={result.id} />` và đọc `props.id`). Mặc dù điều này có vẻ dư thừa, nhưng điều quan trọng là phải tách biệt logic ứng dụng khỏi các gợi ý cho React. diff --git a/src/content/warnings/unknown-prop.md b/src/content/warnings/unknown-prop.md index 80bcdb142..c245ee997 100644 --- a/src/content/warnings/unknown-prop.md +++ b/src/content/warnings/unknown-prop.md @@ -1,38 +1,38 @@ --- -title: Unknown Prop Warning +title: Cảnh báo Prop Không Xác Định --- -The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around. +Cảnh báo unknown-prop sẽ xuất hiện nếu bạn cố gắng render một phần tử DOM với một prop mà React không nhận ra là một thuộc tính/property DOM hợp lệ. Bạn nên đảm bảo rằng các phần tử DOM của bạn không có các prop giả mạo xung quanh. -There are a couple of likely reasons this warning could be appearing: +Có một vài lý do có thể khiến cảnh báo này xuất hiện: -1. Are you using `{...props}` or `cloneElement(element, props)`? When copying props to a child component, you should ensure that you are not accidentally forwarding props that were intended only for the parent component. See common fixes for this problem below. +1. Bạn có đang sử dụng `{...props}` hoặc `cloneElement(element, props)` không? Khi sao chép các prop cho một component con, bạn nên đảm bảo rằng bạn không vô tình chuyển tiếp các prop chỉ dành cho component cha. Xem các cách khắc phục phổ biến cho vấn đề này bên dưới. -2. You are using a non-standard DOM attribute on a native DOM node, perhaps to represent custom data. If you are trying to attach custom data to a standard DOM element, consider using a custom data attribute as described [on MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes). +2. Bạn đang sử dụng một thuộc tính DOM không chuẩn trên một nút DOM gốc, có thể để biểu diễn dữ liệu tùy chỉnh. Nếu bạn đang cố gắng đính kèm dữ liệu tùy chỉnh vào một phần tử DOM tiêu chuẩn, hãy cân nhắc sử dụng thuộc tính data tùy chỉnh như được mô tả [trên MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes). -3. React does not yet recognize the attribute you specified. This will likely be fixed in a future version of React. React will allow you to pass it without a warning if you write the attribute name lowercase. +3. React chưa nhận ra thuộc tính bạn đã chỉ định. Điều này có thể sẽ được khắc phục trong một phiên bản React trong tương lai. React sẽ cho phép bạn chuyển nó mà không có cảnh báo nếu bạn viết tên thuộc tính ở dạng chữ thường. -4. You are using a React component without an upper case, for example `<myButton />`. React interprets it as a DOM tag because React JSX transform uses the upper vs. lower case convention to distinguish between user-defined components and DOM tags. For your own React components, use PascalCase. For example, write `<MyButton />` instead of `<myButton />`. +4. Bạn đang sử dụng một component React mà không có chữ hoa, ví dụ: `<myButton />`. React hiểu nó là một thẻ DOM vì chuyển đổi React JSX sử dụng quy ước chữ hoa so với chữ thường để phân biệt giữa các component do người dùng định nghĩa và các thẻ DOM. Đối với các component React của riêng bạn, hãy sử dụng PascalCase. Ví dụ: viết `<MyButton />` thay vì `<myButton />`. --- -If you get this warning because you pass props like `{...props}`, your parent component needs to "consume" any prop that is intended for the parent component and not intended for the child component. Example: +Nếu bạn nhận được cảnh báo này vì bạn truyền các prop như `{...props}`, component cha của bạn cần "tiêu thụ" bất kỳ prop nào dành cho component cha và không dành cho component con. Ví dụ: -**Bad:** Unexpected `layout` prop is forwarded to the `div` tag. +**Tệ:** Prop `layout` không mong muốn được chuyển tiếp đến thẻ `div`. ```js function MyDiv(props) { if (props.layout === 'horizontal') { - // BAD! Because you know for sure "layout" is not a prop that <div> understands. + // TỆ! Vì bạn chắc chắn "layout" không phải là một prop mà <div> hiểu. return <div {...props} style={getHorizontalStyle()} /> } else { - // BAD! Because you know for sure "layout" is not a prop that <div> understands. + // TỆ! Vì bạn chắc chắn "layout" không phải là một prop mà <div> hiểu. return <div {...props} style={getVerticalStyle()} /> } } ``` -**Good:** The spread syntax can be used to pull variables off props, and put the remaining props into a variable. +**Tốt:** Cú pháp spread có thể được sử dụng để kéo các biến ra khỏi props và đặt các prop còn lại vào một biến. ```js function MyDiv(props) { @@ -45,7 +45,7 @@ function MyDiv(props) { } ``` -**Good:** You can also assign the props to a new object and delete the keys that you're using from the new object. Be sure not to delete the props from the original `this.props` object, since that object should be considered immutable. +**Tốt:** Bạn cũng có thể gán các prop cho một đối tượng mới và xóa các khóa mà bạn đang sử dụng khỏi đối tượng mới. Hãy nhớ không xóa các prop khỏi đối tượng `this.props` ban đầu, vì đối tượng đó nên được coi là bất biến. ```js function MyDiv(props) { diff --git a/src/sidebarCommunity.json b/src/sidebarCommunity.json index ac8a172d5..862e3365a 100644 --- a/src/sidebarCommunity.json +++ b/src/sidebarCommunity.json @@ -1,46 +1,46 @@ { - "title": "Community", + "title": "Cộng đồng", "path": "/community", "routes": [ { "hasSectionHeader": true, - "sectionHeader": "GET INVOLVED" + "sectionHeader": "THAM GIA" }, { - "title": "Community", + "title": "Cộng đồng", "path": "/community", "skipBreadcrumb": true, "routes": [ { - "title": "React Conferences", + "title": "Hội nghị React", "path": "/community/conferences" }, { - "title": "React Meetups", + "title": "Gặp gỡ React", "path": "/community/meetups" }, { - "title": "React Videos", + "title": "Video React", "path": "/community/videos" }, { - "title": "Meet the Team", + "title": "Gặp gỡ Nhóm", "path": "/community/team" }, { - "title": "Docs Contributors", + "title": "Người đóng góp tài liệu", "path": "/community/docs-contributors" }, { - "title": "Translations", + "title": "Bản dịch", "path": "/community/translations" }, { - "title": "Acknowledgements", + "title": "Lời cảm ơn", "path": "/community/acknowledgements" }, { - "title": "Versioning Policy", + "title": "Chính sách phiên bản", "path": "/community/versioning-policy" } ] diff --git a/src/sidebarHome.json b/src/sidebarHome.json index 4509c26fc..f2eabcdd8 100644 --- a/src/sidebarHome.json +++ b/src/sidebarHome.json @@ -1,37 +1,37 @@ { - "title": "React Docs", + "title": "Tài liệu React", "path": "/", "routes": [ { "hasSectionHeader": true, - "sectionHeader": "GET STARTED" + "sectionHeader": "BẮT ĐẦU" }, { - "title": "Quick Start", + "title": "Bắt Đầu Nhanh", "path": "/learn" }, { - "title": "Installation", + "title": "Cài đặt", "path": "/learn/installation" }, { "hasSectionHeader": true, - "sectionHeader": "LEARN REACT" + "sectionHeader": "HỌC REACT" }, { - "title": "Describing the UI", + "title": "Mô tả giao diện người dùng", "path": "/learn/describing-the-ui" }, { - "title": "Adding Interactivity", + "title": "Thêm tính tương tác", "path": "/learn/adding-interactivity" }, { - "title": "Managing State", + "title": "Quản lý trạng thái", "path": "/learn/managing-state" }, { - "title": "Escape Hatches", + "title": "Các lối thoát hiểm", "path": "/learn/escape-hatches" }, { @@ -76,18 +76,18 @@ }, { "hasSectionHeader": true, - "sectionHeader": "GET INVOLVED" + "sectionHeader": "THAM GIA" }, { - "title": "React Community", + "title": "Cộng đồng React", "path": "/community" }, { "hasSectionHeader": true, - "sectionHeader": "STAY INFORMED" + "sectionHeader": "LUÔN CẬP NHẬT" }, { - "title": "React Blog", + "title": "Blog React", "path": "/blog" } ] diff --git a/src/sidebarLearn.json b/src/sidebarLearn.json index c94abbe74..9e2682bfc 100644 --- a/src/sidebarLearn.json +++ b/src/sidebarLearn.json @@ -1,17 +1,17 @@ { - "title": "Learn React", + "title": "Học React", "path": "/learn", "routes": [ { "hasSectionHeader": true, - "sectionHeader": "GET STARTED" + "sectionHeader": "BẮT ĐẦU" }, { - "title": "Quick Start", + "title": "Khởi Đầu Nhanh", "path": "/learn", "routes": [ { - "title": "Tutorial: Tic-Tac-Toe", + "title": "Hướng dẫn: Tic-Tac-Toe", "path": "/learn/tutorial-tic-tac-toe" }, { @@ -21,41 +21,41 @@ ] }, { - "title": "Installation", + "title": "Cài Đặt", "path": "/learn/installation", "routes": [ { - "title": "Creating a React App", + "title": "Tạo Một Ứng Dụng React", "path": "/learn/creating-a-react-app" }, { - "title": "Build a React App from Scratch", + "title": "Xây Dựng Một Ứng Dụng React Từ Đầu", "path": "/learn/build-a-react-app-from-scratch" }, { - "title": "Add React to an Existing Project", + "title": "Thêm React Vào Một Dự Án Đã Có", "path": "/learn/add-react-to-an-existing-project" } ] }, { - "title": "Setup", + "title": "Thiết Lập", "path": "/learn/setup", "routes": [ { - "title": "Editor Setup", + "title": "Thiết Lập Editor", "path": "/learn/editor-setup" }, { - "title": "Using TypeScript", + "title": "Sử Dụng TypeScript", "path": "/learn/typescript" }, { - "title": "React Developer Tools", + "title": "Công Cụ Phát Triển React", "path": "/learn/react-developer-tools" }, { - "title": "React Compiler", + "title": "Trình biên dịch React", "path": "/learn/react-compiler", "canary": true } @@ -63,156 +63,156 @@ }, { "hasSectionHeader": true, - "sectionHeader": "LEARN REACT" + "sectionHeader": "HỌC REACT" }, { - "title": "Describing the UI", + "title": "Mô Tả Giao Diện Người Dùng", "tags": [], "path": "/learn/describing-the-ui", "routes": [ { - "title": "Component đầu tiên của bạn", + "title": "Component Đầu Tiên Của Bạn", "path": "/learn/your-first-component" }, { - "title": "Importing and Exporting Components", + "title": "Import và Export Components", "path": "/learn/importing-and-exporting-components" }, { - "title": "Writing Markup with JSX", + "title": "Viết Mã Đánh Dấu Với JSX", "path": "/learn/writing-markup-with-jsx" }, { - "title": "JavaScript in JSX with Curly Braces", + "title": "JavaScript Trong JSX Với Dấu Ngoặc Nhọn", "path": "/learn/javascript-in-jsx-with-curly-braces" }, { - "title": "Passing Props to a Component", + "title": "Truyền Props Cho Một Component", "path": "/learn/passing-props-to-a-component" }, { - "title": "Conditional Rendering", + "title": "Kết Xuất Có Điều Kiện", "path": "/learn/conditional-rendering" }, { - "title": "Rendering Lists", + "title": "Kết Xuất Danh Sách", "path": "/learn/rendering-lists" }, { - "title": "Keeping Components Pure", + "title": "Giữ Các Component Thuần Khiết", "path": "/learn/keeping-components-pure" }, { - "title": "Your UI as a Tree", + "title": "Giao Diện Người Dùng Của Bạn Như Một Cây", "path": "/learn/understanding-your-ui-as-a-tree" } ] }, { - "title": "Adding Interactivity", + "title": "Thêm Tính Tương Tác", "path": "/learn/adding-interactivity", "tags": [], "routes": [ { - "title": "Responding to Events", + "title": "Phản Hồi Các Sự Kiện", "path": "/learn/responding-to-events" }, { - "title": "State: A Component's Memory", + "title": "State: Bộ Nhớ Của Một Component", "path": "/learn/state-a-components-memory" }, { - "title": "Render and Commit", + "title": "Kết Xuất và Commit", "path": "/learn/render-and-commit" }, { - "title": "State như một snapshot", + "title": "State Như Một Snapshot", "path": "/learn/state-as-a-snapshot" }, { - "title": "Queueing a Series of State Updates", + "title": "Xếp Hàng Một Loạt Các Cập Nhật State", "path": "/learn/queueing-a-series-of-state-updates" }, { - "title": "Cập nhật đối tượng trong state", + "title": "Cập Nhật Đối Tượng Trong State", "path": "/learn/updating-objects-in-state" }, { - "title": "Cập nhật mảng trong State", + "title": "Cập Nhật Mảng Trong State", "path": "/learn/updating-arrays-in-state" } ] }, { - "title": "Managing State", + "title": "Quản Lý State", "path": "/learn/managing-state", "tags": ["intermediate"], "routes": [ { - "title": "Reacting to Input with State", + "title": "Phản Ứng Với Input Bằng State", "path": "/learn/reacting-to-input-with-state" }, { - "title": "Choosing the State Structure", + "title": "Chọn Cấu Trúc State", "path": "/learn/choosing-the-state-structure" }, { - "title": "Sharing State Between Components", + "title": "Chia Sẻ State Giữa Các Component", "path": "/learn/sharing-state-between-components" }, { - "title": "Preserving and Resetting State", + "title": "Bảo Toàn và Reset State", "path": "/learn/preserving-and-resetting-state" }, { - "title": "Extracting State Logic into a Reducer", + "title": "Trích Xuất Logic State Vào Một Reducer", "path": "/learn/extracting-state-logic-into-a-reducer" }, { - "title": "Passing Data Deeply with Context", + "title": "Truyền Dữ Liệu Sâu Với Context", "path": "/learn/passing-data-deeply-with-context" }, { - "title": "Scaling Up with Reducer and Context", + "title": "Mở Rộng Với Reducer và Context", "path": "/learn/scaling-up-with-reducer-and-context" } ] }, { - "title": "Escape Hatches", + "title": "Các Lối Thoát Hiểm", "path": "/learn/escape-hatches", "tags": ["advanced"], "routes": [ { - "title": "Referencing Values with Refs", + "title": "Tham Chiếu Các Giá Trị Với Refs", "path": "/learn/referencing-values-with-refs" }, { - "title": "Manipulating the DOM with Refs", + "title": "Thao Tác DOM Với Refs", "path": "/learn/manipulating-the-dom-with-refs" }, { - "title": "Synchronizing with Effects", + "title": "Đồng Bộ Hóa Với Effects", "path": "/learn/synchronizing-with-effects" }, { - "title": "You Might Not Need an Effect", + "title": "Bạn Có Thể Không Cần Một Effect", "path": "/learn/you-might-not-need-an-effect" }, { - "title": "Lifecycle of Reactive Effects", + "title": "Vòng Đời Của Các Reactive Effects", "path": "/learn/lifecycle-of-reactive-effects" }, { - "title": "Separating Events from Effects", + "title": "Tách Các Sự Kiện Khỏi Effects", "path": "/learn/separating-events-from-effects" }, { - "title": "Removing Effect Dependencies", + "title": "Loại Bỏ Các Dependency Của Effect", "path": "/learn/removing-effect-dependencies" }, { - "title": "Reusing Logic with Custom Hooks", + "title": "Tái Sử Dụng Logic Với Custom Hooks", "path": "/learn/reusing-logic-with-custom-hooks" } ] diff --git a/src/sidebarReference.json b/src/sidebarReference.json index 851b544d0..7fcdb6378 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -1,5 +1,5 @@ { - "title": "API Reference", + "title": "Tài liệu tham khảo API", "path": "/reference/react", "routes": [ { @@ -7,7 +7,7 @@ "sectionHeader": "react@{{version}}" }, { - "title": "Overview", + "title": "Tổng quan", "path": "/reference/react" }, { @@ -172,7 +172,7 @@ "path": "/reference/react-dom/components", "routes": [ { - "title": "Common (e.g. <div>)", + "title": "Chung (ví dụ: <div>)", "path": "/reference/react-dom/components/common" }, { @@ -311,22 +311,22 @@ }, { "hasSectionHeader": true, - "sectionHeader": "Rules of React" + "sectionHeader": "Các quy tắc của React" }, { - "title": "Overview", + "title": "Tổng quan", "path": "/reference/rules", "routes": [ { - "title": "Components and Hooks must be pure", + "title": "Components và Hooks phải thuần khiết", "path": "/reference/rules/components-and-hooks-must-be-pure" }, { - "title": "React calls Components and Hooks", + "title": "React gọi Components và Hooks", "path": "/reference/rules/react-calls-components-and-hooks" }, { - "title": "Rules of Hooks", + "title": "Các quy tắc của Hooks", "path": "/reference/rules/rules-of-hooks" } ]