From 16dc27b4dcdc54910b3f2dd8bcbfd574f58b3b67 Mon Sep 17 00:00:00 2001 From: GarlicGo <582149912@qq.com> Date: Tue, 23 May 2023 14:50:16 +0800 Subject: [PATCH 01/17] docs(cn): translate body --- .../learn/lifecycle-of-reactive-effects.md | 418 +++++++++--------- 1 file changed, 209 insertions(+), 209 deletions(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 5d9554a1a9..8e962ae8f1 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -1,37 +1,37 @@ --- -title: 'Lifecycle of Reactive Effects' +title: '响应式 Effect 的生命周期' --- -Effects have a different lifecycle from components. Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it. This cycle can happen multiple times if your Effect depends on props and state that change over time. React provides a linter rule to check that you've specified your Effect's dependencies correctly. This keeps your Effect synchronized to the latest props and state. +Effect 与组件有不同的生命周期。组件可以挂载、更新或卸载。Effect 只能做两件事:开始同步某些东西,然后停止同步它。如果你的 Effect 依赖于随时间变化的 props 和 state,这个循环可能会发生多次。React 提供了一个代码检查规则来检查你是否正确地指定了 Effect 的依赖项,这能够使你的 Effect 与最新的 props 和 state 保持同步。 -- How an Effect's lifecycle is different from a component's lifecycle -- How to think about each individual Effect in isolation -- When your Effect needs to re-synchronize, and why -- How your Effect's dependencies are determined -- What it means for a value to be reactive -- What an empty dependency array means -- How React verifies your dependencies are correct with a linter -- What to do when you disagree with the linter +- Effect 的生命周期与组件的生命周期有何不同 +- 如何独立地考虑每个 Effect +- 什么时候你的 Effect 需要重新同步,为什么 +- 如何确定 Effect 的依赖项 +- 值是响应式的含义是什么 +- 空依赖数组意味着什么 +- React 如何使用检查工具验证你的依赖关系是否正确 +- 当你与代码检查工具产生分歧时,该如何处理 -## The lifecycle of an Effect {/*the-lifecycle-of-an-effect*/} +## 响应式 Effect 的生命周期 {/*the-lifecycle-of-an-effect*/} -Every React component goes through the same lifecycle: +每个 React 组件都经历相同的生命周期: -- A component _mounts_ when it's added to the screen. -- A component _updates_ when it receives new props or state, usually in response to an interaction. -- A component _unmounts_ when it's removed from the screen. +- 当一个组件被添加到屏幕上时,它会进行组件的 **挂载**。 +- 当一个组件接收到新的 props 或 state 时,通常是作为对交互的响应,它会进行组件的 **更新**。 +- 当一个组件从屏幕上移除时,它会进行组件的 **卸载**。 -**It's a good way to think about components, but _not_ about Effects.** Instead, try to think about each Effect independently from your component's lifecycle. An Effect describes how to [synchronize an external system](/learn/synchronizing-with-effects) to the current props and state. As your code changes, synchronization will need to happen more or less often. +**这是一种很好的思考组件的方式,但并不适用于 Effect**。相反,尝试将每个Effect独立思考,与你的组件生命周期无关。Effect 描述了如何将外部系统与当前的 props 和 state 同步。随着你的代码变化,同步的频率可能会增加或减少。 -To illustrate this point, consider this Effect connecting your component to a chat server: +为了说明这一点,考虑下面这个将你的组件连接到聊天服务器的 Effect 示例: ```js const serverUrl = 'https://localhost:1234'; @@ -48,7 +48,7 @@ function ChatRoom({ roomId }) { } ``` -Your Effect's body specifies how to **start synchronizing:** +你的 Effect 的主体部分指定了如何 **开始同步**: ```js {2-3} // ... @@ -60,7 +60,7 @@ Your Effect's body specifies how to **start synchronizing:** // ... ``` -The cleanup function returned by your Effect specifies how to **stop synchronizing:** +你的 Effect 返回的清理函数指定了如何 **停止同步**: ```js {5} // ... @@ -72,141 +72,141 @@ The cleanup function returned by your Effect specifies how to **stop synchronizi // ... ``` -Intuitively, you might think that React would **start synchronizing** when your component mounts and **stop synchronizing** when your component unmounts. However, this is not the end of the story! Sometimes, it may also be necessary to **start and stop synchronizing multiple times** while the component remains mounted. +你可能会直观地认为当组件挂载时React会 **开始同步**,而当组件卸载时会 **停止同步**。然而,事情并没有这么简单!有时,在组件保持挂载状态的同时,可能还需要 **多次开始和停止同步**。 -Let's look at _why_ this is necessary, _when_ it happens, and _how_ you can control this behavior. +让我们来看看 **为什么** 这是必要的、**何时** 会发生以及 **如何** 控制这种行为。 -Some Effects don't return a cleanup function at all. [More often than not,](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) you'll want to return one--but if you don't, React will behave as if you returned an empty cleanup function. +有些 Effect 根本不返回清理函数。[在大多数情况下](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development),你可能希望返回一个清理函数,但如果没有返回,React将表现得好像你返回了一个空的清理函数。 -### Why synchronization may need to happen more than once {/*why-synchronization-may-need-to-happen-more-than-once*/} +### 为什么同步可能需要多次进行 {/*why-synchronization-may-need-to-happen-more-than-once*/} -Imagine this `ChatRoom` component receives a `roomId` prop that the user picks in a dropdown. Let's say that initially the user picks the `"general"` room as the `roomId`. Your app displays the `"general"` chat room: +想象一下,这个 `ChatRoom` 组件接收一个 `roomId` 属性,用户可以在下拉菜单中选择。假设初始时,用户选择了 `"所有"` 作为 `roomId`。你的应用程序会显示 `"所有"` 聊天室: ```js {3} const serverUrl = 'https://localhost:1234'; -function ChatRoom({ roomId /* "general" */ }) { +function ChatRoom({ roomId /* "所有" */ }) { // ... - return

Welcome to the {roomId} room!

; + return

欢迎来到 {roomId} 房间!

; } ``` -After the UI is displayed, React will run your Effect to **start synchronizing.** It connects to the `"general"` room: +在 UI 显示之后,React 将运行你的 Effect 来 **开始同步**。它连接到`"所有"`聊天室: ```js {3,4} -function ChatRoom({ roomId /* "general" */ }) { +function ChatRoom({ roomId /* "所有" */ }) { useEffect(() => { - const connection = createConnection(serverUrl, roomId); // Connects to the "general" room + const connection = createConnection(serverUrl, roomId); // 连接到 "所有" 聊天室 connection.connect(); return () => { - connection.disconnect(); // Disconnects from the "general" room + connection.disconnect(); // 断开与 "所有" 聊天室的连接 }; }, [roomId]); // ... ``` -So far, so good. +到目前为止,一切都很顺利。 -Later, the user picks a different room in the dropdown (for example, `"travel"`). First, React will update the UI: +之后,用户在下拉菜单中选择了不同的房间(例如 `"旅游"`)。首先,React会更新 UI: ```js {1} -function ChatRoom({ roomId /* "travel" */ }) { +function ChatRoom({ roomId /* "旅游" */ }) { // ... - return

Welcome to the {roomId} room!

; + return

欢迎来到 {roomId} 房间!

; } ``` -Think about what should happen next. The user sees that `"travel"` is the selected chat room in the UI. However, the Effect that ran the last time is still connected to the `"general"` room. **The `roomId` prop has changed, so what your Effect did back then (connecting to the `"general"` room) no longer matches the UI.** +思考接下来应该发生什么。用户在界面中看到 `"旅游"` 是当前选定的聊天室。然而,上次运行的 Effect 仍然连接到 `"所有"` 聊天室。**`roomId` 属性已经发生了变化,所以之前 Effect 所做的事情(连接到 `"所有"` 聊天室)不再与 UI 匹配。** -At this point, you want React to do two things: +此时,你希望React执行两个操作: -1. Stop synchronizing with the old `roomId` (disconnect from the `"general"` room) -2. Start synchronizing with the new `roomId` (connect to the `"travel"` room) +1. 停止与旧的 `roomId` 同步(断开与 `"所有"` 聊天室的连接) +2. 开始与新的 `roomId` 同步(连接到 `"旅游"` 聊天室) -**Luckily, you've already taught React how to do both of these things!** Your Effect's body specifies how to start synchronizing, and your cleanup function specifies how to stop synchronizing. All that React needs to do now is to call them in the correct order and with the correct props and state. Let's see how exactly that happens. +**幸运的是,你已经教会了 React 如何执行这两个操作**!你的 Effect 的主体部分指定了如何开始同步,而清理函数指定了如何停止同步。现在,React 只需要按照正确的顺序和正确的 props 和 state 来调用它们。让我们看看具体是如何实现的。 -### How React re-synchronizes your Effect {/*how-react-re-synchronizes-your-effect*/} +### React 如何重新同步你的 Effect {/*how-react-re-synchronizes-your-effect*/} -Recall that your `ChatRoom` component has received a new value for its `roomId` prop. It used to be `"general"`, and now it is `"travel"`. React needs to re-synchronize your Effect to re-connect you to a different room. +回想一下,你的 `ChatRoom` 组件已经接收到了 `roomId` 属性的新值。之前它是 `"所有"`,现在变成了 `"旅游"`。React 需要重新同步你的 Effect,以将你重新连接到不同的聊天室。 -To **stop synchronizing,** React will call the cleanup function that your Effect returned after connecting to the `"general"` room. Since `roomId` was `"general"`, the cleanup function disconnects from the `"general"` room: +为了 **停止同步**,React 将调用你的 Effect 返回的清理函数,该函数在连接到 `"所有"` 聊天室后返回。由于 `roomId` 为 `"所有"`,清理函数将断开与 `"所有"` 聊天室的连接: ```js {6} -function ChatRoom({ roomId /* "general" */ }) { +function ChatRoom({ roomId /* "所有" */ }) { useEffect(() => { - const connection = createConnection(serverUrl, roomId); // Connects to the "general" room + const connection = createConnection(serverUrl, roomId); // 连接到 "所有" 聊天室 connection.connect(); return () => { - connection.disconnect(); // Disconnects from the "general" room + connection.disconnect(); // 断开与 "所有" 聊天室的连接 }; // ... ``` -Then React will run the Effect that you've provided during this render. This time, `roomId` is `"travel"` so it will **start synchronizing** to the `"travel"` chat room (until its cleanup function is eventually called too): +然后,React 将运行你在此渲染期间提供的 Effect。这次,`roomId` 为 `"旅游"`,因此它将 **开始同步** 到 `"旅游"` 聊天室(直到最终也调用了清理函数): ```js {3,4} -function ChatRoom({ roomId /* "travel" */ }) { +function ChatRoom({ roomId /* "旅游" */ }) { useEffect(() => { - const connection = createConnection(serverUrl, roomId); // Connects to the "travel" room + const connection = createConnection(serverUrl, roomId); // 连接到 "旅游" 聊天室 connection.connect(); // ... ``` -Thanks to this, you're now connected to the same room that the user chose in the UI. Disaster averted! +多亏了这一点,现在你已经连接到了用户在 UI 中选择的同一个聊天室。避免了灾难! -Every time after your component re-renders with a different `roomId`, your Effect will re-synchronize. For example, let's say the user changes `roomId` from `"travel"` to `"music"`. React will again **stop synchronizing** your Effect by calling its cleanup function (disconnecting you from the `"travel"` room). Then it will **start synchronizing** again by running its body with the new `roomId` prop (connecting you to the `"music"` room). +每当你的组件使用不同的 `roomId` 重新渲染后,你的 Effect 将重新进行同步。例如,假设用户将 `roomId` 从 `"旅游"` 更改为 `"音乐"`。React 将再次通过调用清理函数 **停止同步** 你的 Effect(断开与 `"旅游"` 聊天室的连接)。然后,它将通过使用新的 `roomId` 属性再次运行 Effect 的主体部分 **开始同步**(将你连接到 `"音乐"` 聊天室)。 -Finally, when the user goes to a different screen, `ChatRoom` unmounts. Now there is no need to stay connected at all. React will **stop synchronizing** your Effect one last time and disconnect you from the `"music"` chat room. +最后,当用户切换到不同的屏幕时,`ChatRoom` 组件将被卸载。现在没有必要保持连接了。React 将 **最后一次停止同步** 你的 Effect,并将你从 `"音乐"` 聊天室断开连接。 -### Thinking from the Effect's perspective {/*thinking-from-the-effects-perspective*/} +### 从 Effect 的角度思考 {/*thinking-from-the-effects-perspective*/} -Let's recap everything that's happened from the `ChatRoom` component's perspective: +让我们总结一下从 `ChatRoom` 组件的角度所发生的一切: -1. `ChatRoom` mounted with `roomId` set to `"general"` -1. `ChatRoom` updated with `roomId` set to `"travel"` -1. `ChatRoom` updated with `roomId` set to `"music"` -1. `ChatRoom` unmounted +1. `ChatRoom` 组件挂载,`roomId` 设置为 `"所有"` +1. `ChatRoom` 组件更新,`roomId` 设置为 `"旅游"` +1. `ChatRoom` 组件更新,`roomId` 设置为 `"音乐"` +1. `ChatRoom` 组件卸载 -During each of these points in the component's lifecycle, your Effect did different things: +在组件生命周期的每个阶段,你的 Effect 执行了不同的操作: -1. Your Effect connected to the `"general"` room -1. Your Effect disconnected from the `"general"` room and connected to the `"travel"` room -1. Your Effect disconnected from the `"travel"` room and connected to the `"music"` room -1. Your Effect disconnected from the `"music"` room +1. 你的 Effect 连接到了 `"所有"` 聊天室 +1. 你的 Effect 断开了与 `"所有"` 聊天室的连接,并连接到了 `"旅游"` 聊天室 +1. 你的 Effect 断开了与 `"旅游"` 聊天室的连接,并连接到了 `"音乐"` 聊天室 +1. 你的 Effect 断开了与 `"音乐"` 聊天室的连接 -Now let's think about what happened from the perspective of the Effect itself: +现在让我们从 Effect 本身的角度来思考所发生的事情: ```js useEffect(() => { - // Your Effect connected to the room specified with roomId... + // 你的 Effect 连接到了通过 roomId 指定的聊天室... const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { - // ...until it disconnected + // ...直到它断开连接 connection.disconnect(); }; }, [roomId]); ``` -This code's structure might inspire you to see what happened as a sequence of non-overlapping time periods: +这段代码的结构可能会让你将所发生的事情看作是一系列不重叠的时间段: -1. Your Effect connected to the `"general"` room (until it disconnected) -1. Your Effect connected to the `"travel"` room (until it disconnected) -1. Your Effect connected to the `"music"` room (until it disconnected) +1. 你的 Effect 连接到了 `"所有"` 聊天室(直到断开连接) +1. 你的 Effect 连接到了 `"旅游"` 聊天室(直到断开连接) +1. 你的 Effect 连接到了 `"音乐"` 聊天室(直到断开连接) -Previously, you were thinking from the component's perspective. When you looked from the component's perspective, it was tempting to think of Effects as "callbacks" or "lifecycle events" that fire at a specific time like "after a render" or "before unmount". This way of thinking gets complicated very fast, so it's best to avoid. +之前,你是从组件的角度思考的。当你从组件的角度思考时,很容易将 Effect 视为在特定时间点触发的“回调函数”或“生命周期事件”,例如“渲染后”或“卸载前”。这种思维方式很快变得复杂,所以最好避免使用。 -**Instead, always focus on a single start/stop cycle at a time. It shouldn't matter whether a component is mounting, updating, or unmounting. All you need to do is to describe how to start synchronization and how to stop it. If you do it well, your Effect will be resilient to being started and stopped as many times as it's needed.** +**相反,始终专注于单个启动/停止周期。无论组件是挂载、更新还是卸载,都不应该有影响。你只需要描述如何开始同步和如何停止。如果你做得好,你的 Effect 将能够在需要时始终具备启动和停止的弹性。** -This might remind you how you don't think whether a component is mounting or updating when you write the rendering logic that creates JSX. You describe what should be on the screen, and React [figures out the rest.](/learn/reacting-to-input-with-state) +这可能会让你想起当你编写创建 JSX 的渲染逻辑时,并不考虑组件是挂载还是更新。你描述的是应该显示在屏幕上的内容,而 React 会 [解决其余的问题](/learn/reacting-to-input-with-state)。 -### How React verifies that your Effect can re-synchronize {/*how-react-verifies-that-your-effect-can-re-synchronize*/} +### React 如何验证你的 Effect 可以重新进行同步 {/*how-react-verifies-that-your-effect-can-re-synchronize*/} -Here is a live example that you can play with. Press "Open chat" to mount the `ChatRoom` component: +这里有一个可以互动的实时示例。点击“打开聊天”来挂载 `ChatRoom` 组件: @@ -222,27 +222,27 @@ function ChatRoom({ roomId }) { connection.connect(); return () => connection.disconnect(); }, [roomId]); - return

Welcome to the {roomId} room!

; + return

欢迎来到 {roomId} 房间!

; } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('所有'); const [show, setShow] = useState(false); return ( <> {show &&
} {show && } @@ -253,13 +253,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // 真正的实现实际上会连接到服务器 return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ 连接到 "' + roomId + '" 房间,位于' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ 断开 "' + roomId + '" 房间,位于' + serverUrl); } }; } @@ -272,49 +272,49 @@ button { margin-left: 10px; }
-Notice that when the component mounts for the first time, you see three logs: +请注意,当组件首次挂载时,你会看到三个日志: -1. `✅ Connecting to "general" room at https://localhost:1234...` *(development-only)* -1. `❌ Disconnected from "general" room at https://localhost:1234.` *(development-only)* -1. `✅ Connecting to "general" room at https://localhost:1234...` +1. `✅ 连接到 "所有" 聊天室,位于 https://localhost:1234...` *(仅限开发环境)* +2. `❌ 从 "所有" 聊天室断开连接,位于 https://localhost:1234.` *(仅限开发环境)* +3. `✅ 连接到 "所有" 聊天室,位于 https://localhost:1234...` -The first two logs are development-only. In development, React always remounts each component once. +前两个日志仅适用于开发环境。在开发环境中,React 总是会重新挂载每个组件一次。 -**React verifies that your Effect can re-synchronize by forcing it to do that immediately in development.** This might remind you of opening a door and closing it an extra time to check if the door lock works. React starts and stops your Effect one extra time in development to check [you've implemented its cleanup well.](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development) +**React 通过在开发环境中立即强制 Effect 重新进行同步来验证其是否能够重新同步**。这可能让你想起打开门并额外关闭它以检查门锁是否有效的情景。React 在开发环境中额外启动和停止 Effect 一次,以检查 [你是否正确实现了它的清理功能](/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)。 -The main reason your Effect will re-synchronize in practice is if some data it uses has changed. In the sandbox above, change the selected chat room. Notice how, when the `roomId` changes, your Effect re-synchronizes. +实际上,Effect 重新进行同步的主要原因是它所使用的某些数据发生了变化。在上面的示例中,更改所选的聊天室。注意当 `roomId` 发生变化时,Effect 会重新进行同步。 -However, there are also more unusual cases in which re-synchronization is necessary. For example, try editing the `serverUrl` in the sandbox above while the chat is open. Notice how the Effect re-synchronizes in response to your edits to the code. In the future, React may add more features that rely on re-synchronization. +然而,还存在其他一些不寻常的情况需要重新进行同步。例如,在上面的示例中,尝试在聊天打开时编辑 `serverUrl`。注意当你修改代码时,Effect会重新进行同步。将来,React可能会添加更多依赖于重新同步的功能。 -### How React knows that it needs to re-synchronize the Effect {/*how-react-knows-that-it-needs-to-re-synchronize-the-effect*/} +### React 如何知道需要重新进行 Effect 的同步 {/*how-react-knows-that-it-needs-to-re-synchronize-the-effect*/} -You might be wondering how React knew that your Effect needed to re-synchronize after `roomId` changes. It's because *you told React* that its code depends on `roomId` by including it in the [list of dependencies:](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies) +你可能想知道 React 是如何知道在 `roomId` 更改后需要重新同步你的 Effect。这是因为 **你告诉了React** 它的代码依赖于 `roomId`,通过将其包含在 [依赖列表](/learn/synchronizing-with-effects#step-2-specify-the-effect-dependencies) 中。 ```js {1,3,8} -function ChatRoom({ roomId }) { // The roomId prop may change over time +function ChatRoom({ roomId }) { // roomId 属性可能会随时间变化。 useEffect(() => { - const connection = createConnection(serverUrl, roomId); // This Effect reads roomId + const connection = createConnection(serverUrl, roomId); // 这个 Effect 读取了 roomId connection.connect(); return () => { connection.disconnect(); }; - }, [roomId]); // So you tell React that this Effect "depends on" roomId + }, [roomId]); // 因此,你告诉 React 这个 Effect "依赖于" roomId // ... ``` -Here's how this works: +下面是它的工作原理: -1. You knew `roomId` is a prop, which means it can change over time. -2. You knew that your Effect reads `roomId` (so its logic depends on a value that may change later). -3. This is why you specified it as your Effect's dependency (so that it re-synchronizes when `roomId` changes). +1. 你知道 `roomId` 是一个 prop,这意味着它可能会随着时间的推移发生变化。 +2. 你知道你的 Effect 读取了 `roomId`(因此其逻辑依赖于可能会在之后发生变化的值)。 +3. 这就是为什么你将其指定为 Effect 的依赖项(以便在 `roomId` 发生变化时重新进行同步)。 -Every time after your component re-renders, React will look at the array of dependencies that you have passed. If any of the values in the array is different from the value at the same spot that you passed during the previous render, React will re-synchronize your Effect. +每次在组件重新渲染后,React 都会查看你传递的依赖项数组。如果数组中的任何值与上一次渲染时在相同位置传递的值不同,React 将重新同步你的 Effect。 -For example, if you passed `["general"]` during the initial render, and later you passed `["travel"]` during the next render, React will compare `"general"` and `"travel"`. These are different values (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), so React will re-synchronize your Effect. On the other hand, if your component re-renders but `roomId` has not changed, your Effect will remain connected to the same room. +例如,如果在初始渲染时传递了 `["所有"]`,然后在下一次渲染时传递了 `["旅游"]`,React 将比较 `"所有"` 和 `"旅游"`。这些是不同的值(使用 [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) 进行比较),因此 React 将重新同步你的 Effect。另一方面,如果你的组件重新渲染但 `roomId` 没有发生变化,你的 Effect 将继续连接到相同的房间。 -### Each Effect represents a separate synchronization process {/*each-effect-represents-a-separate-synchronization-process*/} +### 每个 Effect 表示一个独立的同步过程。 {/*each-effect-represents-a-separate-synchronization-process*/} -Resist adding unrelated logic to your Effect only because this logic needs to run at the same time as an Effect you already wrote. For example, let's say you want to send an analytics event when the user visits the room. You already have an Effect that depends on `roomId`, so you might feel tempted to add the analytics call there: +抵制将与 Effect 无关的逻辑添加到已经编写的 Effect 中,仅仅因为这些逻辑需要与 Effect 同时运行。例如,假设你想在用户访问房间时发送一个分析事件。你已经有一个依赖于 `roomId` 的 Effect,所以你可能会感到诱惑将分析调用添加到那里: ```js {3} function ChatRoom({ roomId }) { @@ -330,7 +330,7 @@ function ChatRoom({ roomId }) { } ``` -But imagine you later add another dependency to this Effect that needs to re-establish the connection. If this Effect re-synchronizes, it will also call `logVisit(roomId)` for the same room, which you did not intend. Logging the visit **is a separate process** from connecting. Write them as two separate Effects: +但是想象一下,如果以后你给这个 Effect 添加了另一个需要重新建立连接的依赖项。如果这个 Effect 重新进行同步,它将为相同的房间调用 `logVisit(roomId)`,而这不是你的意图。记录访问行为是 **一个独立的过程**,与连接不同。将它们作为两个单独的 Effects 编写: ```js {2-4} function ChatRoom({ roomId }) { @@ -346,13 +346,13 @@ function ChatRoom({ roomId }) { } ``` -**Each Effect in your code should represent a separate and independent synchronization process.** +**代码中的每个 Effect 应该代表一个独立的同步过程。.** -In the above example, deleting one Effect wouldn’t break the other Effect's logic. This is a good indication that they synchronize different things, and so it made sense to split them up. On the other hand, if you split up a cohesive piece of logic into separate Effects, the code may look "cleaner" but will be [more difficult to maintain.](/learn/you-might-not-need-an-effect#chains-of-computations) This is why you should think whether the processes are same or separate, not whether the code looks cleaner. +在上面的示例中,删除一个 Effect 不会影响另一个 Effect 的逻辑。这表明它们同步不同的内容,因此将它们拆分开是有意义的。另一方面,如果将一个内聚的逻辑拆分成多个独立的 Effects,代码可能会看起来更加“清晰”,但[维护起来会更加困难](/learn/you-might-not-need-an-effect#chains-of-computations)。这就是为什么你应该考虑这些过程是相同还是独立的,而不是只考虑代码是否看起来更整洁。 -## Effects "react" to reactive values {/*effects-react-to-reactive-values*/} +## Effect 会“响应”于响应式值 {/*effects-react-to-reactive-values*/} -Your Effect reads two variables (`serverUrl` and `roomId`), but you only specified `roomId` as a dependency: +你的 Effect 读取了两个变量(`serverUrl` 和 `roomId`),但是你只将 `roomId` 指定为依赖项: ```js {5,10} const serverUrl = 'https://localhost:1234'; @@ -369,32 +369,32 @@ function ChatRoom({ roomId }) { } ``` -Why doesn't `serverUrl` need to be a dependency? +为什么 `serverUrl` 不需要作为依赖项呢? -This is because the `serverUrl` never changes due to a re-render. It's always the same no matter how many times the component re-renders and why. Since `serverUrl` never changes, it wouldn't make sense to specify it as a dependency. After all, dependencies only do something when they change over time! +这是因为 `serverUrl` 永远不会因为重新渲染而发生变化。无论组件重新渲染多少次以及原因是什么,`serverUrl` 都保持不变。既然 `serverUrl` 从不变化,将其指定为依赖项就没有意义。毕竟,依赖项只有在随时间变化时才会起作用! -On the other hand, `roomId` may be different on a re-render. **Props, state, and other values declared inside the component are _reactive_ because they're calculated during rendering and participate in the React data flow.** +另一方面,`roomId` 在重新渲染时可能会不同。**在组件内部声明的 props、state 和其他值都是 _响应式_ 的,因为它们是在渲染过程中计算的,并参与了 React 的数据流。** -If `serverUrl` was a state variable, it would be reactive. Reactive values must be included in dependencies: +如果 `serverUrl` 是一个状态变量,那么它就是响应式的。响应式值必须包含在依赖项中: ```js {2,5,10} -function ChatRoom({ roomId }) { // Props change over time - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // State may change over time +function ChatRoom({ roomId }) { // Props 随时间变化 + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // State 可能随时间变化 useEffect(() => { - const connection = createConnection(serverUrl, roomId); // Your Effect reads props and state + const connection = createConnection(serverUrl, roomId); // 你的 Effect 读取 props 和 state connection.connect(); return () => { connection.disconnect(); }; - }, [roomId, serverUrl]); // So you tell React that this Effect "depends on" on props and state + }, [roomId, serverUrl]); // 因此,你告诉 React 这个 Effect "依赖于" props 和 state // ... } ``` -By including `serverUrl` as a dependency, you ensure that the Effect re-synchronizes after it changes. +通过将 `serverUrl` 包含在依赖项中,你确保 Effect 在其发生变化后重新同步。 -Try changing the selected chat room or edit the server URL in this sandbox: +尝试在此沙盒中更改所选的聊天室或编辑服务器 URL: @@ -414,30 +414,30 @@ function ChatRoom({ roomId }) { return ( <> -

Welcome to the {roomId} room!

+

欢迎来到 {roomId} 房间!

); } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('所有'); return ( <>
@@ -449,13 +449,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // 真正的实现实际上会连接到服务器 return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ 连接到 "' + roomId + '" 房间,位于' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ 断开 "' + roomId + '" 房间,位于' + serverUrl); } }; } @@ -468,15 +468,15 @@ button { margin-left: 10px; }
-Whenever you change a reactive value like `roomId` or `serverUrl`, the Effect re-connects to the chat server. +无论何时你更改一个类似 `roomId` 或 `serverUrl` 的响应式值,该 Effect 都会重新连接到聊天服务器。 -### What an Effect with empty dependencies means {/*what-an-effect-with-empty-dependencies-means*/} +### 一个没有依赖项的 Effect 的含义 {/*what-an-effect-with-empty-dependencies-means*/} -What happens if you move both `serverUrl` and `roomId` outside the component? +如果将 `serverUrl` 和 `roomId` 都移出组件会发生什么? ```js {1,2} const serverUrl = 'https://localhost:1234'; -const roomId = 'general'; +const roomId = '所有'; function ChatRoom() { useEffect(() => { @@ -485,14 +485,14 @@ function ChatRoom() { return () => { connection.disconnect(); }; - }, []); // ✅ All dependencies declared + }, []); // ✅ 声明的所有依赖 // ... } ``` -Now your Effect's code does not use *any* reactive values, so its dependencies can be empty (`[]`). +现在你的 Effect 的代码不使用任何响应式值,因此它的依赖可以是空的 (`[]`)。 -Thinking from the component's perspective, the empty `[]` dependency array means this Effect connects to the chat room only when the component mounts, and disconnects only when the component unmounts. (Keep in mind that React would still [re-synchronize it an extra time](#how-react-verifies-that-your-effect-can-re-synchronize) in development to stress-test your logic.) +从组件的角度来看,空的 `[]` 依赖数组意味着这个 Effect 仅在组件挂载时连接到聊天室,并在组件卸载时断开连接。(请记住,在开发环境中,React 仍会 [额外执行一次](#how-react-verifies-that-your-effect-can-re-synchronize) 来对你的逻辑进行压力测试。) @@ -502,7 +502,7 @@ import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; const serverUrl = 'https://localhost:1234'; -const roomId = 'general'; +const roomId = '所有'; function ChatRoom() { useEffect(() => { @@ -510,7 +510,7 @@ function ChatRoom() { connection.connect(); return () => connection.disconnect(); }, []); - return

Welcome to the {roomId} room!

; + return

欢迎来到 {roomId} 房间!

; } export default function App() { @@ -518,7 +518,7 @@ export default function App() { return ( <> {show &&
} {show && } @@ -529,13 +529,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // 真正的实现实际上会连接到服务器 return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ 连接到 "' + roomId + '" 房间,位于' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ 断开 "' + roomId + '" 房间,位于' + serverUrl); } }; } @@ -548,52 +548,52 @@ button { margin-left: 10px; }
-However, if you [think from the Effect's perspective,](#thinking-from-the-effects-perspective) you don't need to think about mounting and unmounting at all. What's important is you've specified what your Effect does to start and stop synchronizing. Today, it has no reactive dependencies. But if you ever want the user to change `roomId` or `serverUrl` over time (and they would become reactive), your Effect's code won't change. You will only need to add them to the dependencies. +然而,如果你 [从 Effect 的角度思考](#thinking-from-the-effects-perspective),你根本不需要考虑挂载和卸载。重要的是,你已经指定了你的 Effect 如何开始和停止同步。目前,它没有任何响应式依赖。但是,如果你希望用户随时间改变 `roomId` 或 `serverUrl`(它们将变为响应式),你的 Effect 的代码不需要改变。你只需要将它们添加到依赖项中即可。 -### All variables declared in the component body are reactive {/*all-variables-declared-in-the-component-body-are-reactive*/} +### 在组件主体中声明的所有变量都是响应式的 {/*all-variables-declared-in-the-component-body-are-reactive*/} -Props and state aren't the only reactive values. Values that you calculate from them are also reactive. If the props or state change, your component will re-render, and the values calculated from them will also change. This is why all variables from the component body used by the Effect should be in the Effect dependency list. +Props 和 state 并不是唯一的响应式值。从它们计算出的值也是响应式的。如果 props 或 state 发生变化,你的组件将重新渲染,从中计算出的值也会随之改变。这就是为什么 Effect 使用的组件主体中的所有变量都应该在依赖列表中。 -Let's say that the user can pick a chat server in the dropdown, but they can also configure a default server in settings. Suppose you've already put the settings state in a [context](/learn/scaling-up-with-reducer-and-context) so you read the `settings` from that context. Now you calculate the `serverUrl` based on the selected server from props and the default server: +假设用户可以在下拉菜单中选择聊天服务器,但他们还可以在设置中配置默认服务器。假设你已经将设置状态放入了一个 [上下文](/learn/scaling-up-with-reducer-and-context),因此你从该上下文中读取 `settings`。现在,你可以根据 props 中选择的服务器和默认服务器来计算 `serverUrl`: ```js {3,5,10} -function ChatRoom({ roomId, selectedServerUrl }) { // roomId is reactive - const settings = useContext(SettingsContext); // settings is reactive - const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl is reactive +function ChatRoom({ roomId, selectedServerUrl }) { // roomId 是响应式的 + const settings = useContext(SettingsContext); // settings 是响应式的 + const serverUrl = selectedServerUrl ?? settings.defaultServerUrl; // serverUrl 是响应式的 useEffect(() => { - const connection = createConnection(serverUrl, roomId); // Your Effect reads roomId and serverUrl + const connection = createConnection(serverUrl, roomId); // 你的 Effect 读取了 roomId 和 serverUrl connection.connect(); return () => { connection.disconnect(); }; - }, [roomId, serverUrl]); // So it needs to re-synchronize when either of them changes! + }, [roomId, serverUrl]); // 因此,当它们中的任何一个发生变化时,它需要重新同步! // ... } ``` -In this example, `serverUrl` is not a prop or a state variable. It's a regular variable that you calculate during rendering. But it's calculated during rendering, so it can change due to a re-render. This is why it's reactive. +在这个例子中,`serverUrl` 不是一个 prop 或 state 变量。它是你在渲染过程中计算的普通变量。但是它是在渲染过程中计算的,所以它可能会因为重新渲染而改变。这就是为什么它是响应式的。 -**All values inside the component (including props, state, and variables in your component's body) are reactive. Any reactive value can change on a re-render, so you need to include reactive values as Effect's dependencies.** +**组件内部的所有值(包括 props、state 和组件体内的变量)都是响应式的。任何响应式值都可以在重新渲染时发生变化,所以你需要将响应式值包括在 Effect 的依赖项中。** -In other words, Effects "react" to all values from the component body. +换句话说,Effect 对组件体内的所有值都会“react”。 -#### Can global or mutable values be dependencies? {/*can-global-or-mutable-values-be-dependencies*/} +#### 全局变量或可变值可以作为依赖项吗? {/*can-global-or-mutable-values-be-dependencies*/} -Mutable values (including global variables) aren't reactive. +可变值(包括全局变量)不是响应式的。 -**A mutable value like [`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) can't be a dependency.** It's mutable, so it can change at any time completely outside of the React rendering data flow. Changing it wouldn't trigger a re-render of your component. Therefore, even if you specified it in the dependencies, React *wouldn't know* to re-synchronize the Effect when it changes. This also breaks the rules of React because reading mutable data during rendering (which is when you calculate the dependencies) breaks [purity of rendering.](/learn/keeping-components-pure) Instead, you should read and subscribe to an external mutable value with [`useSyncExternalStore`.](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store) +例如,像 [`location.pathname`](https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname) 这样的可变值不能作为依赖项。它是可变的,因此可以在 React 渲染数据流之外的任何时间发生变化。更改它不会触发组件的重新渲染。因此,即使你在依赖项中指定了它,React 也无法知道在其更改时重新同步 Effect。这也违反了 React 的规则,因为在渲染过程中读取可变数据(即在计算依赖项时)会破坏 [纯粹的渲染](/learn/keeping-components-pure)。相反,你应该使用 [`useSyncExternalStore`](/learn/you-might-not-need-an-effect#subscribing-to-an-external-store) 来读取和订阅外部可变值。 -**A mutable value like [`ref.current`](/reference/react/useRef#reference) or things you read from it also can't be a dependency.** The ref object returned by `useRef` itself can be a dependency, but its `current` property is intentionally mutable. It lets you [keep track of something without triggering a re-render.](/learn/referencing-values-with-refs) But since changing it doesn't trigger a re-render, it's not a reactive value, and React won't know to re-run your Effect when it changes. +**另外,像 [`ref.current`](/reference/react/useRef#reference) 或从中读取的值也不能作为依赖项。`useRef` 返回的 ref 对象本身可以作为依赖项**,但其 `current` 属性是有意可变的。它允许你 [跟踪某些值而不触发重新渲染](/learn/referencing-values-with-refs)。但由于更改它不会触发重新渲染,它不是一个响应式值,React 不会知道在其更改时重新运行你的 Effect。 -As you'll learn below on this page, a linter will check for these issues automatically. +正如你将在本页面下面学到的那样,检查工具将自动检查这些问题。 -### React verifies that you specified every reactive value as a dependency {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/} +### React 会验证你是否将每个响应式值都作为依赖项进行了指定 {/*react-verifies-that-you-specified-every-reactive-value-as-a-dependency*/} -If your linter is [configured for React,](/learn/editor-setup#linting) it will check that every reactive value used by your Effect's code is declared as its dependency. For example, this is a lint error because both `roomId` and `serverUrl` are reactive: +如果你的检查工具 [配置了 React](/learn/editor-setup#linting),它将检查你的 Effect 代码中使用的每个响应式值是否已声明为其依赖项。例如,以下示例是一个 lint 错误,因为 `roomId` 和 `serverUrl` 都是响应式的: @@ -601,42 +601,42 @@ If your linter is [configured for React,](/learn/editor-setup#linting) it will c import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; -function ChatRoom({ roomId }) { // roomId is reactive - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl is reactive +function ChatRoom({ roomId }) { // roomId 是响应式的 + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl 是响应式的 useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); - }, []); // <-- Something's wrong here! + }, []); // <-- 这里有些问题! return ( <> -

Welcome to the {roomId} room!

+

欢迎来到 {roomId} 房间!

); } export default function App() { - const [roomId, setRoomId] = useState('general'); + const [roomId, setRoomId] = useState('所有'); return ( <>
@@ -648,13 +648,13 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // A real implementation would actually connect to the server + // 真正的实现实际上会连接到服务器 return { connect() { - console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...'); + console.log('✅ 连接到 "' + roomId + '" 房间,位于' + serverUrl + '...'); }, disconnect() { - console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl); + console.log('❌ 断开 "' + roomId + '" 房间,位于' + serverUrl); } }; } @@ -667,41 +667,41 @@ button { margin-left: 10px; }
-This may look like a React error, but really React is pointing out a bug in your code. Both `roomId` and `serverUrl` may change over time, but you're forgetting to re-synchronize your Effect when they change. You will remain connected to the initial `roomId` and `serverUrl` even after the user picks different values in the UI. +这可能看起来像是一个 React 错误,但实际上 React 是在指出你代码中的一个 bug。`roomId` 和 `serverUrl` 都可能随时间改变,但你忘记了在它们改变时重新同步你的 Effect。即使用户在 UI 中选择了不同的值,你仍然保持连接到初始的 `roomId` 和 `serverUrl`。 -To fix the bug, follow the linter's suggestion to specify `roomId` and `serverUrl` as dependencies of your Effect: +要修复这个 bug,请按照检查工具的建议将 `roomId` 和 `serverUrl` 作为你的 Effect 的依赖进行指定: ```js {9} -function ChatRoom({ roomId }) { // roomId is reactive - const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl is reactive +function ChatRoom({ roomId }) { // roomId 是响应式的 + const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // serverUrl 是响应式的 useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; - }, [serverUrl, roomId]); // ✅ All dependencies declared + }, [serverUrl, roomId]); // ✅ 声明的所有依赖 // ... } ``` -Try this fix in the sandbox above. Verify that the linter error is gone, and the chat re-connects when needed. +在上面的沙盒中尝试这个修复方法。验证一下是否消除了检查工具的错误,并且在需要时聊天会重新连接。 -In some cases, React *knows* that a value never changes even though it's declared inside the component. For example, the [`set` function](/reference/react/useState#setstate) returned from `useState` and the ref object returned by [`useRef`](/reference/react/useRef) are *stable*--they are guaranteed to not change on a re-render. Stable values aren't reactive, so you may omit them from the list. Including them is allowed: they won't change, so it doesn't matter. +在某些情况下,React **知道** 一个值永远不会改变,即使它在组件内部声明。例如,从 `useState` 返回的 `set` 函数和从 `useRef`返回的 ref 对象是 **稳定的** ——它们保证在重新渲染时不会改变。稳定值不是响应式的,因此你可以从列表中省略它们。包括它们是允许的:它们不会改变,所以无关紧要。 -### What to do when you don't want to re-synchronize {/*what-to-do-when-you-dont-want-to-re-synchronize*/} +### 当你不想进行重新同步时该怎么办 {/*what-to-do-when-you-dont-want-to-re-synchronize*/} -In the previous example, you've fixed the lint error by listing `roomId` and `serverUrl` as dependencies. +在上一个示例中,你通过将 `roomId` 和 `serverUrl` 列为依赖项来修复了 lint 错误。 -**However, you could instead "prove" to the linter that these values aren't reactive values,** i.e. that they *can't* change as a result of a re-render. For example, if `serverUrl` and `roomId` don't depend on rendering and always have the same values, you can move them outside the component. Now they don't need to be dependencies: +然而,你可以通过向检查工具“证明”这些值不是响应式值,即它们 **不会** 因为重新渲染而改变。例如,如果 `serverUrl` 和 `roomId` 不依赖于渲染并且始终具有相同的值,你可以将它们移到组件外部。现在它们不需要成为依赖项: ```js {1,2,11} -const serverUrl = 'https://localhost:1234'; // serverUrl is not reactive -const roomId = 'general'; // roomId is not reactive +const serverUrl = 'https://localhost:1234'; // serverUrl 不是响应式的 +const roomId = '所有'; // roomId 不是响应式的 function ChatRoom() { useEffect(() => { @@ -710,66 +710,66 @@ function ChatRoom() { return () => { connection.disconnect(); }; - }, []); // ✅ All dependencies declared + }, []); // ✅ 声明的所有依赖 // ... } ``` -You can also move them *inside the Effect.* They aren't calculated during rendering, so they're not reactive: +你也可以将它们 **移动到 Effect 内部**。它们不是在渲染过程中计算的,因此它们不是响应式的: ```js {3,4,10} function ChatRoom() { useEffect(() => { - const serverUrl = 'https://localhost:1234'; // serverUrl is not reactive - const roomId = 'general'; // roomId is not reactive + const serverUrl = 'https://localhost:1234'; // serverUrl 不是响应式的 + const roomId = '所有'; // roomId 不是响应式的 const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; - }, []); // ✅ All dependencies declared + }, []); // ✅ 声明的所有依赖 // ... } ``` -**Effects are reactive blocks of code.** They re-synchronize when the values you read inside of them change. Unlike event handlers, which only run once per interaction, Effects run whenever synchronization is necessary. +**Effect 是一段响应式的代码块**。它们在你读取的值发生变化时重新进行同步。与事件处理程序不同,事件处理程序只在每次交互时运行一次,而 Effect 则在需要进行同步时运行。 -**You can't "choose" your dependencies.** Your dependencies must include every [reactive value](#all-variables-declared-in-the-component-body-are-reactive) you read in the Effect. The linter enforces this. Sometimes this may lead to problems like infinite loops and to your Effect re-synchronizing too often. Don't fix these problems by suppressing the linter! Here's what to try instead: +**你不能“选择”你的依赖项**。你的依赖项必须包括你在 Effect 中读取的每个 [响应式值](#all-variables-declared-in-the-component-body-are-reactive)。代码检查工具会强制执行此规则。有时,这可能会导致出现无限循环的问题,或者你的 Effect 过于频繁地重新进行同步。不要通过禁用代码检查来解决这些问题!下面是一些解决方案: -* **Check that your Effect represents an independent synchronization process.** If your Effect doesn't synchronize anything, [it might be unnecessary.](/learn/you-might-not-need-an-effect) If it synchronizes several independent things, [split it up.](#each-effect-represents-a-separate-synchronization-process) +* **检查你的 Effect 是否表示了独立的同步过程**。如果你的 Effect 没有进行任何同步操作,[可能是不必要的](/learn/you-might-not-need-an-effect)。如果它同时进行了几个独立的同步操作,[将其拆分为多个 Effect](#each-effect-represents-a-separate-synchronization-process)。 -* **If you want to read the latest value of props or state without "reacting" to it and re-synchronizing the Effect,** you can split your Effect into a reactive part (which you'll keep in the Effect) and a non-reactive part (which you'll extract into something called an _Effect Event_). [Read about separating Events from Effects.](/learn/separating-events-from-effects) +* **如果你想读取 props 或 state 的最新值,但又不想对其做出反应并重新同步 Effect**,你可以将 Effect 拆分为具有反应性的部分(保留在 Effect 中)和非反应性的部分(提取为名为 "Effect Event" 的内容)。[阅读关于将事件与效果分离的内容](/learn/separating-events-from-effects)。 -* **Avoid relying on objects and functions as dependencies.** If you create objects and functions during rendering and then read them from an Effect, they will be different on every render. This will cause your Effect to re-synchronize every time. [Read more about removing unnecessary dependencies from Effects.](/learn/removing-effect-dependencies) +* **避免将对象和函数作为依赖项**。如果你在渲染过程中创建对象和函数,然后在 Effect 中读取它们,它们将在每次渲染时都不同。这将导致你的 Effect 每次都重新同步。[阅读有关从 Effect 中删除不必要依赖项的更多内容](/learn/removing-effect-dependencies)。 -The linter is your friend, but its powers are limited. The linter only knows when the dependencies are *wrong*. It doesn't know *the best* way to solve each case. If the linter suggests a dependency, but adding it causes a loop, it doesn't mean the linter should be ignored. You need to change the code inside (or outside) the Effect so that that value isn't reactive and doesn't *need* to be a dependency. +检查工具是你的朋友,但它们的能力是有限的。检查工具只知道依赖关系是否 **错误**。它并不知道每种情况下的 **最佳** 解决方法。如果静态代码分析工具建议添加某个依赖关系,但添加该依赖关系会导致循环,这并不意味着应该忽略静态代码分析工具。你需要修改 Effect 内部(或外部)的代码,使得该值不是响应式的,也不 **需要** 成为依赖项。 -If you have an existing codebase, you might have some Effects that suppress the linter like this: +如果你有一个现有的代码库,可能会有一些像这样禁用了检查工具的 Effect: ```js {3-4} useEffect(() => { // ... - // 🔴 Avoid suppressing the linter like this: + // 🔴 避免这样禁用静态代码分析工具: // eslint-ignore-next-line react-hooks/exhaustive-deps }, []); ``` -On the [next](/learn/separating-events-from-effects) [pages](/learn/removing-effect-dependencies), you'll learn how to fix this code without breaking the rules. It's always worth fixing! +在 [下一页](/learn/separating-events-from-effects) 和 [之后的页面](/learn/removing-effect-dependencies) 中,你将学习如何修复这段代码,而不违反规则。修复代码总是值得的! -- Components can mount, update, and unmount. -- Each Effect has a separate lifecycle from the surrounding component. -- Each Effect describes a separate synchronization process that can *start* and *stop*. -- When you write and read Effects, think from each individual Effect's perspective (how to start and stop synchronization) rather than from the component's perspective (how it mounts, updates, or unmounts). -- Values declared inside the component body are "reactive". -- Reactive values should re-synchronize the Effect because they can change over time. -- The linter verifies that all reactive values used inside the Effect are specified as dependencies. -- All errors flagged by the linter are legitimate. There's always a way to fix the code to not break the rules. +- 组件可以挂载、更新和卸载。 +- 每个 Effect 与周围组件有着独立的生命周期。 +- 每个 Effect 描述了一个独立的同步过程,可以 **开始** 和 **停止**。 +- 在编写和读取 Effect 时,要独立地考虑每个 Effect(如何开始和停止同步),而不是从组件的角度思考(如何挂载、更新或卸载)。 +- 在组件主体内声明的值是“响应式”的。 +- 响应式值应该重新进行同步 Effect,因为它们可以随着时间的推移而发生变化。 +- 检查工具验证在 Effect 内部使用的所有响应式值都被指定为依赖项。 +- 检查工具标记的所有错误都是合理的。总是有一种方法可以修复代码,同时不违反规则。 From 0b045a5147af4a21d012c4acd95b83f3814823ac Mon Sep 17 00:00:00 2001 From: NewVo1d <89888306+NewVo1d@users.noreply.github.com> Date: Tue, 23 May 2023 15:32:32 +0800 Subject: [PATCH 02/17] docs(cn): translate challenges (#1) --- .../learn/lifecycle-of-reactive-effects.md | 368 +++++++++--------- 1 file changed, 183 insertions(+), 185 deletions(-) diff --git a/src/content/learn/lifecycle-of-reactive-effects.md b/src/content/learn/lifecycle-of-reactive-effects.md index 8e962ae8f1..797ecbdd59 100644 --- a/src/content/learn/lifecycle-of-reactive-effects.md +++ b/src/content/learn/lifecycle-of-reactive-effects.md @@ -226,7 +226,7 @@ function ChatRoom({ roomId }) { } export default function App() { - const [roomId, setRoomId] = useState('所有'); + const [roomId, setRoomId] = useState('综合'); const [show, setShow] = useState(false); return ( <> @@ -253,7 +253,7 @@ export default function App() { ```js chat.js export function createConnection(serverUrl, roomId) { - // 真正的实现实际上会连接到服务器 + // 实际的实现将会连接到服务器 return { connect() { console.log('✅ 连接到 "' + roomId + '" 房间,位于' + serverUrl + '...'); @@ -426,7 +426,7 @@ function ChatRoom({ roomId }) { } export default function App() { - const [roomId, setRoomId] = useState('所有'); + const [roomId, setRoomId] = useState('综合'); return ( <>