From d7534c7b963afc2b313e36172a6335328f61a254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E4=BC=9F=E5=8D=9A?= <129571565+qiyu22@users.noreply.github.com> Date: Sun, 4 Aug 2024 22:36:03 +0800 Subject: [PATCH 01/23] =?UTF-8?q?fix:=20=E5=B0=86=E2=80=9C=E5=AD=97?= =?UTF-8?q?=E6=AF=8D=E2=80=9D=E6=9B=BF=E6=8D=A2=E6=88=90=E2=80=9C=E4=BF=A1?= =?UTF-8?q?=E4=BB=B6=E2=80=9D=20(#1576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content/learn/choosing-the-state-structure.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/content/learn/choosing-the-state-structure.md b/src/content/learn/choosing-the-state-structure.md index cbdacf1d41..406476bc51 100644 --- a/src/content/learn/choosing-the-state-structure.md +++ b/src/content/learn/choosing-the-state-structure.md @@ -2284,9 +2284,9 @@ ul, li { margin: 0; padding: 0; } #### 修复消失的选项 {/*fix-the-disappearing-selection*/} -有一个 `letters` 列表在 state 中。当你悬停或聚焦到特定的字母时,它会被突出显示。当前突出显示的字母存储在 `highlightedLetter` state 变量中。您可以“Star”和“Unstar”单个字母,这将更新 state 中的 `letters` 数组。 +有一个 `letters` 列表在 state 中。当你悬停或聚焦到特定的信件时,它会被突出显示。当前突出显示的信件存储在 `highlightedLetter` state 变量中。您可以“Star”和“Unstar”单个信件,这将更新 state 中的 `letters` 数组。 -虽然这段代码可以运行,但是有一个小的 UI 问题。当你点击“Star”或“Unstar”时,高亮会短暂消失。不过只要你移动鼠标指针或者用键盘切换到另一个字母,它就会重新出现。为什么会这样?请修复它,使得在按钮点击后高亮不会消失。 +虽然这段代码可以运行,但是有一个小的 UI 问题。当你点击“Star”或“Unstar”时,高亮会短暂消失。不过只要你移动鼠标指针或者用键盘切换到另一个信件,它就会重新出现。为什么会这样?请修复它,使得在按钮点击后高亮不会消失。 @@ -2393,9 +2393,9 @@ li { border-radius: 5px; } -这个问题点在于你将字母对象存储在 `highlightedLetter` 中。但是,你也将相同的信息存储在 `letters` 数组中。因此,你的 state 存在重复!当你在按钮点击后更新 `letters` 数组时,会创建一个新的字母对象,它与 `highlightedLetter` 不同。这就是为什么 `highlightedLetter === letter` 执行变为 `false`,并且高亮消失的原因。当指针移动时下一次调用 `setHighlightedLetter` 时它会重新出现。 +这个问题点在于你将信件对象存储在 `highlightedLetter` 中。但是,你也将相同的信息存储在 `letters` 数组中。因此,你的 state 存在重复!当你在按钮点击后更新 `letters` 数组时,会创建一个新的信件对象,它与 `highlightedLetter` 不同。这就是为什么 `highlightedLetter === letter` 执行变为 `false`,并且高亮消失的原因。当指针移动时下一次调用 `setHighlightedLetter` 时它会重新出现。 -为了解决这个问题,请从 state 中删除重复项。不要在两个地方存储 **字母对象本身**,而是存储 `highlightedId`。然后,您可以使用 `letter.id === highlightedId` 检查每个带有 `isHighlighted` 属性的字母,即使 `letter` 对象在上次渲染后发生了变化,这也是可行的。 +为了解决这个问题,请从 state 中删除重复项。不要在两个地方存储 **信件对象本身**,而是存储 `highlightedId`。然后,您可以使用 `letter.id === highlightedId` 检查每个带有 `isHighlighted` 属性的信件,即使 `letter` 对象在上次渲染后发生了变化,这也是可行的。 @@ -2611,7 +2611,7 @@ label { width: 100%; padding: 5px; display: inline-block; } -在 state 中保留一个 `selectedIds` **数组**,而不是单个的 `selectedId`。例如,如果您选择了第一个和最后一个字母,则它将包含 `[0, 2]`。当没有选定任何内容时,它将为空数组 `[]`: +在 state 中保留一个 `selectedIds` **数组**,而不是单个的 `selectedId`。例如,如果您选择了第一个和最后一个信件,则它将包含 `[0, 2]`。当没有选定任何内容时,它将为空数组 `[]`: From 6671ba7b77142a73184caf863890d5ff20f2361b Mon Sep 17 00:00:00 2001 From: Oliver Joseph Ash Date: Wed, 7 Aug 2024 10:46:23 +0100 Subject: [PATCH 02/23] Document behaviour of setting state inside `useLayoutEffect` (#7096) Co-authored-by: Sebastian Silbermann --- src/content/reference/react/useLayoutEffect.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content/reference/react/useLayoutEffect.md b/src/content/reference/react/useLayoutEffect.md index 0d7b048412..d38458f146 100644 --- a/src/content/reference/react/useLayoutEffect.md +++ b/src/content/reference/react/useLayoutEffect.md @@ -67,6 +67,8 @@ function Tooltip() { * 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) +* If you trigger a state update inside `useLayoutEffect`, React will execute all remaining Effects immediately including `useEffect`. + --- ## Usage {/*usage*/} From d3491b8ed24c48e9558b84a08f0412f14639bca7 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 8 Aug 2024 20:04:08 +0800 Subject: [PATCH 03/23] docs: update description of useLayoutEffect behavior --- src/content/reference/react/useLayoutEffect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/useLayoutEffect.md b/src/content/reference/react/useLayoutEffect.md index cedce604f9..0888e3d506 100644 --- a/src/content/reference/react/useLayoutEffect.md +++ b/src/content/reference/react/useLayoutEffect.md @@ -67,7 +67,7 @@ function Tooltip() { * `useLayoutEffect` 内部的代码和所有计划的状态更新阻塞了浏览器重新绘制屏幕。如果过度使用,这会使你的应用程序变慢。如果可能的话,尽量选择 [`useEffect`](/reference/react/useEffect)。 -* If you trigger a state update inside `useLayoutEffect`, React will execute all remaining Effects immediately including `useEffect`. +* 如果你在 `useLayoutEffect` 内部触发状态更新,React 将立即执行所有剩余的 Effects,包括 `useEffect`。 --- From 2bfa7a628b0534bb0d437ff7520a72010ab970c3 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 8 Aug 2024 10:43:41 -0400 Subject: [PATCH 04/23] Update compiler docs installation instructions (#7095) --- src/content/learn/react-compiler.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/learn/react-compiler.md b/src/content/learn/react-compiler.md index f34c382ed9..2920e86434 100644 --- a/src/content/learn/react-compiler.md +++ b/src/content/learn/react-compiler.md @@ -121,7 +121,7 @@ In addition to these docs, we recommend checking the [React Compiler Working Gro Prior to installing the compiler, you can first check to see if your codebase is compatible: -npx react-compiler-healthcheck@latest +npx react-compiler-healthcheck@experimental This script will: @@ -143,7 +143,7 @@ Found no usage of incompatible libraries. 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. -npm install eslint-plugin-react-compiler +npm install eslint-plugin-react-compiler@experimental Then, add it to your eslint config: @@ -203,7 +203,7 @@ If you're starting a new project, you can enable the compiler on your entire cod ### Babel {/*usage-with-babel*/} -npm install babel-plugin-react-compiler +npm install babel-plugin-react-compiler@experimental The compiler includes a Babel plugin which you can use in your build pipeline to run the compiler. @@ -258,7 +258,7 @@ Next.js has an experimental configuration to enable the React Compiler. It autom - Install `babel-plugin-react-compiler` -npm install next@canary babel-plugin-react-compiler +npm install next@canary babel-plugin-react-compiler@experimental Then configure the experimental option in `next.config.js`: From a220bb3f21170210fb37375f349c943150a5364d Mon Sep 17 00:00:00 2001 From: k8o <61353435+k35o@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:46:36 +0900 Subject: [PATCH 05/23] docs: replace check mark emoji to check mark button emoji (#7121) --- .../blog/2023/03/16/introducing-react-dev.md | 4 +-- src/content/learn/conditional-rendering.md | 32 +++++++++---------- src/content/learn/describing-the-ui.md | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/content/blog/2023/03/16/introducing-react-dev.md b/src/content/blog/2023/03/16/introducing-react-dev.md index 2498f40dce..c4da2b61f0 100644 --- a/src/content/blog/2023/03/16/introducing-react-dev.md +++ b/src/content/blog/2023/03/16/introducing-react-dev.md @@ -269,7 +269,7 @@ Use the conditional operator (`cond ? a : b`) to render a ❌ if `isPacked` isn function Item({ name, isPacked }) { return (
  • - {name} {isPacked && '✔'} + {name} {isPacked && '✅'}
  • ); } @@ -307,7 +307,7 @@ export default function PackingList() { function Item({ name, isPacked }) { return (
  • - {name} {isPacked ? '✔' : '❌'} + {name} {isPacked ? '✅' : '❌'}
  • ); } diff --git a/src/content/learn/conditional-rendering.md b/src/content/learn/conditional-rendering.md index 895d610d33..95be5d2e01 100644 --- a/src/content/learn/conditional-rendering.md +++ b/src/content/learn/conditional-rendering.md @@ -52,13 +52,13 @@ export default function PackingList() {
    -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}`. +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}`. You can write this as an [`if`/`else` statement](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) like so: ```js if (isPacked) { - return
  • {name} ✔
  • ; + return
  • {name} ✅
  • ; } return
  • {name}
  • ; ``` @@ -70,7 +70,7 @@ If the `isPacked` prop is `true`, this code **returns a different JSX tree.** Wi ```js function Item({ name, isPacked }) { if (isPacked) { - return
  • {name} ✔
  • ; + return
  • {name} ✅
  • ; } return
  • {name}
  • ; } @@ -159,7 +159,7 @@ In practice, returning `null` from a component isn't common because it might sur 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: ```js -
  • {name} ✔
  • +
  • {name} ✅
  • ``` is very similar to @@ -172,7 +172,7 @@ Both of the conditional branches return `
  • ...
  • `: ```js if (isPacked) { - return
  • {name} ✔
  • ; + return
  • {name} ✅
  • ; } return
  • {name}
  • ; ``` @@ -187,7 +187,7 @@ Instead of this: ```js if (isPacked) { - return
  • {name} ✔
  • ; + return
  • {name} ✅
  • ; } return
  • {name}
  • ; ``` @@ -197,12 +197,12 @@ You can write this: ```js return (
  • - {isPacked ? name + ' ✔' : name} + {isPacked ? name + ' ✅' : name}
  • ); ``` -You can read it as *"if `isPacked` is true, then (`?`) render `name + ' ✔'`, otherwise (`:`) render `name`"*. +You can read it as *"if `isPacked` is true, then (`?`) render `name + ' ✅'`, otherwise (`:`) render `name`"*. @@ -222,7 +222,7 @@ function Item({ name, isPacked }) {
  • {isPacked ? ( - {name + ' ✔'} + {name + ' ✅'} ) : ( name @@ -265,7 +265,7 @@ Another common shortcut you'll encounter is the [JavaScript logical AND (`&&`) o ```js return (
  • - {name} {isPacked && '✔'} + {name} {isPacked && '✅'}
  • ); ``` @@ -280,7 +280,7 @@ Here it is in action: function Item({ name, isPacked }) { return (
  • - {name} {isPacked && '✔'} + {name} {isPacked && '✅'}
  • ); } @@ -337,7 +337,7 @@ Use an `if` statement to reassign a JSX expression to `itemContent` if `isPacked ```js if (isPacked) { - itemContent = name + " ✔"; + itemContent = name + " ✅"; } ``` @@ -357,7 +357,7 @@ This style is the most verbose, but it's also the most flexible. Here it is in a function Item({ name, isPacked }) { let itemContent = name; if (isPacked) { - itemContent = name + " ✔"; + itemContent = name + " ✅"; } return (
  • @@ -401,7 +401,7 @@ function Item({ name, isPacked }) { if (isPacked) { itemContent = ( - {name + " ✔"} + {name + " ✅"} ); } @@ -464,7 +464,7 @@ Use the conditional operator (`cond ? a : b`) to render a ❌ if `isPacked` isn function Item({ name, isPacked }) { return (
  • - {name} {isPacked && '✔'} + {name} {isPacked && '✅'}
  • ); } @@ -502,7 +502,7 @@ export default function PackingList() { function Item({ name, isPacked }) { return (
  • - {name} {isPacked ? '✔' : '❌'} + {name} {isPacked ? '✅' : '❌'}
  • ); } diff --git a/src/content/learn/describing-the-ui.md b/src/content/learn/describing-the-ui.md index ce49b85c8f..34ee0c01a1 100644 --- a/src/content/learn/describing-the-ui.md +++ b/src/content/learn/describing-the-ui.md @@ -327,7 +327,7 @@ In this example, the JavaScript `&&` operator is used to conditionally render a function Item({ name, isPacked }) { return (
  • - {name} {isPacked && '✔'} + {name} {isPacked && '✅'}
  • ); } From b5f28b48441e7e46c11ba066110a3c952c33c4ba Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:21:11 +0200 Subject: [PATCH 06/23] Redirect lists-and-keys to rendering-lists describing key (#7120) --- vercel.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vercel.json b/vercel.json index eac0efb9c4..8b0546e372 100644 --- a/vercel.json +++ b/vercel.json @@ -24,6 +24,11 @@ "destination": "/learn/rendering-lists#keeping-list-items-in-order-with-key", "permanent": false }, + { + "source": "/docs/lists-and-keys", + "destination": "/learn/rendering-lists#keeping-list-items-in-order-with-key", + "permanent": false + }, { "source": "/link/invalid-hook-call", "destination": "/warnings/invalid-hook-call-warning", From 7d50c3ffd4df2dc7903f4e41069653a456a9c223 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:32:42 +0200 Subject: [PATCH 07/23] Emphasize the second problem paragraph with chain of effects example (#7108) * Emphasize the second problem acapit with chain of effects example * Replace 'One' with 'First' to keep problems counting consistent --- src/content/learn/you-might-not-need-an-effect.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/learn/you-might-not-need-an-effect.md b/src/content/learn/you-might-not-need-an-effect.md index 66cdc31177..27031720d5 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -408,9 +408,9 @@ function Game() { There are two problems with this code. -One 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. +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. -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. +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. In this case, it's better to calculate what you can during rendering, and adjust the state in the event handler: From dea3597b5483dfab7f477fbb418144c673fa142e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=85=E7=A7=8B?= Date: Mon, 26 Aug 2024 11:35:05 +0800 Subject: [PATCH 08/23] fix conflicts --- src/content/learn/conditional-rendering.md | 12 ++---------- src/content/learn/you-might-not-need-an-effect.md | 10 ++-------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/content/learn/conditional-rendering.md b/src/content/learn/conditional-rendering.md index d368800ea6..9c41604bc6 100644 --- a/src/content/learn/conditional-rendering.md +++ b/src/content/learn/conditional-rendering.md @@ -54,11 +54,7 @@ export default function PackingList() {
    -<<<<<<< HEAD -需要注意的是,有些 `Item` 组件的 `isPacked` 属性是被设为 `true` 而不是 `false`。你可以在那些满足 `isPacked={true}` 条件的物品旁加上一个勾选符号(✔)。 -======= -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}`. ->>>>>>> 7d50c3ffd4df2dc7903f4e41069653a456a9c223 +需要注意的是,有些 `Item` 组件的 `isPacked` 属性是被设为 `true` 而不是 `false`。你可以在那些满足 `isPacked={true}` 条件的物品旁加上一个勾选符号(✅)。 你可以用 [if/else 语句](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/if...else) 去判断: @@ -208,11 +204,7 @@ return ( ); ``` -<<<<<<< HEAD -你可以认为,*“如果 `isPacked` 为 true 时,则(`?`)渲染 `name + ' ✔'`,否则(`:`)渲染 `name`。”* -======= -You can read it as *"if `isPacked` is true, then (`?`) render `name + ' ✅'`, otherwise (`:`) render `name`"*. ->>>>>>> 7d50c3ffd4df2dc7903f4e41069653a456a9c223 +你可以认为,*“如果 `isPacked` 为 true 时,则(`?`)渲染 `name + ' ✅'`,否则(`:`)渲染 `name`。”* 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 ef7acda80a..af2e16f2d1 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -408,15 +408,9 @@ function Game() { 这段代码里有两个问题。 -<<<<<<< HEAD -一个问题是它非常低效:在链式的每个 `set` 调用之间,组件(及其子组件)都不得不重新渲染。在上面的例子中,在最坏的情况下(`setCard` → 渲染 → `setGoldCardCount` → 渲染 → `setRound` → 渲染 → `setIsGameOver` → 渲染)有三次不必要的重新渲染。 +第一个问题是它非常低效:在链式的每个 `set` 调用之间,组件(及其子组件)都不得不重新渲染。在上面的例子中,在最坏的情况下(`setCard` → 渲染 → `setGoldCardCount` → 渲染 → `setRound` → 渲染 → `setIsGameOver` → 渲染)有三次不必要的重新渲染。 -即使不考虑渲染效率问题,随着代码不断扩展,你会遇到这条 “链式” 调用不符合新需求的情况。试想一下,你现在需要添加一种方法来回溯游戏的历史记录,可以通过更新每个 state 变量到之前的值来实现。然而,将 `card` 设置为之前的的某个值会再次触发 Effect 链并更改你正在显示的数据。这样的代码往往是僵硬而脆弱的。 -======= -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. - -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. ->>>>>>> 7d50c3ffd4df2dc7903f4e41069653a456a9c223 +第二个问题是,即使不考虑渲染效率问题,随着代码不断扩展,你会遇到这条 “链式” 调用不符合新需求的情况。试想一下,你现在需要添加一种方法来回溯游戏的历史记录,可以通过更新每个 state 变量到之前的值来实现。然而,将 `card` 设置为之前的的某个值会再次触发 Effect 链并更改你正在显示的数据。这样的代码往往是僵硬而脆弱的。 在这个例子中,更好的做法是:尽可能在渲染期间进行计算,以及在事件处理函数中调整 state: From 50004fa267465da70fafdbaa7914ebf9fe00ae83 Mon Sep 17 00:00:00 2001 From: Denis Radin Date: Mon, 26 Aug 2024 19:00:18 +0300 Subject: [PATCH 09/23] Adds React Advanced London 2024 (#7127) PR to add Radv 2024 conference --- src/content/community/conferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index 5070fbc411..3e2d760603 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -55,6 +55,11 @@ October 18, 2024. In-person in Brussels, Belgium (hybrid event) [Website](https://www.react.brussels/) - [Twitter](https://x.com/BrusselsReact) +### React Advanced London 2024 {/*react-advanced-london-2024*/} +October 25 & 28, 2024. In-person in London, UK + online (hybrid event) + +[Website](https://reactadvanced.com/) - [Twitter](https://x.com/reactadvanced) + ### React Africa 2024 {/*react-africa-2024*/} November 29, 2024. In-person in Casablanca, Morocco (hybrid event) From 24c2a611594d88598800e787c7240e289fa795eb Mon Sep 17 00:00:00 2001 From: Elvis Liao <1219585136@qq.com> Date: Wed, 28 Aug 2024 09:47:41 +0800 Subject: [PATCH 10/23] docs(cn): fixed some grammar problems (#1581) --- ...react-labs-what-we-have-been-working-on-february-2024.md | 2 +- src/content/learn/choosing-the-state-structure.md | 4 ++-- src/content/learn/extracting-state-logic-into-a-reducer.md | 6 +++--- src/content/learn/importing-and-exporting-components.md | 2 +- src/content/learn/lifecycle-of-reactive-effects.md | 2 +- src/content/learn/manipulating-the-dom-with-refs.md | 2 +- src/content/learn/preserving-and-resetting-state.md | 2 +- src/content/learn/queueing-a-series-of-state-updates.md | 2 +- src/content/learn/referencing-values-with-refs.md | 4 ++-- src/content/learn/synchronizing-with-effects.md | 4 ++-- src/content/learn/updating-objects-in-state.md | 2 +- src/content/learn/you-might-not-need-an-effect.md | 4 ++-- src/content/reference/react-dom/findDOMNode.md | 2 +- src/content/reference/react/useEffect.md | 4 ++-- src/content/reference/react/useLayoutEffect.md | 2 +- src/content/reference/react/useState.md | 6 +++--- 16 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md b/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md index 79c1edaa8d..4e80e3e864 100644 --- a/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md +++ b/src/content/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024.md @@ -31,7 +31,7 @@ React 编译器不再是一个研究项目:该编译器现在已经在生产 正如我们在 [之前的文章](/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023#react-optimizing-compiler) 中所讨论的,当状态发生变化时,React 有时会过度重新渲染。自 React 早期以来,我们对这种情况的解决方案一直是手动记忆化。在我们当前的 API 中,这意味着使用 [`useMemo`](/reference/react/useMemo)、[`useCallback`](/reference/react/useCallback) 和 [`memo`](/reference/react/memo) API 手动调整 React 在状态变化时重新渲染的程度。但是手动记忆化是一种妥协。它会使我们的代码变得混乱、很容易出错,并且需要额外的工作来保持更新。 -手动记忆化是一个合理的妥协,但我们并不满意。我们的愿景是当状态发生变化时 React 可以自动重新渲染 UI 的恰当部分,而不是向 React 的核心心智模型妥协。我们相信 React 的方式——将 UI 视为状态的简单函数,使用标准的 JavaScript 值和习惯用法——是 React 为许多开发人员提供可接近性的关键部分。这就是为什么我们投资于构建 React 的优化编译器的原因。 +手动记忆化是一个合理的妥协,但我们并不满意。我们的愿景是当状态发生变化时 React 可以自动重新渲染 UI 的恰当部分,而不是向 React 的核心心智模型妥协。我们相信 React 的方式——将 UI 视为状态的简单函数,使用标准的 JavaScript 值和习惯用法——是 React 为许多开发人员提供可接近性的关键部分。这就是我们投资于构建 React 的优化编译器的原因。 JavaScript 是一个因其松散规则和动态特性而闻名的具有挑战性的语言。React 编译器能够通过模拟 JavaScript 的规则和“React 的规则”来安全地编译代码。例如,React 组件必须是幂等的——给定相同的输入返回相同的值——并且不能突变 props 或状态值。这些规则限制了开发人员可以做的事情,并为编译器优化开辟了一个安全的空间。 diff --git a/src/content/learn/choosing-the-state-structure.md b/src/content/learn/choosing-the-state-structure.md index 406476bc51..34eb48a2fc 100644 --- a/src/content/learn/choosing-the-state-structure.md +++ b/src/content/learn/choosing-the-state-structure.md @@ -2393,7 +2393,7 @@ li { border-radius: 5px; } -这个问题点在于你将信件对象存储在 `highlightedLetter` 中。但是,你也将相同的信息存储在 `letters` 数组中。因此,你的 state 存在重复!当你在按钮点击后更新 `letters` 数组时,会创建一个新的信件对象,它与 `highlightedLetter` 不同。这就是为什么 `highlightedLetter === letter` 执行变为 `false`,并且高亮消失的原因。当指针移动时下一次调用 `setHighlightedLetter` 时它会重新出现。 +这个问题点在于你将信件对象存储在 `highlightedLetter` 中。但是,你也将相同的信息存储在 `letters` 数组中。因此,你的 state 存在重复!当你在按钮点击后更新 `letters` 数组时,会创建一个新的信件对象,它与 `highlightedLetter` 不同。这就是 `highlightedLetter === letter` 执行变为 `false`,并且高亮消失的原因。当指针移动时下一次调用 `setHighlightedLetter` 时它会重新出现。 为了解决这个问题,请从 state 中删除重复项。不要在两个地方存储 **信件对象本身**,而是存储 `highlightedId`。然后,您可以使用 `letter.id === highlightedId` 检查每个带有 `isHighlighted` 属性的信件,即使 `letter` 对象在上次渲染后发生了变化,这也是可行的。 @@ -2824,7 +2824,7 @@ label { width: 100%; padding: 5px; display: inline-block; } 现在每个项目都会进行 `selectedIds.has(letter.id)` 检查,这非常快。 -请记住,你[不应该在 state 中改变对象](/learn/updating-objects-in-state),包括 Set 中。这就是为什么 `handleToggle` 函数首先创建 Set 的 **副本**,然后更新该副本的原因。 +请记住,你[不应该在 state 中改变对象](/learn/updating-objects-in-state),包括 Set 中。这就是 `handleToggle` 函数首先创建 Set 的 **副本**,然后更新该副本的原因。 diff --git a/src/content/learn/extracting-state-logic-into-a-reducer.md b/src/content/learn/extracting-state-logic-into-a-reducer.md index 5a121d02f2..c16d9af9e8 100644 --- a/src/content/learn/extracting-state-logic-into-a-reducer.md +++ b/src/content/learn/extracting-state-logic-into-a-reducer.md @@ -1086,7 +1086,7 @@ li {
    -Reducers 应该是纯净的,所以它们不应该去修改 state。而 Immer 为你提供了一种特殊的 `draft` 对象,你可以通过它安全的修改 state。在底层,Immer 会基于当前 state 创建一个副本。这就是为什么通过 `useImmerReducer` 来管理 reducers 时,可以修改第一个参数,且不需要返回一个新的 state 的原因。 +Reducers 应该是纯净的,所以它们不应该去修改 state。而 Immer 为你提供了一种特殊的 `draft` 对象,你可以通过它安全的修改 state。在底层,Immer 会基于当前 state 创建一个副本。这就是通过 `useImmerReducer` 来管理 reducers 时,可以修改第一个参数,且不需要返回一个新的 state 的原因。 @@ -1105,7 +1105,7 @@ Reducers 应该是纯净的,所以它们不应该去修改 state。而 Immer #### 通过事件处理函数 dispatch actions {/*dispatch-actions-from-event-handlers*/} -目前,`ContactList.js` 和 `Chat.js` 中的事件处理程序包含 `// TODO` 注释。这就是为什么输入不起作用,点击按钮也不会改变收件人的原因。 +目前,`ContactList.js` 和 `Chat.js` 中的事件处理程序包含 `// TODO` 注释。这就是输入不起作用,点击按钮也不会改变收件人的原因。 将这两个 `// TODO` 替换为 `dispatch` 相应的 action。如果要查看 action 的结构和类型,请查看 `messengerReducer.js` 中的 reducer。reducer 已经写好了,你不需要再修改它。你只需要在 `ContactList.js` 和 `Chat.js` 中 dispatch 相应的 action 即可。 @@ -1860,7 +1860,7 @@ textarea { 结果虽然是一样的。但请记住,action 的类型应该准确描述 “用户做了什么”,而不是 “你希望状态如何改变”。这使得以后添加更多特性变的容易。 -不管是哪一种解决方案,最重要的是你 **不要** 把 `alert` 放置在 reducer 中。reducer 必须是一个纯函数——它应该只计算下一个状态。而不应该 “做” 其它事情,包括向用户显示消息。这应该在事件处理程序中处理。(为了便于捕获这样的错误,React 会在严格模式下多次调用你的 reducer。这就是为什么当你在 reducer 中加入一个 alert,它会触发两次的原因。) +不管是哪一种解决方案,最重要的是你 **不要** 把 `alert` 放置在 reducer 中。reducer 必须是一个纯函数——它应该只计算下一个状态。而不应该 “做” 其它事情,包括向用户显示消息。这应该在事件处理程序中处理。(为了便于捕获这样的错误,React 会在严格模式下多次调用你的 reducer。这就是当你在 reducer 中加入一个 alert,它会触发两次的原因。) diff --git a/src/content/learn/importing-and-exporting-components.md b/src/content/learn/importing-and-exporting-components.md index 2b1bfde77e..dc7630ceec 100644 --- a/src/content/learn/importing-and-exporting-components.md +++ b/src/content/learn/importing-and-exporting-components.md @@ -144,7 +144,7 @@ import Gallery from './Gallery'; | 默认 | `export default function Button() {}` | `import Button from './Button.js';` | | 具名 | `export function Button() {}` | `import { Button } from './Button.js';` | -当使用默认导入时,你可以在 `import` 语句后面进行任意命名。比如 `import Banana from './Button.js'`,如此你能获得与默认导出一致的内容。相反,对于具名导入,导入和导出的名字必须一致。这也是为什么称其为 **具名** 导入的原因! +当使用默认导入时,你可以在 `import` 语句后面进行任意命名。比如 `import Banana from './Button.js'`,如此你能获得与默认导出一致的内容。相反,对于具名导入,导入和导出的名字必须一致。这也是称其为 **具名** 导入的原因! **通常,文件中仅包含一个组件时,人们会选择默认导出,而当文件中包含多个组件或某个值需要导出时,则会选择具名导出。** 无论选择哪种方式,请记得给你的组件和相应的文件命名一个有意义的名字。我们不建议创建未命名的组件,比如 `export default () => {}`,因为这样会使得调试变得异常困难。 diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index e1f01a2473..9210f5d551 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -860,7 +860,7 @@ button { margin-left: 10px; } -这个 Effect 实际上没有任何依赖数组,所以它在每次重新渲染后都会重新同步。首先,添加依赖数组。然后,确保每个被 Effect 使用的响应式值都在数组中指定。例如,`roomId` 是响应式的(因为它是 `prop`),所以它应该包含在数组中。这样可以确保当用户选择不同的聊天室时,聊天会重新连接。另一方面,`serverUrl` 是在组件外部定义的,这就是为什么它不需要在数组中的原因。 +这个 Effect 实际上没有任何依赖数组,所以它在每次重新渲染后都会重新同步。首先,添加依赖数组。然后,确保每个被 Effect 使用的响应式值都在数组中指定。例如,`roomId` 是响应式的(因为它是 `prop`),所以它应该包含在数组中。这样可以确保当用户选择不同的聊天室时,聊天会重新连接。另一方面,`serverUrl` 是在组件外部定义的,这就是它不需要在数组中的原因。 diff --git a/src/content/learn/manipulating-the-dom-with-refs.md b/src/content/learn/manipulating-the-dom-with-refs.md index 5f8b60f5f4..c546ae063c 100644 --- a/src/content/learn/manipulating-the-dom-with-refs.md +++ b/src/content/learn/manipulating-the-dom-with-refs.md @@ -587,7 +587,7 @@ setTodos([ ...todos, newTodo]); listRef.current.lastChild.scrollIntoView(); ``` -在 React 中,[state 更新是排队进行的](/learn/queueing-a-series-of-state-updates)。通常,这就是你想要的。但是,在这个示例中会导致问题,因为 `setTodos` 不会立即更新 DOM。因此,当你将列表滚动到最后一个元素时,尚未添加待办事项。这就是为什么滚动总是“落后”一项的原因。 +在 React 中,[state 更新是排队进行的](/learn/queueing-a-series-of-state-updates)。通常,这就是你想要的。但是,在这个示例中会导致问题,因为 `setTodos` 不会立即更新 DOM。因此,当你将列表滚动到最后一个元素时,尚未添加待办事项。这就是滚动总是“落后”一项的原因。 要解决此问题,你可以强制 React 同步更新(“刷新”)DOM。 为此,从 `react-dom` 导入 `flushSync` 并**将 state 更新包裹** 到 `flushSync` 调用中: diff --git a/src/content/learn/preserving-and-resetting-state.md b/src/content/learn/preserving-and-resetting-state.md index fd584408e7..5141d21940 100644 --- a/src/content/learn/preserving-and-resetting-state.md +++ b/src/content/learn/preserving-and-resetting-state.md @@ -702,7 +702,7 @@ label { -以下是为什么你不应该把组件函数的定义嵌套起来的原因。 +以下是你不应该把组件函数的定义嵌套起来的原因。 示例中, `MyTextField` 组件被定义在 `MyComponent` **内部**: diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index 355eef864e..00fa8fcadd 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -58,7 +58,7 @@ setNumber(0 + 1); setNumber(0 + 1); ``` -但是这里还有另外一个影响因素需要讨论。**React 会等到事件处理函数中的** 所有 **代码都运行完毕再处理你的 state 更新。** 这就是为什么重新渲染只会发生在所有这些 `setNumber()` 调用 **之后** 的原因。 +但是这里还有另外一个影响因素需要讨论。**React 会等到事件处理函数中的** 所有 **代码都运行完毕再处理你的 state 更新。** 这就是重新渲染只会发生在所有这些 `setNumber()` 调用 **之后** 的原因。 这可能会让你想起餐厅里帮你点菜的服务员。服务员不会在你说第一道菜的时候就跑到厨房!相反,他们会让你把菜点完,让你修改菜品,甚至会帮桌上的其他人点菜。 diff --git a/src/content/learn/referencing-values-with-refs.md b/src/content/learn/referencing-values-with-refs.md index 5d9e33e166..f0334bad12 100644 --- a/src/content/learn/referencing-values-with-refs.md +++ b/src/content/learn/referencing-values-with-refs.md @@ -235,7 +235,7 @@ export default function Counter() { -这就是为什么在渲染期间读取 `ref.current` 会导致代码不可靠的原因。如果需要,请改用 state。 +这就是在渲染期间读取 `ref.current` 会导致代码不可靠的原因。如果需要,请改用 state。 @@ -528,7 +528,7 @@ button { display: block; margin: 10px; } -像 `timeoutID` 这样的变量是被所有组件共享的。这就是为什么单击第二个按钮会重置第一个按钮未完成的 timeout 的原因。要解决此问题,你可以把 timeout 保存在 ref 中。每个按钮都有自己的 ref,因此它们不会相互冲突。请注意快速单击两个按钮如何显示两个消息。 +像 `timeoutID` 这样的变量是被所有组件共享的。这就是单击第二个按钮会重置第一个按钮未完成的 timeout 的原因。要解决此问题,你可以把 timeout 保存在 ref 中。每个按钮都有自己的 ref,因此它们不会相互冲突。请注意快速单击两个按钮如何显示两个消息。 diff --git a/src/content/learn/synchronizing-with-effects.md b/src/content/learn/synchronizing-with-effects.md index 8a78e6fe75..cf9611bd27 100644 --- a/src/content/learn/synchronizing-with-effects.md +++ b/src/content/learn/synchronizing-with-effects.md @@ -854,7 +854,7 @@ export default function App() { -在最开始时可以看到三个日志输出:`安排 "a" 日志`,`取消 "a" 日志`,还有一个 `安排 "a" 日志`。三秒后,还会有一条日志显示:`a`。正如之前所说,额外的安排/取消动作产生的原因是因为 React 在开发环境中,会重新挂载组件一次,以验证你是否正确地实现了清理函数。 +在最开始时可以看到三个日志输出:`安排 "a" 日志`,`取消 "a" 日志`,还有一个 `安排 "a" 日志`。三秒后,还会有一条日志显示:`a`。正如之前所说,额外的安排/取消动作产生的原因是:React 在开发环境中,会重新挂载组件一次,以验证你是否正确地实现了清理函数。 现在编辑输入框,输入 `abc`。如果输入速度足够快,你会看到 `安排 "ab" 日志`,紧接着的是 `取消 "ab" 日志` 和 `安排 "abc" 日志`。**React 总是在执行下一轮渲染的 Effect 之前清理上一轮渲染的 Effect**。这就是为什么即使你快速输入,最多也只有一个安排操作。试试多次编辑输入框,并观察控制台以了解 Effect 是如何被清理的。 @@ -1400,7 +1400,7 @@ body { -在 [严格模式](/reference/react/StrictMode) 下,(本网站中的示例沙盒(sandbox)都已开启严格模式),React 在开发模式中,每个组件都会重复挂载一次。这也就导致计数器组件被挂载了两次。所以,计时器也被设立了两次,这就是为什么计数器每秒递增两次的原因。 +在 [严格模式](/reference/react/StrictMode) 下,(本网站中的示例沙盒(sandbox)都已开启严格模式),React 在开发模式中,每个组件都会重复挂载一次。这也就导致计数器组件被挂载了两次。所以,计时器也被设立了两次,这就是计数器每秒递增两次的原因。 然而,这并不是 React 本身的错:而是 Effect 代码中本身就存在问题。React 只不过把这个问题放大了。真正的错误原因是这样的 Effect 启动后,但没有提供清理函数,所以上一次的 Effect 残留就没有被除去。 diff --git a/src/content/learn/updating-objects-in-state.md b/src/content/learn/updating-objects-in-state.md index 7a4bdac24b..5d2e3be616 100644 --- a/src/content/learn/updating-objects-in-state.md +++ b/src/content/learn/updating-objects-in-state.md @@ -804,7 +804,7 @@ img { width: 200px; height: 200px; } * **优化**:React 常见的 [优化策略](/reference/react/memo) 依赖于如果之前的 props 或者 state 的值和下一次相同就跳过渲染。如果你从未直接修改 state ,那么你就可以很快看到 state 是否发生了变化。如果 `prevObj === obj`,那么你就可以肯定这个对象内部并没有发生改变。 * **新功能**:我们正在构建的 React 的新功能依赖于 state 被 [像快照一样看待](/learn/state-as-a-snapshot) 的理念。如果你直接修改 state 的历史版本,可能会影响你使用这些新功能。 * **需求变更**:有些应用功能在不出现任何修改的情况下会更容易实现,比如实现撤销/恢复、展示修改历史,或是允许用户把表单重置成某个之前的值。这是因为你可以把 state 之前的拷贝保存到内存中,并适时对其进行再次使用。如果一开始就用了直接修改 state 的方式,那么后面要实现这样的功能就会变得非常困难。 -* **更简单的实现**:React 并不依赖于 mutation ,所以你不需要对对象进行任何特殊操作。它不需要像很多“响应式”的解决方案一样去劫持对象的属性、总是用代理把对象包裹起来,或者在初始化时做其他工作。这也是为什么 React 允许你把任何对象存放在 state 中——不管对象有多大——而不会造成有任何额外的性能或正确性问题的原因。 +* **更简单的实现**:React 并不依赖于 mutation ,所以你不需要对对象进行任何特殊操作。它不需要像很多“响应式”的解决方案一样去劫持对象的属性、总是用代理把对象包裹起来,或者在初始化时做其他工作。这也是 React 允许你把任何对象存放在 state 中——不管对象有多大——而不会造成有任何额外的性能或正确性问题的原因。 在实践中,你经常可以“侥幸”直接修改 state 而不出现什么问题,但是我们强烈建议你不要这样做,这样你就可以使用我们秉承着这种理念开发的 React 新功能。未来的贡献者甚至是你未来的自己都会感谢你的! 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 af2e16f2d1..f27f42513a 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -718,7 +718,7 @@ function SearchResults({ query }) { 这可能看起来与之前需要将逻辑放入事件处理函数中的示例相矛盾!但是,考虑到这并不是 **键入事件**,这是在这里获取数据的主要原因。搜索输入框的值经常从 URL 中预填充,用户可以在不关心输入框的情况下导航到后退和前进页面。 -`page` 和 `query` 的来源其实并不重要。只要该组件可见,你就需要通过当前 `page` 和 `query` 的值,保持 `results` 和网络数据的 [同步](/learn/synchronizing-with-effects)。这就是为什么这里是一个 Effect 的原因。 +`page` 和 `query` 的来源其实并不重要。只要该组件可见,你就需要通过当前 `page` 和 `query` 的值,保持 `results` 和网络数据的 [同步](/learn/synchronizing-with-effects)。这就是这里是一个 Effect 的原因。 然而,上面的代码有一个问题。假设你快速地输入 `“hello”`。那么 `query` 会从 `“h”` 变成 `“he”`,`“hel”`,`“hell”` 最后是 `“hello”`。这会触发一连串不同的数据获取请求,但无法保证对应的返回顺序。例如,`“hell”` 的响应可能在 `“hello”` 的响应 **之后** 返回。由于它的 `setResults()` 是在最后被调用的,你将会显示错误的搜索结果。这种情况被称为 “[竞态条件](https://en.wikipedia.org/wiki/Race_condition)”:两个不同的请求 “相互竞争”,并以与你预期不符的顺序返回。 @@ -751,7 +751,7 @@ function SearchResults({ query }) { 处理竞态条件并不是实现数据获取的唯一难点。你可能还需要考虑缓存响应结果(使用户点击后退按钮时可以立即看到先前的屏幕内容),如何在服务端获取数据(使服务端初始渲染的 HTML 中包含获取到的内容而不是加载动画),以及如何避免网络瀑布(使子组件不必等待每个父组件的数据获取完毕后才开始获取数据)。 -**这些问题适用于任何 UI 库,而不仅仅是 React。解决这些问题并不容易,这也是为什么现代 [框架](/learn/start-a-new-react-project#production-grade-react-frameworks) 提供了比在 Effect 中获取数据更有效的内置数据获取机制的原因。** +**这些问题适用于任何 UI 库,而不仅仅是 React。解决这些问题并不容易,这也是现代 [框架](/learn/start-a-new-react-project#production-grade-react-frameworks) 提供了比在 Effect 中获取数据更有效的内置数据获取机制的原因。** 如果你不使用框架(也不想开发自己的框架),但希望使从 Effect 中获取数据更符合人类直觉,请考虑像这个例子一样,将获取逻辑提取到一个自定义 Hook 中: diff --git a/src/content/reference/react-dom/findDOMNode.md b/src/content/reference/react-dom/findDOMNode.md index db5825b1f6..dfc575892d 100644 --- a/src/content/reference/react-dom/findDOMNode.md +++ b/src/content/reference/react-dom/findDOMNode.md @@ -424,7 +424,7 @@ export default MyInput; 有时,一个组件想要知道子元素的位置和大小。这会让你想要使用 `findDOMNode(this)` 查找子元素,然后使用 [`getBoundingClientRect`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect) 等 DOM 方法来进行测量。 -目前,还没有直接适用于此场景的替代方法,这就是为什么 `findDOMNode` 已弃用但尚未从 React 中完全删除的原因。在此期间,你可以尝试在内容周围使用 `
    ` 包装,并向其添加 ref。但是,额外的包装可能会破坏样式。 +目前,还没有直接适用于此场景的替代方法,这就是 `findDOMNode` 已弃用但尚未从 React 中完全删除的原因。在此期间,你可以尝试在内容周围使用 `
    ` 包装,并向其添加 ref。但是,额外的包装可能会破坏样式。 ```js
    diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index 7de29665f7..c0c5913337 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -1733,7 +1733,7 @@ function Page({ url, shoppingCart }) { ### 在服务器和客户端上显示不同的内容 {/*displaying-different-content-on-the-server-and-the-client*/} -如果你的应用程序使用服务端([直接](/reference/react-dom/server) 或通过 [框架](/learn/start-a-new-react-project#production-grade-react-frameworks))渲染,你的组件将会在两个不同的环境中渲染。在服务器上,它将渲染生成初始 HTML。在客户端,React 将再次运行渲染代码,以便将事件处理附加到该 HTML 上。这就是为什么要让 [hydration](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) 发挥作用,你的初始渲染输出必须在客户端和服务器上完全相同的原因。 +如果你的应用程序使用服务端([直接](/reference/react-dom/server) 或通过 [框架](/learn/start-a-new-react-project#production-grade-react-frameworks))渲染,你的组件将会在两个不同的环境中渲染。在服务器上,它将渲染生成初始 HTML。在客户端,React 将再次运行渲染代码,以便将事件处理附加到该 HTML 上。这就是为什么要让 [hydration](/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) 发挥作用,你的初始渲染输出必须在客户端和服务器上完全相同。 在极少数情况下,你可能需要在客户端上显示不同的内容。例如,如果你的应用从 [`localStorage`](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage) 中读取某些数据,服务器上肯定不可能做到这一点。以下是这如何实现的: @@ -1753,7 +1753,7 @@ function MyComponent() { } ``` -当应用加载时,用户首先会看到初始渲染的输出。然后,当它加载完并进行 hydrate 时,Effect 将会运行并且将 `didMount` 设置为 `true`,从而触发重新渲染。这将切换到仅在客户端的渲染输出。Effect 不在服务器上运行,这就是为什么 `didMount` 在初始服务器渲染期间为 `false` 的原因。 +当应用加载时,用户首先会看到初始渲染的输出。然后,当它加载完并进行 hydrate 时,Effect 将会运行并且将 `didMount` 设置为 `true`,从而触发重新渲染。这将切换到仅在客户端的渲染输出。Effect 不在服务器上运行,这就是 `didMount` 在初始服务器渲染期间为 `false` 的原因。 谨慎使用此模式。请记住,网络连接速度较慢的用户将在相当长的时间内(可能是数秒钟)看到初始内容,因此你不希望对组件的外观进行突兀的更改。在许多情况下,你可以通过使用 CSS 条件性地显示不同的内容来避免这种需要。 diff --git a/src/content/reference/react/useLayoutEffect.md b/src/content/reference/react/useLayoutEffect.md index 0888e3d506..f753a028f2 100644 --- a/src/content/reference/react/useLayoutEffect.md +++ b/src/content/reference/react/useLayoutEffect.md @@ -253,7 +253,7 @@ export default function TooltipContainer({ children, x, y, contentRef }) { -注意,即使 `Tooltip` 组件需要两次渲染(首先,使用初始值为 0 的 `tooltipHeight` 渲染,然后使用实际测量的高度渲染),你也只能看到最终结果。这就是为什么在这个例子中需要 `useLayoutEffect` 而不是 [`useEffect`](/reference/react/useEffect) 的原因。让我们来看看下面的细节差别。 +注意,即使 `Tooltip` 组件需要两次渲染(首先,使用初始值为 0 的 `tooltipHeight` 渲染,然后使用实际测量的高度渲染),你也只能看到最终结果。这就是在这个例子中需要 `useLayoutEffect` 而不是 [`useEffect`](/reference/react/useEffect) 的原因。让我们来看看下面的细节差别。 diff --git a/src/content/reference/react/useState.md b/src/content/reference/react/useState.md index 5a71941904..ad82a4a425 100644 --- a/src/content/reference/react/useState.md +++ b/src/content/reference/react/useState.md @@ -439,7 +439,7 @@ setForm({ -#### 表单(对象){/*form-object*/} +#### 表单(对象) {/*form-object*/} 在此示例中,`form` 状态变量保存一个对象。每个输入框都有一个变更处理函数,用整个表单的下一个状态调用 `setForm`。`{ ...form }` 展开语法确保替换状态对象而不是改变它。 @@ -512,7 +512,7 @@ input { margin-left: 5px; } -#### 表单(嵌套对象){/*form-nested-object*/} +#### 表单(嵌套对象) {/*form-nested-object*/} 在此示例中,状态更为嵌套。当你更新嵌套状态时,你需要复制一份正在更新的对象,以及向上“包含”它的所有对象。阅读 [更新嵌套对象](/learn/updating-objects-in-state#updating-a-nested-object) 以了解更多。 @@ -1261,7 +1261,7 @@ setTodos(prevTodos => { }); ``` -现在,这个更新函数是纯粹的,所以多调用一次不会对行为产生影响。这就是为什么 React 调用它两次可以帮助你找到错误的原因。**只有组件、初始化函数和更新函数需要是纯粹的**。事件处理函数不需要是纯粹的,所以 React 不会两次调用你的事件处理函数。 +现在,这个更新函数是纯粹的,所以多调用一次不会对行为产生影响。这就是 React 调用它两次可以帮助你找到错误的原因。**只有组件、初始化函数和更新函数需要是纯粹的**。事件处理函数不需要是纯粹的,所以 React 不会两次调用你的事件处理函数。 阅读 [保持组件纯粹](/learn/keeping-components-pure) 以了解更多信息。 From 40d73490733a1665596eee8b547278231db3b8e3 Mon Sep 17 00:00:00 2001 From: Sophie Alpert Date: Wed, 28 Aug 2024 17:58:32 -0700 Subject: [PATCH 11/23] Parallel structure --- src/content/reference/react/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/index.md b/src/content/reference/react/index.md index 5663af392a..a68ddc014d 100644 --- a/src/content/reference/react/index.md +++ b/src/content/reference/react/index.md @@ -15,7 +15,7 @@ The React reference documentation is broken down into functional subsections: Programmatic React features: * [Hooks](/reference/react/hooks) - Use different React features from your components. -* [Components](/reference/react/components) - Documents built-in components that you can use in your JSX. +* [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. From 55b3ccf4096423a65e8db7ec2469ee86033955d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=85=E7=A7=8B?= Date: Sat, 31 Aug 2024 16:28:56 +0800 Subject: [PATCH 12/23] fix conflicts --- src/content/reference/react/index.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/content/reference/react/index.md b/src/content/reference/react/index.md index 00d2ae4015..774dd40aa2 100644 --- a/src/content/reference/react/index.md +++ b/src/content/reference/react/index.md @@ -14,17 +14,10 @@ React 参考文档分为以下内容: 编程式 React 功能: -<<<<<<< HEAD * [Hook](/reference/react/hooks) —— 在组件中使用不同的 React 特性。 -* [组件](/reference/react/components) —— 记录了可以在 JSX 中使用的内置组件。 +* [组件](/reference/react/components) —— 可以在 JSX 中使用的内置组件。 * [API](/reference/react/apis) —— 用于定义组件的有用 API。 * [指示符](/reference/rsc/directives) —— 为与 React 服务器组件兼容的捆绑器提供指示。 -======= -* [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. ->>>>>>> 40d73490733a1665596eee8b547278231db3b8e3 ## React DOM {/*react-dom*/} From c2d61310664cc0d94f89ca21fc1d44e674329799 Mon Sep 17 00:00:00 2001 From: Tom Eastman Date: Sun, 1 Sep 2024 18:31:29 +1200 Subject: [PATCH 13/23] Fix typo 'bulit' -> 'built' (#7138) Co-authored-by: Tom Eastman --- src/content/reference/react-dom/components/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index c9b355c846..ec2e1d2eea 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -34,7 +34,7 @@ They are special in React because passing the `value` prop to them makes them *[ ## Resource and Metadata Components {/*resource-and-metadata-components*/} -These bulit-in browser components let you load external resources or annotate the document with metadata: +These built-in browser components let you load external resources or annotate the document with metadata: * [``](/reference/react-dom/components/link) * [``](/reference/react-dom/components/meta) From 391dadb993d43c6bd15d9ebfdfc3adeb16d52094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=A9=EA=B8=B0=ED=98=84?= <102677317+kihyeoon@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:54:02 +0900 Subject: [PATCH 14/23] Fix typos in lazy.md and cache.md (#7141) --- src/content/reference/react/cache.md | 4 ++-- src/content/reference/react/lazy.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/reference/react/cache.md b/src/content/reference/react/cache.md index 0e56e3ef0d..099d1c6f15 100644 --- a/src/content/reference/react/cache.md +++ b/src/content/reference/react/cache.md @@ -226,7 +226,7 @@ By caching a long-running data fetch, you can kick off asynchronous work prior t ```jsx [[2, 6, "await getUser(id)"], [1, 17, "getUser(id)"]] const getUser = cache(async (id) => { return await db.user.query(id); -}) +}); async function Profile({id}) { const user = await getUser(id); @@ -327,7 +327,7 @@ In general, you should use [`useMemo`](/reference/react/useMemo) for caching a e 'use client'; function WeatherReport({record}) { - const avgTemp = useMemo(() => calculateAvg(record)), record); + const avgTemp = useMemo(() => calculateAvg(record), record); // ... } diff --git a/src/content/reference/react/lazy.md b/src/content/reference/react/lazy.md index db99daa115..5ec74d0d9c 100644 --- a/src/content/reference/react/lazy.md +++ b/src/content/reference/react/lazy.md @@ -78,7 +78,7 @@ Now that your component's code loads on demand, you also need to specify what sh }>

    Preview

    -
    + ``` In this example, the code for `MarkdownPreview` won't be loaded until you attempt to render it. If `MarkdownPreview` hasn't loaded yet, `Loading` will be shown in its place. Try ticking the checkbox: From 9ac6c319882d8d1bc65c7cb6c33570f1edc21df7 Mon Sep 17 00:00:00 2001 From: William Du <3161377+polaris-dxz@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:33:04 +0800 Subject: [PATCH 15/23] docs(cn): translate `learn`/`react-compiler` into Chinese (#1575) Co-authored-by: Xavi Lee --- src/content/learn/react-compiler.md | 200 ++++++++++++++-------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/src/content/learn/react-compiler.md b/src/content/learn/react-compiler.md index 2920e86434..a4ce793bff 100644 --- a/src/content/learn/react-compiler.md +++ b/src/content/learn/react-compiler.md @@ -3,52 +3,52 @@ title: React Compiler --- -This page will give you an introduction to the new experimental React Compiler and how to try it out successfully. +本页面将为你介绍新的实验性 React Compiler,以及如何成功试用。 -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. +这些文档仍在不断完善中。更多文档可在 [React Compiler 工作组代码库](https://github.com/reactwg/react-compiler/discussions) 中找到,并在这些文档更加稳定时被整合进来。 -* Getting started with the compiler -* Installing the compiler and eslint plugin -* Troubleshooting +* 开始使用 React Compiler +* 安装 React Compiler 和 ESLint 插件 +* 疑难解答 -React Compiler is a new experimental compiler that we've open sourced to get early feedback from the community. It still has rough edges and is not yet fully ready for production. +React Compiler 是一个新的实验性编译器,我们已经开源,以便从社区中获得早期反馈。它仍然存在一些问题,还没有完全准备好投入生产。 -React Compiler requires React 19 RC. If you are unable to upgrade to React 19, you may try a userspace implementation of the cache function as described in the [Working Group](https://github.com/reactwg/react-compiler/discussions/6). However, please note that this is not recommended and you should upgrade to React 19 when possible. +React Compiler 需要 React 19 RC。如果你无法升级到 React 19,你可以尝试 userspace 实现缓存功能,如 [工作组](https://github.com/reactwg/react-compiler/discussions/6) 中所述。但请注意,这并不推荐,你应尽可能升级到 React 19。 -React Compiler is a new experimental 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 是一个新的实验性编译器,我们已经开源以获得社区的早期反馈。它是一个仅在构建时使用的工具,可以自动优化你的 React 应用程序。它可以与纯 JavaScript 一起使用,并且了解 [React 规则](/reference/rules),因此你无需重写任何代码即可使用它。 -The compiler also includes an [eslint plugin](#installing-eslint-plugin-react-compiler) that surfaces the analysis from the compiler right in your editor. The plugin runs independently of the compiler and can be used even if you aren't using the compiler in your app. We recommend all React developers to use this eslint plugin to help improve the quality of your codebase. +编译器还包括一个 [ESLint 插件](#installing-eslint-plugin-react-compiler),可以在你的编辑器中直接显示编译器的分析结果。该插件独立运行,即使你的应用程序中没有使用编译器也可以使用。我们建议所有 React 开发人员使用这个 ESLint 插件来帮助提高代码库的质量。 -### What does the compiler do? {/*what-does-the-compiler-do*/} +### 编译器是做什么的? {/*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. +为了优化应用程序,React Compiler 会自动对你的代码进行记忆化处理。你可能已经熟悉了像 `useMemo`、`useCallback` 和 `React.memo` 这样的 API,通过这些 API,你可以告诉 React,如果输入没有发生变化,应用程序的某些部分不需要重新计算,从而减少更新时的工作量。虽然这些功能很强大,但很容易忘记应用记忆化或者错误地应用它们。这可能会导致更新效率低下,因为 React 必须检查 UI 中没有任何**有意义**的更改的部分。 -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. +编译器利用其对 JavaScript 和 React 规则的了解,自动对组件和钩子中的值或值组进行记忆化。如果它检测到规则的破坏,它将自动跳过那些组件或钩子,并继续安全地编译其他代码。 -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. +如果你的代码库已经非常好地进行了记忆化处理,你可能不会指望通过编译器看到主要的性能改进。然而,在实践中,手动正确记忆化导致性能问题的依赖关系是很棘手的。 -#### What kind of memoization does React Compiler add? {/*what-kind-of-memoization-does-react-compiler-add*/} +#### React Compiler 添加了什么样的记忆? {/*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: +React 编译器的初始版本主要专注于**改善更新性能**(重新渲染现有组件),因此它专注于以下两种用例: -1. **Skipping cascading re-rendering of components** - * Re-rendering `` causes many components in its component tree to re-render, even though only `` 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. **跳过组件的级联重新渲染** + * 重新渲染 `` 会导致其组件树中的许多组件重新渲染,即使只有 `` 发生了变化 +2. **从 React 外部跳过昂贵计算** + * 例如,在需要该数据的组件或钩子内部调用 `expensivelyProcessAReallyLargeArrayOfObjects()` -#### Optimizing Re-renders {/*optimizing-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, `` will re-render whenever ``'s state changes: +React 允许你将你的 UI 表达为它们当前状态的函数(更具体地说:它们的属性、状态和上下文)。在当前的实现中,当组件的状态发生变化时,React 将重新渲染该组件及其**所有子组件**——除非你使用 `useMemo()`、`useCallback()` 或 `React.memo()` 应用了某种形式的手动记忆。例如,在以下示例中,每当 `` 的状态发生变化时,`` 将重新呈现: ```javascript function FriendList({ friends }) { @@ -67,70 +67,70 @@ function FriendList({ friends }) { ); } ``` -[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA) +[**在 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 `` can be reused even as `friends` changes, and can avoid recreating this JSX _and_ avoid re-rendering `` as the count changes. +React Compiler 会自动应用等效的手动记忆,确保只有应用的相关部分在状态发生变化时重新渲染,这有时被称为“细粒度反应”。在上面的例子中,React Compiler 确定 `` 的返回值即使在 `friends` 发生变化时也可以重用,并且可以避免重新创建此 JSX,**并**避免在 `onlineCount` 变化时重新渲染 ``。 -#### Expensive calculations also get memoized {/*expensive-calculations-also-get-memoized*/} +#### 昂贵计算也会被记忆 {/*expensive-calculations-also-get-memoized*/} -The compiler can also automatically memoize for expensive calculations used during rendering: +编译器还可以自动记忆渲染过程中使用的昂贵计算: ```js -// **Not** memoized by React Compiler, since this is not a component or hook +// 由于这不是组件或钩子,React Compiler 不会进行记忆 function expensivelyProcessAReallyLargeArrayOfObjects() { /* ... */ } -// Memoized by React Compiler since this is a component +// 由 React Compiler 进行了记忆化,因为这是一个组件 function TableContainer({ items }) { - // This function call would be memoized: + // 这个函数调用将被记忆: const data = expensivelyProcessAReallyLargeArrayOfObjects(items); // ... } ``` -[_See this example in the React Compiler Playground_](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA) +[**在 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: +但是,如果 `expensivelyProcessAReallyLargeArrayOfObjects` 确实是一个昂贵的函数,你可能需要考虑在 React 之外实现它自己的记忆,因为: -- 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 只记住 React 组件和钩子,而不是每个函数 +- React Compiler 的记忆不会在多个组件或钩子之间共享 -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. +因此,如果在许多不同的组件中使用 `expensivelyProcessAReallyLargeArrayOfObjects`,即使传递相同的 `items`,那昂贵的计算也会被重复运行。我们建议先进行 [性能分析](/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive),看看是否真的那么昂贵,然后再使代码更加复杂。 -### What does the compiler assume? {/*what-does-the-compiler-assume*/} +### 编译器假设什么? {/*what-does-the-compiler-assume*/} -React Compiler assumes that your code: +React Compiler 假设你的代码: -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. 是有效的,语义化的 JavaScript +2. 在访问可空/可选值和属性之前,测试它们是否已定义(例如,如果使用 TypeScript,则启用 [`strictNullChecks`](https://www.typescriptlang.org/tsconfig/#strictNullChecks)),即:`if (object.nullableProperty) { object.nullableProperty.foo }` 或者使用可选链 `object.nullableProperty?.foo` +3. 遵循 [React 规则](/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 可以静态验证 React 的许多规则,并且在检测到错误时会安全地跳过编译。要查看错误,我们建议同时安装 [eslint-plugin-react-compiler](https://www.npmjs.com/package/eslint-plugin-react-compiler)。 -### Should I try out the compiler? {/*should-i-try-out-the-compiler*/} +### 我应该尝试一下编译器吗? {/*should-i-try-out-the-compiler*/} -Please note that the compiler is still experimental 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). +请注意,编译器仍处于实验阶段,存在许多不完善之处。虽然它已经在 Meta 等公司的生产环境中使用过,但将编译器应用于你的应用程序生产环境将取决于你的代码库的健康状况以及你是否遵循了 [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. +**你现在不必急着使用编译器。在采用它之前等到它达到稳定版本是可以的。** 然而,我们确实赞赏在你的应用程序中进行小型实验,以便你可以向我们 [提供反馈](#reporting-issues),帮助使编译器更好。 -## Getting Started {/*getting-started*/} +## 开始 {/*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. +除了这些文档之外,我们还建议查看 [React Compiler 工作组](https://github.com/reactwg/react-compiler),以获取有关编译器的更多信息和讨论。 -### Checking compatibility {/*checking-compatibility*/} +### 检查兼容性 {/*checking-compatibility*/} -Prior to installing the compiler, you can first check to see if your codebase is compatible: +在安装编译器之前,你可以先检查你的代码库是否兼容: npx react-compiler-healthcheck@experimental -This script will: +此脚本将: -- Check how many components can be successfully optimized: higher is better -- Check for `` usage: having this enabled and followed means a higher chance that the [Rules of React](/reference/rules) are followed -- Check for incompatible library usage: known libraries that are incompatible with the compiler +- 检查有多少个组件可以成功优化:越多越好 +- 检查 `` 的使用情况:启用并遵循此功能意味着遵循 [React 规则](/reference/rules) 的可能性更高 +- 检查不兼容的库使用情况:与编译器不兼容的已知库 -As an example: +举个例子: Successfully compiled 8 out of 9 components. @@ -138,15 +138,15 @@ StrictMode usage not found. Found no usage of incompatible libraries. -### Installing eslint-plugin-react-compiler {/*installing-eslint-plugin-react-compiler*/} +### 安装 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 还为 ESLint 插件提供支持。ESLint 插件可以**独立**于编译器使用,这意味着即使你不使用编译器,也可以使用 ESLint 插件。 npm install eslint-plugin-react-compiler@experimental -Then, add it to your eslint config: +然后,将其添加到你的 ESLint 配置中: ```js module.exports = { @@ -159,16 +159,16 @@ module.exports = { } ``` -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 插件将在编辑器中显示任何违反 React 规则的行为。当它这样做时,这意味着编译器跳过了优化该组件或钩子。这是完全可以的,编译器可以恢复并继续优化代码库中的其他组件。 -**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. +**你不必立即修复所有的违反 ESLint 规则的代码。** 你可以按照自己的节奏来处理它们,以增加被优化的组件和钩子的数量,但在你可以使用编译器之前并不需要修复所有问题。 -### Rolling out the compiler to your codebase {/*using-the-compiler-effectively*/} +### 将编译器应用到你的代码库 {/*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. +#### 现有项目 {/*existing-projects*/} +编译器旨在编译遵循 React 规则的功能组件和钩子。它还可以处理违反这些规则的代码,通过跳过这些组件或钩子来终止执行。然而,由于JavaScript的灵活性,编译器无法捕捉到每一个可能的违规行为,可能会出现错误的负面编译:也就是说,编译器可能会意外地编译出一个违反 [React 规则](/reference/rules) 的组件或钩子,这可能导致未定义的行为。 -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: +因此,要在现有项目中成功采用编译器,我们建议你先在项目代码中的一个小目录中运行它。你可以通过将编译器配置为仅在一组特定的目录上运行来执行此操作: ```js {3} const ReactCompilerConfig = { @@ -178,7 +178,7 @@ const ReactCompilerConfig = { }; ``` -In rare cases, you can also configure the compiler to run in "opt-in" mode using the `compilationMode: "annotation"` option. This makes it so the compiler will only compile components and hooks annotated with a `"use memo"` directive. Please note that the `annotation` mode is a temporary one to aid early adopters, and that we don't intend for the `"use memo"` directive to be used for the long term. +在罕见的情况下,你还可以使用 `compilationMode: "annotation"` 选项将编译器配置为以 "opt-in" 模式运行。这样编译器将只编译带有 `"use memo"` 指令的组件和钩子。请注意,`annotation` 模式是为了帮助早期采用者而设立的临时模式,我们并不打算长期使用 `"use memo"` 指令。 ```js {2,7} const ReactCompilerConfig = { @@ -192,13 +192,13 @@ export default function App() { } ``` -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. +当你对编译器的推出更有信心时,你也可以将覆盖范围扩展到其他目录,并逐渐将其推出到整个应用程序。 -#### New projects {/*new-projects*/} +#### 新项目 {/*new-projects*/} -If you're starting a new project, you can enable the compiler on your entire codebase, which is the default behavior. +如果你正在启动一个新项目,你可以在整个代码库上启用编译器,这是默认行为。 -## Usage {/*installation*/} +## 用法 {/*installation*/} ### Babel {/*usage-with-babel*/} @@ -206,9 +206,9 @@ If you're starting a new project, you can enable the compiler on your entire cod npm install babel-plugin-react-compiler@experimental -The compiler includes a Babel plugin which you can use in your build pipeline to run the compiler. +编译器包含一个 Babel 插件,你可以在构建流水线中使用它来运行编译器。 -After installing, add it to your Babel config. Please note that it's critical that the compiler run **first** in the pipeline: +安装后,请将其添加到你的 Babel 配置中。请注意,编译器必须**首先**在流水线中运行。 ```js {7} // babel.config.js @@ -217,18 +217,18 @@ const ReactCompilerConfig = { /* ... */ }; module.exports = function () { return { plugins: [ - ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first! + ['babel-plugin-react-compiler', ReactCompilerConfig], // 必须首先运行! // ... ], }; }; ``` -`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` 应该在其他 Babel 插件之前运行,因为编译器需要输入源信息进行声音分析。 ### Vite {/*usage-with-vite*/} -If you use Vite, you can add the plugin to vite-plugin-react: +如果你使用 Vite,你可以将插件添加到 vite-plugin-react 中: ```js {10} // vite.config.js @@ -252,16 +252,16 @@ export default defineConfig(() => { ### Next.js {/*usage-with-nextjs*/} -Next.js has an experimental configuration to enable the React Compiler. It automatically ensures Babel is set up with `babel-plugin-react-compiler`. +Next.js 有一个实验性配置来启用 React 编译器。它会自动确保 Babel 已经配置了 `babel-plugin-react-compiler`。 -- Install Next.js canary, which uses React 19 Release Candidate -- Install `babel-plugin-react-compiler` +- 安装使用 React 19 RC 版本的 Next.js canary +- 安装 `babel-plugin-react-compiler` npm install next@canary babel-plugin-react-compiler@experimental -Then configure the experimental option in `next.config.js`: +然后在 `next.config.js` 中配置实验选项: ```js {4,5,6} // next.config.js @@ -275,16 +275,16 @@ const nextConfig = { module.exports = nextConfig; ``` -Using the experimental option ensures support for the React Compiler in: +使用实验选项可确保在以下方面支持 React Compiler: - App Router - Pages Router - Webpack (default) -- Turbopack (opt-in through `--turbo`) +- Turbopack (通过 `--turbo` 接入) ### Remix {/*usage-with-remix*/} -Install `vite-plugin-babel`, and add the compiler's Babel plugin to it: +安装 `vite-plugin-babel`, 并将编译器的 Babel 插件添加到其中: npm install vite-plugin-babel @@ -302,7 +302,7 @@ export default defineConfig({ babel({ filter: /\.[jt]sx?$/, babelConfig: { - presets: ["@babel/preset-typescript"], // if you use TypeScript + presets: ["@babel/preset-typescript"], // 如果你使用 TypeScript plugins: [ ["babel-plugin-react-compiler", ReactCompilerConfig], ], @@ -314,7 +314,7 @@ export default defineConfig({ ### Webpack {/*usage-with-webpack*/} -You can create your own loader for React Compiler, like so: +你可以为 React Compiler 创建自己的 loader,就像这样: ```js const ReactCompilerConfig = { /* ... */ }; @@ -351,46 +351,46 @@ module.exports = reactCompilerLoader; ### Expo {/*usage-with-expo*/} -Please refer to [Expo's docs](https://docs.expo.dev/preview/react-compiler/) to enable and use the React Compiler in Expo apps. +请参考 [Expo 文档](https://docs.expo.dev/preview/react-compiler/) 应用程序中启用和使用 React Compiler。 ### 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 通过 Metro 使用 Babel,因此请参考 [使用 Babel](#usage-with-babel) 部分的安装说明。 ### 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. +请参考 [Rspack 文档](https://rspack.dev/guide/tech/react#react-compiler) 以启用并在 Rspack 应用程序中使用 React Compiler。 ### 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. +请参考 [Rsbuild 文档](https://rsbuild.dev/guide/framework/react#react-compiler) 以在 Rsbuild 应用程序中启用和使用 React Compiler。 -## Troubleshooting {/*troubleshooting*/} +## 疑难解答 {/*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. +请先在 [React Compiler Playground](https://playground.react.dev/) 上创建一个最小的可复现问题,并将其包含在你的错误报告中。你可以在 [facebook/react](https://github.com/facebook/react/issues) 仓库中提交 issue。 -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). +你也可以通过申请成为成员,在 React Compiler 工作组中提供反馈意见。请查看 [README](https://github.com/reactwg/react-compiler) 以获取更多加入详情。 ### `(0 , _c) is not a function` error {/*0--_c-is-not-a-function-error*/} -This occurs if you are not using React 19 RC and up. To fix this, [upgrade your app to React 19 RC](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) first. +如果你没有使用 React 19 RC 及更高版本,则会发生这种情况。要解决此问题,请先 [将你的项目升级到 React 19 RC](https://react.dev/blog/2024/04/25/react-19-upgrade-guide)。 -If you are unable to upgrade to React 19, you may try a userspace implementation of the cache function as described in the [Working Group](https://github.com/reactwg/react-compiler/discussions/6). However, please note that this is not recommended and you should upgrade to React 19 when possible. +如果你无法升级到 React 19,你可以尝试根据 [工作组](https://github.com/reactwg/react-compiler/discussions/6) 描述的缓存功能的用户空间实现。但是,请注意这并不建议,你应尽快升级到React 19。 -### How do I know my components have been optimized? {/*how-do-i-know-my-components-have-been-optimized*/} +### 我如何知道我的组件已被优化? {/*how-do-i-know-my-components-have-been-optimized*/} -[React Devtools](/learn/react-developer-tools) (v5.0+) has built-in support for React Compiler and will display a "Memo ✨" badge next to components that have been optimized by the compiler. +[React 开发工具](/learn/react-developer-tools)(v5.0 及以上版本)对 React Compiler 有内置支持,并会在已被编译器优化的组件旁边显示“Memo ✨”徽章。 -### 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. +### 编译后某些内容无法正常工作 {/*something-is-not-working-after-compilation*/} +如果你安装了 eslint-plugin-react-compiler ,编译器将在你的编辑器中显示任何违反 React 规则的情况。当它这样做时,意味着编译器跳过了对该组件或钩子的优化。这完全没问题,并且编译器可以恢复并继续优化你代码库中的其他组件。**你不必立即修复所有的违反 ESLint 规则的代码。** 你可以按照自己的节奏来处理它们,以增加被优化的组件和钩子的数量。 -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. +然而,由于 JavaScript 的灵活和动态性质,不可能全面检测到所有情况。在这些情况下,可能会出现错误和未定义的行为,例如无限循环。 -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). +如果你的应用在编译后无法正常工作,并且你没有看到任何 ESLint 错误,编译器可能错误地编译了你的代码。为了确认这一点,尝试通过积极选择你认为可能相关的任何组件或钩子来解决问题,通过 [`"use no memo"` 指令](#opt-out-of-the-compiler-for-a-component)。 ```js {2} function SuspiciousComponent() { - "use no memo"; // opts out this component from being compiled by React Compiler + "use no memo"; // 选择不让此组件由 React Compiler 进行编译 // ... } ``` @@ -398,13 +398,13 @@ function SuspiciousComponent() { #### `"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"` 是一个**临时的**逃避机制,它允许你选择不让组件和钩子由 React Compiler 进行编译。此指令不像例如 [`"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. +除非绝对必要,否则不建议使用这个指令。一旦你选择退出一个组件或钩子,它将永久退出,直到指令被移除。这意味着即使你修复了代码,编译器仍然会跳过编译它,除非你移除指令。 -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. +当你修复错误时,请确认删除退出指令是否会使问题重新出现。然后使用 [React Compiler Playground](https://playground.react.dev) 与我们分享一个错误报告(你可以尝试将其减少到一个小的重现,或者如果是开源代码,你也可以直接粘贴整个源代码),这样我们就可以识别并帮助解决问题。 -### Other issues {/*other-issues*/} +### 其他问题 {/*other-issues*/} -Please see https://github.com/reactwg/react-compiler/discussions/7. +请查阅 https://github.com/reactwg/react-compiler/discussions/7 。 From 5b5bbe4d9f44a442cd2723fdfd650d9e594e70a8 Mon Sep 17 00:00:00 2001 From: Xavi Lee Date: Wed, 4 Sep 2024 10:36:16 +0800 Subject: [PATCH 16/23] =?UTF-8?q?translate=20`hydrate`=20into=20=E2=80=9C?= =?UTF-8?q?=E6=BF=80=E6=B4=BB=E2=80=9D=20(#1583)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blog/2022/03/08/react-18-upgrade-guide.md | 4 +- src/content/blog/2022/03/29/react-v18.md | 8 ++-- src/content/blog/2024/04/25/react-19.md | 8 ++-- .../reference/react-dom/client/hydrateRoot.md | 38 +++++++++---------- .../reference/react-dom/components/common.md | 2 +- src/content/reference/react-dom/hydrate.md | 20 +++++----- .../react-dom/server/renderToNodeStream.md | 2 +- .../server/renderToPipeableStream.md | 4 +- .../server/renderToStaticNodeStream.md | 6 +-- .../react-dom/server/renderToString.md | 2 +- src/content/reference/react/useActionState.md | 2 +- src/content/reference/react/useEffect.md | 2 +- src/content/reference/react/useId.md | 2 +- .../reference/react/useLayoutEffect.md | 2 +- .../reference/react/useSyncExternalStore.md | 2 +- 15 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/content/blog/2022/03/08/react-18-upgrade-guide.md b/src/content/blog/2022/03/08/react-18-upgrade-guide.md index 6a8b335dae..05d834e454 100644 --- a/src/content/blog/2022/03/08/react-18-upgrade-guide.md +++ b/src/content/blog/2022/03/08/react-18-upgrade-guide.md @@ -103,7 +103,7 @@ root.render(); -最后如果应用通过 hydrate 使用了服务端渲染,你需要将 `hydrate` 升级到 `hydrateRoot`: +最后如果应用通过激活使用了服务端渲染,你需要将 `hydrate` 升级到 `hydrateRoot`: ```js // 之前 @@ -306,7 +306,7 @@ globalThis.IS_REACT_ACT_ENVIRONMENT = true; ## 其他破坏性变更 {/*other-breaking-changes*/} * **一致的 useEffect 时间**:现在,如果更新是在类似点击或者敲击键盘事件这样的离散用户输入事件期间触发,React 总是同步刷新 Effect 函数。而之前的行为不是一直可预测或者一致的。 -* **更严格的 hydrate 报错**:由于缺失或者额外的文本而导致的 hydrate 不匹配现在会作为错误而不是告警对待。React 将不再试图通过在客户端增加或删除节点来“修补”单个节点来匹配服务端标记,并且将会回退客户端渲染到树中最近的 `` 边界。这可以保证 hydrate 树保持一致并且避免可能由 hydrate 不匹配导致的隐私和安全漏洞。 +* **更严格的激活报错**:由于缺失或者额外的文本而导致的激活内容不匹配现在会作为错误而不是告警对待。React 将不再试图通过在客户端增加或删除节点来“修补”单个节点来匹配服务端标记,并且将会回退客户端渲染到树中最近的 `` 边界。这可以保证激活树保持一致并且避免可能由激活内容不匹配导致的隐私和安全漏洞。 * **Suspense 树一直保持一致**:如果一个组件在它完全被添加到树上之前挂起,React 将不会把它以不完整的状态添加到树或者触发它的 effect。React 会完全扔掉新树,等待异步操作结束,然后重新尝试从头开始再次渲染。React 会同时渲染重试尝试,并且不会阻塞浏览器。 * **使用 Suspense 的 Layout Effect**:当一个树重新挂起并恢复为后备方案时,现在的 React 会清理 layout effect,然后在边界内的内容再次显示时重新创建它们。这修复了一个在与 Suspense 一起使用时的问题:阻止组件库正确测量布局。 * **新的 JavaScript 环境要求**:React 现在依赖于现代浏览器特性,包括 `Promise`、`Symbol` 和 `Object.assign`。如果你需要支持像 Internet Explorer 这样较老版本的浏览器和设备,它们本身不提供现代浏览器特性或者有不兼容的实现,可以考虑在打包后的应用中包含全局的 polyfill。 diff --git a/src/content/blog/2022/03/29/react-v18.md b/src/content/blog/2022/03/29/react-v18.md index a66d85e2c2..542bf8a5c6 100644 --- a/src/content/blog/2022/03/29/react-v18.md +++ b/src/content/blog/2022/03/29/react-v18.md @@ -180,7 +180,7 @@ React 18 中的 Suspense 在与 Transition API 结合时效果最好。如果你 * `createRoot`:为 `render` 或者 `unmount` 创建根节点的新方法。请用它替代 `ReactDOM.render`。如果没有它,React 18 中的新功能就无法生效。 * `hydrateRoot`:hydrate 服务端渲染的应用的新方法。使用它来替代 `ReactDOM.hydrate` 与新的 React DOM 服务端 API 一起使用。如果没有它,React 18 中的新功能就无法生效。 -`createRoot` 和 `hydrateRoot` 都能接受一个新的可选参数叫做 `onRecoverableError`,它能在 React 在渲染或者 hydrate 过程发生错误后又恢复时,做日志记录对你进行通知。默认情况下,React 会使用 [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError),如果在老旧版本浏览器中,则会使用 `console.error`。 +`createRoot` 和 `hydrateRoot` 都能接受一个新的可选参数叫做 `onRecoverableError`,它能在 React 在渲染或者激活过程发生错误后又恢复时,做日志记录对你进行通知。默认情况下,React 会使用 [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError),如果在老旧版本浏览器中,则会使用 `console.error`。 [参阅 React DOM Client 的文档](/reference/react-dom/client)。 @@ -232,7 +232,7 @@ React 18 中的 Suspense 在与 Transition API 结合时效果最好。如果你 #### useId {/*useid*/} -`useId` 是一个新的Hook,用于生成在客户端和服务端两侧都独一无二的 id,避免 hydrate 后两侧内容不匹配。它主要用于需要唯一 id 的,具有集成 API 的组件库。这个更新不仅解决了一个在 React 17 及更低版本中的存在的问题,而且它会在 React 18 中发挥更重要的作用,因为新的流式服务端渲染响应 HTML 的方式将是无序的,需要独一无二的 id 作为索引。[参阅文档](/reference/react/useId)。 +`useId` 是一个新的Hook,用于生成在客户端和服务端两侧都独一无二的 id,避免激活后两侧内容不匹配。它主要用于需要唯一 id 的,具有集成 API 的组件库。这个更新不仅解决了一个在 React 17 及更低版本中的存在的问题,而且它会在 React 18 中发挥更重要的作用,因为新的流式服务端渲染响应 HTML 的方式将是无序的,需要独一无二的 id 作为索引。[参阅文档](/reference/react/useId)。 > Note > @@ -299,12 +299,12 @@ React 18 中的 Suspense 在与 Transition API 结合时效果最好。如果你 * 修复生成的 License 头。([#23004](https://github.com/facebook/react/pull/23004) [@vitaliemiron](https://github.com/vitaliemiron)) * 添加 `package.json` 作为入口点之一。 ([#22954](https://github.com/facebook/react/pull/22954) [@Jack](https://github.com/Jack-Works)) * 允许在 Suspense 边界外挂起。([#23267](https://github.com/facebook/react/pull/23267) [@acdlite](https://github.com/acdlite)) -* 每当 hydrate 失败时记录一个可恢复的错误。([#23319](https://github.com/facebook/react/pull/23319) [@acdlite](https://github.com/acdlite)) +* 每当激活失败时记录一个可恢复的错误。([#23319](https://github.com/facebook/react/pull/23319) [@acdlite](https://github.com/acdlite)) ### React DOM {/*react-dom*/} * 添加 `createRoot` 和 `hydrateRoot`。([#10239](https://github.com/facebook/react/pull/10239),[#11225](https://github.com/facebook/react/pull/11225),[#12117](https://github.com/facebook/react/pull/12117),[#13732](https://github.com/facebook/react/pull/13732),[#15502](https://github.com/facebook/react/pull/15502),[#15532](https://github.com/facebook/react/pull/15532),[#17035](https://github.com/facebook/react/pull/17035),[#17165](https://github.com/facebook/react/pull/17165),[#20669](https://github.com/facebook/react/pull/20669),[#20748](https://github.com/facebook/react/pull/20748),[#20888](https://github.com/facebook/react/pull/20888),[#21072](https://github.com/facebook/react/pull/21072),[#21417](https://github.com/facebook/react/pull/21417),[#21652](https://github.com/facebook/react/pull/21652),[#21687](https://github.com/facebook/react/pull/21687),[#23207](https://github.com/facebook/react/pull/23207),[#23385](https://github.com/facebook/react/pull/23385) [@acdlite](https://github.com/acdlite),[@bvaughn](https://github.com/bvaughn),[@gaearon](https://github.com/gaearon),[@lunaruan](https://github.com/lunaruan),[@rickhanlonii](https://github.com/rickhanlonii),[@trueadm](https://github.com/trueadm),and [@sebmarkbage](https://github.com/sebmarkbage)) -* 添加选择性 hydrate。([#14717](https://github.com/facebook/react/pull/14717),[#14884](https://github.com/facebook/react/pull/14884),[#16725](https://github.com/facebook/react/pull/16725),[#16880](https://github.com/facebook/react/pull/16880),[#17004](https://github.com/facebook/react/pull/17004),[#22416](https://github.com/facebook/react/pull/22416),[#22629](https://github.com/facebook/react/pull/22629),[#22448](https://github.com/facebook/react/pull/22448),[#22856](https://github.com/facebook/react/pull/22856),[#23176](https://github.com/facebook/react/pull/23176) [@acdlite](https://github.com/acdlite),[@gaearon](https://github.com/gaearon),[@salazarm](https://github.com/salazarm),and [@sebmarkbage](https://github.com/sebmarkbage)) +* 添加选择性激活。([#14717](https://github.com/facebook/react/pull/14717),[#14884](https://github.com/facebook/react/pull/14884),[#16725](https://github.com/facebook/react/pull/16725),[#16880](https://github.com/facebook/react/pull/16880),[#17004](https://github.com/facebook/react/pull/17004),[#22416](https://github.com/facebook/react/pull/22416),[#22629](https://github.com/facebook/react/pull/22629),[#22448](https://github.com/facebook/react/pull/22448),[#22856](https://github.com/facebook/react/pull/22856),[#23176](https://github.com/facebook/react/pull/23176) [@acdlite](https://github.com/acdlite),[@gaearon](https://github.com/gaearon),[@salazarm](https://github.com/salazarm),and [@sebmarkbage](https://github.com/sebmarkbage)) * 在已知的 ARIA 属性列表中增加 `aria-description`。([#22142](https://github.com/facebook/react/pull/22142) [@mahyareb](https://github.com/mahyareb)) * 为 video 元素添加 `onResize` 事件。([#21973](https://github.com/facebook/react/pull/21973) [@rileyjshaw](https://github.com/rileyjshaw)) * 将 `imageSizes` 和 `imageSrcSet` 添加到已知属性中。([#22550](https://github.com/facebook/react/pull/22550) [@eps1lon](https://github.com/eps1lon)) diff --git a/src/content/blog/2024/04/25/react-19.md b/src/content/blog/2024/04/25/react-19.md index a87b711b5f..d8be8e0081 100644 --- a/src/content/blog/2024/04/25/react-19.md +++ b/src/content/blog/2024/04/25/react-19.md @@ -378,7 +378,7 @@ function MyInput({placeholder, ref}) { -### hydration 错误的差异 {/*diffs-for-hydration-errors*/} +### 激活错误的差异 {/*diffs-for-hydration-errors*/} 在 `react-dom` 中,我们也改进了水合错误的错误报告。例如,现在不再在 DEV 中记录多个没有任何不匹配信息的错误: @@ -682,11 +682,11 @@ function MyComponent() { ### 兼容第三方脚本和扩展 {/*compatibility-with-third-party-scripts-and-extensions*/} -我们改进了 hydration,以考虑第三方脚本和浏览器扩展。 +我们改进了激活机制,以考虑第三方脚本和浏览器扩展。 -在 hydration 过程中,如果在客户端渲染的元素与从服务器获取的 HTML 中找到的元素不匹配,React 将强制进行客户端重新渲染以修复内容。以前,如果一个元素是由第三方脚本或浏览器扩展插入的,它会触发一个不匹配的错误并进行客户端渲染。 +在激活过程中,如果在客户端渲染的元素与从服务器获取的 HTML 中找到的元素不匹配,React 将强制进行客户端重新渲染以修复内容。以前,如果一个元素是由第三方脚本或浏览器扩展插入的,它会触发一个不匹配的错误并进行客户端渲染。 -在 React 19 中,`` 和 `` 中的意外标签将被跳过,避免了不匹配的错误。如果 React 需要由于无关的 hydration 不匹配而重新渲染整个文档,它将保留由第三方脚本和浏览器扩展插入的样式表。 +在 React 19 中,`` 和 `` 中的意外标签将被跳过,避免了不匹配的错误。如果 React 需要由于无关的激活不匹配而重新渲染整个文档,它将保留由第三方脚本和浏览器扩展插入的样式表。 ### 更好的错误报告 {/*error-handling*/} diff --git a/src/content/reference/react-dom/client/hydrateRoot.md b/src/content/reference/react-dom/client/hydrateRoot.md index 9b09210a80..db2a9072bb 100644 --- a/src/content/reference/react-dom/client/hydrateRoot.md +++ b/src/content/reference/react-dom/client/hydrateRoot.md @@ -56,7 +56,7 @@ React 将会连接到内部有 `domNode` 的 HTML 上,然后接管其中的 `d #### 警告 {/*caveats*/} * `hydrateRoot()` 期望渲染内容与服务端渲染的内容完全相同。你应该将不匹配视为错误并进行修复。 -* 在开发模式下,React 会在 hydrate 期间发出不匹配警告。在不匹配的情况下,不能保证内容差异会被修补。出于性能原因,这很重要,因为在大多数应用程序中,不匹配很少见,因此验证所有标记将是昂贵而不可行的。 +* 在开发模式下,React 会在激活期间发出不匹配警告。在不匹配的情况下,不能保证内容差异会被修补。出于性能原因,这很重要,因为在大多数应用程序中,不匹配很少见,因此验证所有标记将是昂贵而不可行的。 * 你的应用程序可能只有一个 `hydrateRoot()` 函数调用。如果你使用框架,则可能会为你完成此调用。 * 如果你的应用程序是客户端渲染,并且没有已渲染好的 HTML,则不支持使用 `hydrateRoot()`。请改用 [`createRoot()`](/reference/react-dom/client/createRoot)。 @@ -64,13 +64,13 @@ React 将会连接到内部有 `domNode` 的 HTML 上,然后接管其中的 `d ### `root.render(reactNode)` {/*root-render*/} -使用 `root.render` 更新一个 hydrate 根组件中的 React 组件来渲染浏览器端 DOM 元素。 +使用 `root.render` 更新一个激活根组件中的 React 组件来渲染浏览器端 DOM 元素。 ```js root.render(); ``` -React 将会在 hydrate `root` 中更新 ``。 +React 将会在激活 `root` 中更新 ``。 [参见下面更多示例](#usage)。 @@ -85,7 +85,7 @@ React 将会在 hydrate `root` 中更新 ``。 #### 警告 {/*root-render-caveats*/} -* 如果你在根节点还没有完成 hydrate 的情况下调用了 `root.render`,React 将清除现有的服务端渲染 HTML 内容,并将整个根节点切换到客户端渲染。 +* 如果你在根节点还没有完成激活的情况下调用了 `root.render`,React 将清除现有的服务端渲染 HTML 内容,并将整个根节点切换到客户端渲染。 --- @@ -123,7 +123,7 @@ root.unmount(); ## 用法 {/*usage*/} -### hydrate 服务端渲染的 HTML {/*hydrating-server-rendered-html*/} +### 激活服务端渲染的 HTML {/*hydrating-server-rendered-html*/} 如果你的应用程序的 HTML 是由 [`react-dom/server`](/reference/react-dom/client/createRoot) 生成的,你需要在客户端上进行 **hydrate**。 @@ -133,9 +133,9 @@ import { hydrateRoot } from 'react-dom/client'; hydrateRoot(document.getElementById('root'), ); ``` -对于你的应用程序来说,这将 hydrate 你的服务端 HTML 来复苏里面的 浏览器 DOM 节点React 组件。通常,你只需要在启动时执行一次。如果你使用框架,则可能会自动在幕后执行此操作。 +对于你的应用程序来说,这将激活你的服务端 HTML 来复苏里面的 浏览器 DOM 节点React 组件。通常,你只需要在启动时执行一次。如果你使用框架,则可能会自动在幕后执行此操作。 -为了进行 hydrate,React 将把你的组件逻辑连接到服务器上生成的初始 HTML 中。hydrate 可以将来自服务器的初始 HTML 快照转换为在浏览器中运行的完全可交互应用。 +为了进行激活,React 将把你的组件逻辑连接到服务器上生成的初始 HTML 中。激活可以将来自服务器的初始 HTML 快照转换为在浏览器中运行的完全可交互应用。 @@ -190,20 +190,20 @@ function Counter() { 这对于用户体验非常重要。用户会在你的 JavaScript 代码加载前花费一些时间来查看服务端生成的 HTML。服务端渲染通过显示应用输出的 HTML 快照来产生了应用程序加速加载的错觉。突然出现不同的内容会破坏这种错觉。这就是为什么服务端渲染输出必须与客户端初始渲染输出匹配。 -导致 hydrate 错误的最常见原因包括: +导致激活错误的最常见原因包括: * 根节点 React 生成的 HTML 周围存在额外的空白符(如换行符)。 * 在渲染逻辑中使用 `typeof window !== 'undefined'` 这样的判断。 * 在渲染逻辑中使用仅限于浏览器端的 API,例如 [`window.matchMedia`](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/matchMedia)。 * 在服务器和客户端渲染不同的数据。 -React 可以从一些 hydrate 错误中恢复,但 **你必须像处理其他 bug 一样修复它们**。在最好的情况下,它们会导致应用程序加载变慢;在最坏的情况下,事件处理程序可能会附加到错误的元素上。 +React 可以从一些激活错误中恢复,但 **你必须像处理其他 bug 一样修复它们**。在最好的情况下,它们会导致应用程序加载变慢;在最坏的情况下,事件处理程序可能会附加到错误的元素上。 --- -### hydrate 整个文档 {/*hydrating-an-entire-document*/} +### 激活整个文档 {/*hydrating-an-entire-document*/} 完全使用 React 构建的应用程序可以将整个文档作为 JSX 渲染,包括 [``](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/html) 标签: @@ -225,7 +225,7 @@ function App() { } ``` -要对整个文档进行 hydrate 处理,将全局的 [`document`](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/document) 作为 `hydrateRoot` 的第一个参数传递: +要对整个文档进行激活处理,将全局的 [`document`](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/document) 作为 `hydrateRoot` 的第一个参数传递: ```js {4} import { hydrateRoot } from 'react-dom/client'; @@ -236,11 +236,11 @@ hydrateRoot(document, ); --- -### 抑制不可避免的 hydrate 处理不匹配错误 {/*suppressing-unavoidable-hydration-mismatch-errors*/} +### 抑制不可避免的激活处理不匹配错误 {/*suppressing-unavoidable-hydration-mismatch-errors*/} -如果一个单独元素属性或文本内容在服务器和客户端之间是不可避免地不同的(例如,时间戳),则可以抑制 hydrate 处理不匹配警告。 +如果一个单独元素属性或文本内容在服务器和客户端之间是不可避免地不同的(例如,时间戳),则可以抑制激活处理不匹配警告。 -要消除对元素的 hydrate 处理警告,请添加 `suppressHydrationWarning={true}`: +要消除对元素的激活处理警告,请添加 `suppressHydrationWarning={true}`: @@ -318,21 +318,21 @@ export default function App() { -这样,初始渲染将呈现与服务器相同的内容,避免不匹配,但是在 hydrate 之后会同步进行额外的渲染。 +这样,初始渲染将呈现与服务器相同的内容,避免不匹配,但是在激活之后会同步进行额外的渲染。 -这种方法使得 hydrate 变慢,因为你的组件需要渲染两次。要注意在网络连接较慢的情况下用户的体验。JavaScript 代码的加载时间可能会比初始的 HTML 渲染慢很多,因此在 hydrate 之后立即呈现不同的 UI 对用户来说可能也会感到不适。 +这种方法使得激活变慢,因为你的组件需要渲染两次。要注意在网络连接较慢的情况下用户的体验。JavaScript 代码的加载时间可能会比初始的 HTML 渲染慢很多,因此在激活之后立即呈现不同的 UI 对用户来说可能也会感到不适。 --- -### 更新 hydrate 根组件 {/*updating-a-hydrated-root-component*/} +### 更新激活根组件 {/*updating-a-hydrated-root-component*/} -在根组件 hydrate 完成之后,你可以调用 [`root.render`](#root-render) 来更新根 React 组件。**与 [`createRoot`](/reference/react-dom/client/createRoot) 不同的是,通常你不需要这样做,因为初始内容已经渲染为 HTML**。 +在根组件激活完成之后,你可以调用 [`root.render`](#root-render) 来更新根 React 组件。**与 [`createRoot`](/reference/react-dom/client/createRoot) 不同的是,通常你不需要这样做,因为初始内容已经渲染为 HTML**。 -如果在 hydrate 之后某个时刻调用了 `root.render`,并且组件树结构与之前渲染的相匹配,那么 React 将 [保留重置 state](/learn/preserving-and-resetting-state)。请注意,你可以在输入框中输入文字,这意味着在此示例中每秒钟重复调用的 `render` 不会破坏已有的组件状态: +如果在激活之后某个时刻调用了 `root.render`,并且组件树结构与之前渲染的相匹配,那么 React 将 [保留重置 state](/learn/preserving-and-resetting-state)。请注意,你可以在输入框中输入文字,这意味着在此示例中每秒钟重复调用的 `render` 不会破坏已有的组件状态: diff --git a/src/content/reference/react-dom/components/common.md b/src/content/reference/react-dom/components/common.md index ae19cea8ce..f7e6208872 100644 --- a/src/content/reference/react-dom/components/common.md +++ b/src/content/reference/react-dom/components/common.md @@ -34,7 +34,7 @@ title: "普通组件(例如
    )" * `suppressContentEditableWarning`:布尔值。如果为 `true` 将会抑制 React 对同时具有 `child` 和 `contentEditable={true}` 属性的元素发出的警告(这两者通常不能同时使用)。如果你正在构建一个手动管理 `contentEditable` 内容的文本输入库,请使用此选项。 -* `suppressHydrationWarning`:布尔值。如果你使用 [服务器渲染](/reference/react-dom/server),通常会在服务器和客户端渲染不同内容时发出警告。在一些罕见的情况下(比如时间戳),很难或者不可能保证完全匹配。如果你设置 `suppressHydrationWarning` 为 `true`,React 不会在元素属性和内容不匹配时发出警告。它只能在同级工作,并被作为脱围机制。[阅读有关抑制 hydrate 错误的内容](/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors)。 +* `suppressHydrationWarning`:布尔值。如果你使用 [服务器渲染](/reference/react-dom/server),通常会在服务器和客户端渲染不同内容时发出警告。在一些罕见的情况下(比如时间戳),很难或者不可能保证完全匹配。如果你设置 `suppressHydrationWarning` 为 `true`,React 不会在元素属性和内容不匹配时发出警告。它只能在同级工作,并被作为脱围机制。[阅读有关抑制激活错误的内容](/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors)。 * `style`:CSS 样式对象,如 `{ fontWeight:'bold',margin:20 }`。与 DOM [`style`](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/style) 属性类似,CSS 属性应该使用像 `camelCase` 这样的驼峰命名法,如应该使用 `fontWeight` 而不是 `font-weight`。你可以将字符串或数字作为值传递,类似 `width: 100`,React 会自动将值附加为 `px`(“像素”),除非它是一个 [无单位的属性](https://github.com/facebook/react/blob/81d4ee9ca5c405dce62f64e61506b8e155f38d8d/packages/react-dom-bindings/src/shared/CSSProperty.js#L8-L57)。我们建议仅在动态样式中使用 `style`,即事先不知道样式值。在其他情况下,使用普通的 CSS 类和 `className` 更有效。[了解有关 `className` 和 `style` 的更多信息](#applying-css-styles)。 diff --git a/src/content/reference/react-dom/hydrate.md b/src/content/reference/react-dom/hydrate.md index 003513f729..e47add11fc 100644 --- a/src/content/reference/react-dom/hydrate.md +++ b/src/content/reference/react-dom/hydrate.md @@ -46,7 +46,7 @@ React 将会附加到 `domNode` 内部现有的 HTML,并接管有关的 DOM * `domNode`:在服务器中被渲染为根节点的 [DOM 元素](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)。 -* **可选属性** `callback`:一个函数。如果传递了该参数,React 将会在组件 hydrate 后调用它。 +* **可选属性** `callback`:一个函数。如果传递了该参数,React 将会在组件激活后调用它。 #### 返回值 {/*returns*/} @@ -54,7 +54,7 @@ React 将会附加到 `domNode` 内部现有的 HTML,并接管有关的 DOM #### 注意 {/*caveats*/} * `hydrate` 要求渲染的内容与服务器渲染的内容完全相同。尽管 React 可以修复文本内容的差异,但你应该首先将不匹配视为错误并进行修复。 -* 在开发模式下,React 会在 hydration 期间警告不匹配的错误。如果存在不匹配情况,无法保证属性的差异会被修补。在大多数应用程序中不匹配是很少见的,所以验证所有标记的代价将会很高。因此考虑到性能原因,这是很重要的。 +* 在开发模式下,React 会在激活期间警告不匹配的错误。如果存在不匹配情况,无法保证属性的差异会被修补。在大多数应用程序中不匹配是很少见的,所以验证所有标记的代价将会很高。因此考虑到性能原因,这是很重要的。 * 你的应用程序中可能只有一个 `hydrate` 调用。如果你使用了框架,它可能会为你执行此调用。 * 如果你的应用程序是客户端渲染的,并且没有已经渲染的 HTML,则不支持使用 `hydrate()`。请改用 [render()](/reference/react-dom/render)(适用于 React 17 及以下版本)或 [createRoot()](/reference/react-dom/client/createRoot)(适用于 React 18 及以上版本)。 @@ -72,9 +72,9 @@ hydrate(, document.getElementById('root')); 不支持使用 `hydrate()` 渲染仅用于客户端的应用程序(没有服务器渲染的 HTML)。请改用 [`render()`](/reference/react-dom/render)(适用于 React 17 及以下版本)或 [`createRoot()`](/reference/react-dom/client/createRoot)(适用于 React 18 及以上版本)。 -### hydrate 服务器渲染的 HTML {/*hydrating-server-rendered-html*/} +### 激活服务器渲染的 HTML {/*hydrating-server-rendered-html*/} -在 React 中,hydrate 是指将 React “附加(attach)”到在服务器环境中已由 React 渲染的现有 HTML 上。在 hydrate 期间,React 将尝试将事件监听器附加(attach)到现有标记,并在客户端上接管渲染应用程序。 +在 React 中,激活(hydrate)是指将 React “附加(attach)”到在服务器环境中已由 React 渲染的现有 HTML 上。在激活期间,React 将尝试将事件监听器附加(attach)到现有标记,并在客户端上接管渲染应用程序。 在完全使用 React 构建的应用程序中,**通常只会在第一次启动整个应用程序时,hydrate “根”节点**。 @@ -106,15 +106,15 @@ export default function App() { 通常情况下,你不需要再次调用 `hydrate`,也不需要在更多地方调用它。从此时开始,React 将会管理你的应用程序的 DOM。为了更新 UI,你的组件将会 [使用 state](/reference/react/useState)。 -有关 hydrate 的更多信息,请参阅 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot)。 +有关激活的更多信息,请参阅 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot)。 --- -### 抑制不可避免的 hydrate 不匹配错误 {/*suppressing-unavoidable-hydration-mismatch-errors*/} +### 抑制不可避免的激活内容不匹配错误 {/*suppressing-unavoidable-hydration-mismatch-errors*/} -如果服务器和客户端之间某个元素的属性或文本内容无法避免不同(比如一个时间戳),你可以禁止 hydrate 警告。 +如果服务器和客户端之间某个元素的属性或文本内容无法避免不同(比如一个时间戳),你可以禁止激活警告。 -使用 `suppressHydrationWarning={true}` 禁止 hydrate 警告: +使用 `suppressHydrationWarning={true}` 禁止激活警告: @@ -192,10 +192,10 @@ export default function App() { -这样,初始渲染过程将呈现与服务器相同的内容,并且避免不匹配的情况,但会在 hydrate 后立即同步并进行额外的渲染。 +这样,初始渲染过程将呈现与服务器相同的内容,并且避免不匹配的情况,但会在激活后立即同步并进行额外的渲染。 -这种方法会使 hydrate 变慢,因为你的组件必须渲染两次,因此要注意在网络不好情况下的用户体验。JavaScript 代码的加载可能比初始 HTML 渲染要晚许多,因此在 hydrate 后立即渲染不同的 UI 可能会让用户感到不适。 +这种方法会使激活变慢,因为你的组件必须渲染两次,因此要注意在网络不好情况下的用户体验。JavaScript 代码的加载可能比初始 HTML 渲染要晚许多,因此在激活后立即渲染不同的 UI 可能会让用户感到不适。 diff --git a/src/content/reference/react-dom/server/renderToNodeStream.md b/src/content/reference/react-dom/server/renderToNodeStream.md index c4409ad0f8..668676b7c3 100644 --- a/src/content/reference/react-dom/server/renderToNodeStream.md +++ b/src/content/reference/react-dom/server/renderToNodeStream.md @@ -76,4 +76,4 @@ app.use('/', (request, response) => { }); ``` -这里的流会将 React 组件初始输出为非交互式 HTML。在客户端上,你需要调用 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) 方法来 hydrate 服务器生成的 HTML 并使其具有交互功能。 +这里的流会将 React 组件初始输出为非交互式 HTML。在客户端上,你需要调用 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) 方法来激活服务器生成的 HTML 并使其具有交互功能。 diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md index 513a4240c9..a40c0892e3 100644 --- a/src/content/reference/react-dom/server/renderToPipeableStream.md +++ b/src/content/reference/react-dom/server/renderToPipeableStream.md @@ -178,7 +178,7 @@ app.use('/', (request, response) => { }); ``` -因为你的服务端正在渲染 ``,所以你还需要在客户端将这个带有 `assetMap` 的组件再渲染一次进行同构,以此避免 hydrate 错误。你可以像下面这样序列化 `assetMap` 之后再传递: +因为你的服务端正在渲染 ``,所以你还需要在客户端将这个带有 `assetMap` 的组件再渲染一次进行同构,以此避免激活错误。你可以像下面这样序列化 `assetMap` 之后再传递: ```js {9-10} // 你需要从你的打包构建工具中获取这个 JSON。 @@ -209,7 +209,7 @@ import App from './App.js'; hydrateRoot(document, ); ``` -这样一来,客户端和服务端都渲染了带有 `assetMap` 属性的 `App`,因此它们是同构的,就不会出现 hydrate 异常错误。 +这样一来,客户端和服务端都渲染了带有 `assetMap` 属性的 `App`,因此它们是同构的,就不会出现激活异常错误。 diff --git a/src/content/reference/react-dom/server/renderToStaticNodeStream.md b/src/content/reference/react-dom/server/renderToStaticNodeStream.md index 75c78fd083..e2462b5e38 100644 --- a/src/content/reference/react-dom/server/renderToStaticNodeStream.md +++ b/src/content/reference/react-dom/server/renderToStaticNodeStream.md @@ -42,11 +42,11 @@ stream.pipe(response); #### 返回值 {/*returns*/} -输出 HTML 字符串的 [Node.js 只读流](https://nodejs.org/api/stream.html#readable-streams),以此法输出的 HTML 不能被客户端 hydrate。 +输出 HTML 字符串的 [Node.js 只读流](https://nodejs.org/api/stream.html#readable-streams),以此法输出的 HTML 不能被客户端激活。 #### 注意 {/*caveats*/} -* `renderToStaticNodeStream` 的输出不能被 hydrate。 +* `renderToStaticNodeStream` 的输出不能被激活。 * 此方法会等待所有 [Suspense边界](/reference/react/Suspense) 完成后才返回输出。 @@ -76,7 +76,7 @@ app.use('/', (request, response) => { -此方法将会渲染 **无法被 hydrate 的非交互式 HTML**。如果你想将 React 用作简单的静态页面生成器,或者渲染完全静态的内容(如电子邮件),那么这将会很有用。 +此方法将会渲染 **无法被激活的非交互式 HTML**。如果你想将 React 用作简单的静态页面生成器,或者渲染完全静态的内容(如电子邮件),那么这将会很有用。 交互式应用程序应该在服务端使用 [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) 并在客户端结合使用 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot)。 diff --git a/src/content/reference/react-dom/server/renderToString.md b/src/content/reference/react-dom/server/renderToString.md index c48077f83c..1d0d419a1f 100644 --- a/src/content/reference/react-dom/server/renderToString.md +++ b/src/content/reference/react-dom/server/renderToString.md @@ -73,7 +73,7 @@ app.use('/', (request, response) => { }); ``` -这将生成你的 React 组件的初始非交互式 HTML 输出。在客户端上,你需要调用 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) 来将服务器生成的 HTML 进行 hydrate 处理,使其具有交互功能。 +这将生成你的 React 组件的初始非交互式 HTML 输出。在客户端上,你需要调用 [`hydrateRoot`](/reference/react-dom/client/hydrateRoot) 来将服务器生成的 HTML 进行激活处理,使其具有交互功能。 diff --git a/src/content/reference/react/useActionState.md b/src/content/reference/react/useActionState.md index 24f9bbcbc3..9abe84e749 100644 --- a/src/content/reference/react/useActionState.md +++ b/src/content/reference/react/useActionState.md @@ -57,7 +57,7 @@ function StatefulForm({}) { form state 是一个只在表单被提交触发 action 后才会被更新的值。如果该表单没有被提交,该值会保持传入的初始值不变。 -如果配合 Server Action 一起使用,`useActionState` 允许与表单交互的服务器的返回值在 hydration 完成前显示。 +如果配合 Server Action 一起使用,`useActionState` 允许与表单交互的服务器的返回值在激活完成前显示。 [请参阅下方更多示例](#usage)。 diff --git a/src/content/reference/react/useEffect.md b/src/content/reference/react/useEffect.md index c0c5913337..fec265a83b 100644 --- a/src/content/reference/react/useEffect.md +++ b/src/content/reference/react/useEffect.md @@ -1753,7 +1753,7 @@ function MyComponent() { } ``` -当应用加载时,用户首先会看到初始渲染的输出。然后,当它加载完并进行 hydrate 时,Effect 将会运行并且将 `didMount` 设置为 `true`,从而触发重新渲染。这将切换到仅在客户端的渲染输出。Effect 不在服务器上运行,这就是 `didMount` 在初始服务器渲染期间为 `false` 的原因。 +当应用加载时,用户首先会看到初始渲染的输出。然后,当它加载完并进行激活时,Effect 将会运行并且将 `didMount` 设置为 `true`,从而触发重新渲染。这将切换到仅在客户端的渲染输出。Effect 不在服务器上运行,这就是 `didMount` 在初始服务器渲染期间为 `false` 的原因。 谨慎使用此模式。请记住,网络连接速度较慢的用户将在相当长的时间内(可能是数秒钟)看到初始内容,因此你不希望对组件的外观进行突兀的更改。在许多情况下,你可以通过使用 CSS 条件性地显示不同的内容来避免这种需要。 diff --git a/src/content/reference/react/useId.md b/src/content/reference/react/useId.md index 93747ff5f7..16497efc2b 100644 --- a/src/content/reference/react/useId.md +++ b/src/content/reference/react/useId.md @@ -179,7 +179,7 @@ input { margin: 5px; } `useId` 的主要好处是 React 确保它能够与 [服务端渲染](/reference/react-dom/server)一起工作。 在服务器渲染期间,你的组件生成输出 HTML。随后,在客户端,[hydration](/reference/react-dom/client/hydrateRoot) 会将你的事件处理程序附加到生成的 HTML 上。由于 hydration,客户端必须匹配服务器输出的 HTML。 -使用递增计数器很难保证这一点,因为客户端组件被 hydrate 处理后的顺序可能与服务器 HTML 的顺序不匹配。调用 `useId` 可以确保 hydration 正常工作,以及服务器和客户端之间的输出相匹配。 +使用递增计数器很难保证这一点,因为客户端组件被激活处理后的顺序可能与服务器 HTML 的顺序不匹配。调用 `useId` 可以确保激活正常工作,以及服务器和客户端之间的输出相匹配。 在 React 内部,调用组件的“父路径”生成 `useId`。这就是为什么如果客户端和服务器的树相同,不管渲染顺序如何,“父路径”始终都匹配。 diff --git a/src/content/reference/react/useLayoutEffect.md b/src/content/reference/react/useLayoutEffect.md index f753a028f2..a48dab6a26 100644 --- a/src/content/reference/react/useLayoutEffect.md +++ b/src/content/reference/react/useLayoutEffect.md @@ -736,6 +736,6 @@ export default function TooltipContainer({ children, x, y, contentRef }) { - 或者,[将你的组件标记为仅在客户端上渲染](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) 。这告诉 React 在服务器渲染期间将其内容替换为最接近的 [``](/reference/react/Suspense) 处的表示加载中的后备方案(例如,一个加载动画或者光效)。 -- 或者,只有在水合之后,使用 `useLayoutEffect` 渲染组件。保留一个初始化为 `false` 的 `isMounted` 布尔状态,并在 `useEffect` 调用中将其设置为 `true`。然后你的渲染逻辑就会像 `return isMounted ? : ` 这样。在服务端和水合过程中,用户将看到 `FallbackContent`,它不应该调用 `useLayoutEffect`。然后 React 将用 `RealContent` 替换它,`RealContent` 仅在客户端上运行并且可以包含 `useLayoutEffect` 调用。 +- 或者,只有在激活之后,使用 `useLayoutEffect` 渲染组件。保留一个初始化为 `false` 的 `isMounted` 布尔状态,并在 `useEffect` 调用中将其设置为 `true`。然后你的渲染逻辑就会像 `return isMounted ? : ` 这样。在服务端和激活过程中,用户将看到 `FallbackContent`,它不应该调用 `useLayoutEffect`。然后 React 将用 `RealContent` 替换它,`RealContent` 仅在客户端上运行并且可以包含 `useLayoutEffect` 调用。 - 如果你将组件与外部数据存储同步,并且依赖 `useLayouteffect` 的原因不同于测量布局,可以考虑使用 [支持服务端渲染](/reference/react/useSyncExternalStore#adding-support-for-server-rendering) 的 [`useSyncExternalStore`](/reference/react/useSyncExternalStore)。 diff --git a/src/content/reference/react/useSyncExternalStore.md b/src/content/reference/react/useSyncExternalStore.md index f29cab2947..466d984a5a 100644 --- a/src/content/reference/react/useSyncExternalStore.md +++ b/src/content/reference/react/useSyncExternalStore.md @@ -45,7 +45,7 @@ function TodosApp() { * `getSnapshot`:一个函数,返回组件需要的 store 中的数据快照。在 store 不变的情况下,重复调用 `getSnapshot` 必须返回同一个值。如果 store 改变,并且返回值也不同了(用 [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) 比较),React 就会重新渲染组件。 -* **可选** `getServerSnapshot`:一个函数,返回 store 中数据的初始快照。它只会在服务端渲染时,以及在客户端进行服务端渲染内容的 hydration 时被用到。快照在服务端与客户端之间必须相同,它通常是从服务端序列化并传到客户端的。如果你忽略此参数,在服务端渲染这个组件会抛出一个错误。 +* **可选** `getServerSnapshot`:一个函数,返回 store 中数据的初始快照。它只会在服务端渲染时,以及在客户端进行服务端渲染内容的激活时被用到。快照在服务端与客户端之间必须相同,它通常是从服务端序列化并传到客户端的。如果你忽略此参数,在服务端渲染这个组件会抛出一个错误。 #### 返回值 {/*returns*/} From cd923d6895c2fc64bc835851a1e3ec2e4234b3aa Mon Sep 17 00:00:00 2001 From: JiashengWu Date: Sat, 7 Sep 2024 01:10:14 +0800 Subject: [PATCH 17/23] Update conferences.md to move the past conferences lower (#7130) --- src/content/community/conferences.md | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index 3e2d760603..c1bd7967f3 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -10,26 +10,6 @@ Do you know of a local React.js conference? Add it here! (Please keep the list c ## Upcoming Conferences {/*upcoming-conferences*/} -### React Nexus 2024 {/*react-nexus-2024*/} -July 04 & 05, 2024. Bangalore, India (In-person event) - -[Website](https://reactnexus.com/) - [Twitter](https://twitter.com/ReactNexus) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in) - -### Chain React 2024 {/*chain-react-2024*/} -July 17-19, 2024. In-person in Portland, OR, USA - -[Website](https://chainreactconf.com) - [Twitter](https://twitter.com/ChainReactConf) - -### The Geek Conf 2024 {/*the-geek-conf-2024*/} -July 25, 2024. In-person in Berlin, Germany + remote (hybrid event) - -[Website](https://thegeekconf.com) - [Twitter](https://twitter.com/thegeekconf) - -### React Rally 2024 🐙 {/*react-rally-2024*/} -August 12-13, 2024. Park City, UT, USA - -[Website](https://reactrally.com) - [Twitter](https://twitter.com/ReactRally) - [YouTube](https://www.youtube.com/channel/UCXBhQ05nu3L1abBUGeQ0ahw) - ### React Universe Conf 2024 {/*react-universe-conf-2024*/} September 5-6, 2024. Wrocław, Poland. @@ -67,6 +47,26 @@ November 29, 2024. In-person in Casablanca, Morocco (hybrid event) ## Past Conferences {/*past-conferences*/} +### React Rally 2024 🐙 {/*react-rally-2024*/} +August 12-13, 2024. Park City, UT, USA + +[Website](https://reactrally.com) - [Twitter](https://twitter.com/ReactRally) - [YouTube](https://www.youtube.com/channel/UCXBhQ05nu3L1abBUGeQ0ahw) + +### The Geek Conf 2024 {/*the-geek-conf-2024*/} +July 25, 2024. In-person in Berlin, Germany + remote (hybrid event) + +[Website](https://thegeekconf.com) - [Twitter](https://twitter.com/thegeekconf) + +### Chain React 2024 {/*chain-react-2024*/} +July 17-19, 2024. In-person in Portland, OR, USA + +[Website](https://chainreactconf.com) - [Twitter](https://twitter.com/ChainReactConf) + +### React Nexus 2024 {/*react-nexus-2024*/} +July 04 & 05, 2024. Bangalore, India (In-person event) + +[Website](https://reactnexus.com/) - [Twitter](https://twitter.com/ReactNexus) - [Linkedin](https://www.linkedin.com/company/react-nexus) - [YouTube](https://www.youtube.com/reactify_in) + ### React Summit 2024 {/*react-summit-2024*/} June 14 & 18, 2024. In-person in Amsterdam, Netherlands + remote (hybrid event) From 2c0627277fb00b2772f636d142aa41e6265aeae6 Mon Sep 17 00:00:00 2001 From: Denis Urban Date: Fri, 6 Sep 2024 20:14:00 +0300 Subject: [PATCH 18/23] add React Day Berlin 2024 to conferences.md (#7137) --- src/content/community/conferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index c1bd7967f3..cd1c87993b 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -45,6 +45,11 @@ November 29, 2024. In-person in Casablanca, Morocco (hybrid event) [Website](https://react-africa.com/) - [Twitter](https://x.com/BeJS_) +### React Day Berlin 2024 {/*react-day-berlin-2024*/} +December 13 & 16, 2024. In-person in Berlin, Germany + remote (hybrid event) + +[Website](https://reactday.berlin/) - [Twitter](https://x.com/reactdayberlin) + ## Past Conferences {/*past-conferences*/} ### React Rally 2024 🐙 {/*react-rally-2024*/} From 60ef58c98253fed44ea63c7b9f54168fb7a4e487 Mon Sep 17 00:00:00 2001 From: Oleg Komissarov Date: Fri, 6 Sep 2024 19:18:07 +0200 Subject: [PATCH 19/23] Update conferences.md, add conference (#7135) * Update conferences.md, add conference * Update conferences.md --------- Co-authored-by: Eli White --- src/content/community/conferences.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/content/community/conferences.md b/src/content/community/conferences.md index cd1c87993b..cd0862b197 100644 --- a/src/content/community/conferences.md +++ b/src/content/community/conferences.md @@ -40,6 +40,11 @@ October 25 & 28, 2024. In-person in London, UK + online (hybrid event) [Website](https://reactadvanced.com/) - [Twitter](https://x.com/reactadvanced) +### React Summit US 2024 {/*react-summit-us-2024*/} +November 19 & 22, 2024. In-person in New York, USA + online (hybrid event) + +[Website](https://reactsummit.us/) - [Twitter](https://twitter.com/reactsummit) - [Videos](https://portal.gitnation.org/) + ### React Africa 2024 {/*react-africa-2024*/} November 29, 2024. In-person in Casablanca, Morocco (hybrid event) From 9aa2e3668da290f92f8997a25f28bd3f58b2a26d Mon Sep 17 00:00:00 2001 From: TinoM <151432822+Tinttori@users.noreply.github.com> Date: Sun, 8 Sep 2024 04:23:09 +0300 Subject: [PATCH 20/23] =?UTF-8?q?Changed=20the=20documentation=20of=20the?= =?UTF-8?q?=20subscribe=20argument=20to=20a=20more=20accurat=E2=80=A6=20(#?= =?UTF-8?q?6691)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Changed the documentation of the subscribe argument to a more accurate one. * Update useSyncExternalStore.md --------- Co-authored-by: Sophie Alpert --- src/content/reference/react/useSyncExternalStore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/useSyncExternalStore.md b/src/content/reference/react/useSyncExternalStore.md index 481690d7ed..05e0c08319 100644 --- a/src/content/reference/react/useSyncExternalStore.md +++ b/src/content/reference/react/useSyncExternalStore.md @@ -41,7 +41,7 @@ It returns the snapshot of the data in the store. You need to pass two functions #### Parameters {/*parameters*/} -* `subscribe`: A function that takes a single `callback` argument and subscribes it to the store. When the store changes, it should invoke the provided `callback`. This will cause the component to re-render. The `subscribe` function should return a function that cleans up the subscription. +* `subscribe`: A function that takes a single `callback` argument and subscribes it to the store. When the store changes, it should invoke the provided `callback`, which will cause React to re-call `getSnapshot` and (if needed) re-render the component. The `subscribe` function should return a function that cleans up the subscription. * `getSnapshot`: A function that returns a snapshot of the data in the store that's needed by the component. While the store has not changed, repeated calls to `getSnapshot` must return the same value. If the store changes and the returned value is different (as compared by [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), React re-renders the component. From 6e3627b6efa0963704ba51af8df3c9cc8f861805 Mon Sep 17 00:00:00 2001 From: Xleine Date: Mon, 9 Sep 2024 14:29:32 +0800 Subject: [PATCH 21/23] fix conflicts --- src/content/reference/react/useSyncExternalStore.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/content/reference/react/useSyncExternalStore.md b/src/content/reference/react/useSyncExternalStore.md index d0c3bdb807..606a909d5c 100644 --- a/src/content/reference/react/useSyncExternalStore.md +++ b/src/content/reference/react/useSyncExternalStore.md @@ -41,11 +41,7 @@ function TodosApp() { #### 参数 {/*parameters*/} -<<<<<<< HEAD -* `subscribe`:一个函数,接收一个单独的 `callback` 参数并把它订阅到 store 上。当 store 发生改变,它应当调用被提供的 `callback`。这会导致组件重新渲染。`subscribe` 函数会返回清除订阅的函数。 -======= -* `subscribe`: A function that takes a single `callback` argument and subscribes it to the store. When the store changes, it should invoke the provided `callback`, which will cause React to re-call `getSnapshot` and (if needed) re-render the component. The `subscribe` function should return a function that cleans up the subscription. ->>>>>>> 9aa2e3668da290f92f8997a25f28bd3f58b2a26d +* `subscribe`:一个函数,接收一个单独的 `callback` 参数并把它订阅到 store 上。当 store 发生改变时会调用提供的 `callback`,这将导致 React 重新调用 `getSnapshot` 并在需要的时候重新渲染组件。`subscribe` 函数会返回清除订阅的函数。 * `getSnapshot`:一个函数,返回组件需要的 store 中的数据快照。在 store 不变的情况下,重复调用 `getSnapshot` 必须返回同一个值。如果 store 改变,并且返回值也不同了(用 [`Object.is`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is) 比较),React 就会重新渲染组件。 From 0f2284ddc8dcab8bbb9b42c04f3c7af94b5b2e73 Mon Sep 17 00:00:00 2001 From: Jan Kassens Date: Mon, 9 Sep 2024 14:28:04 -0700 Subject: [PATCH 22/23] Update copyright footer (#7152) Per https://fburl.com/wiki/ioqmn40p --- src/components/Layout/Footer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx index 5bcf9df984..4e19039ed2 100644 --- a/src/components/Layout/Footer.tsx +++ b/src/components/Layout/Footer.tsx @@ -283,7 +283,7 @@ export function Footer() {
    - ©{new Date().getFullYear()} + Copyright © Meta Platforms, Inc
    Date: Tue, 10 Sep 2024 12:48:08 +0800 Subject: [PATCH 23/23] fix: translation --- src/content/learn/typescript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/learn/typescript.md b/src/content/learn/typescript.md index 30c0ac4be3..f023d54c64 100644 --- a/src/content/learn/typescript.md +++ b/src/content/learn/typescript.md @@ -228,7 +228,7 @@ export default App = AppTSX; - `const initialState: State` 为初始 state 提供类型,并且也将成为 `useReducer` 默认使用的类型。 - `stateReducer(state: State, action: CounterAction): State` 设置了 reducer 函数参数和返回值的类型。 -为 `useReducer` 提供类型参数的更明确的替代方法是在 `initialState` 上设置类型: +除了在 `initialState` 上设置类型外,一个更明确的替代方法是为 `useReducer` 提供一个类型参数: ```ts import { stateReducer, State } from './your-reducer-implementation';