From 7f254e1bcfec1fcba6c2e553548ca6b64514be1e Mon Sep 17 00:00:00 2001 From: yueming Date: Mon, 18 Feb 2019 00:22:28 +0800 Subject: [PATCH 01/51] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E4=BA=86=20effect=20ho?= =?UTF-8?q?ok=20=E7=9A=84=E5=89=8D=E5=8D=8A=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/hooks-effect.md | 53 +++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 64b32476ef..e79546bc05 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -1,14 +1,16 @@ --- id: hooks-state -title: Using the Effect Hook +title: 使用 Effect Hook permalink: docs/hooks-effect.html next: hooks-rules.html prev: hooks-intro.html --- *Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class. +*Hooks* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他 React 功能。 The *Effect Hook* lets you perform side effects in function components: +*Effect Hook* 让你可以在函数定义组件中执行一些副作用(side effect)操作 ```js{1,6-10} import React, { useState, useEffect } from 'react'; @@ -34,24 +36,34 @@ function Example() { ``` This snippet is based on the [counter example from the previous page](/docs/hooks-state.html), but we added a new feature to it: we set the document title to a custom message including the number of clicks. +这段代码是基于[上一页中的计数器示例](/docs/hooks-state.html)修改的,我们为计数器增加了一个小功能:将 document 的 title 设置为一句包含了点击次数的消息 Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you're used to calling these operations "side effects" (or just "effects"), you've likely performed them in your components before. +数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你之前是否将他们称为"副作用"(可能就是你想要的效果),应该都在组件中使用过了。 + >Tip > >If you're familiar with React class lifecycle methods, you can think of `useEffect` Hook as `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` combined. +>如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hooks 看做 `componentDidMount` 和 `componentWillUnmount` 这两个函数的结合 There are two common kinds of side effects in React components: those that don't require cleanup, and those that do. Let's look at this distinction in more detail. +通常来说在 React 组件中有两种副作用操作:须要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 ## Effects Without Cleanup {#effects-without-cleanup} +## 无需清理的 Effect {#effects-without-cleanup} Sometimes, we want to **run some additional code after React has updated the DOM.** Network requests, manual DOM mutations, and logging are common examples of effects that don't require a cleanup. We say that because we can run them and immediately forget about them. Let's compare how classes and Hooks let us express such side effects. +有时候,我们只是想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清理的操作。因为我们在执行完这些操作之后,就可以忽略他们了。 ### Example Using Classes {#example-using-classes} +### 使用 class 的示例 {#example-using-classes} In React class components, the `render` method itself shouldn't cause side effects. It would be too early -- we typically want to perform our effects *after* React has updated the DOM. +在 React 的类定义组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。 This is why in React classes, we put side effects into `componentDidMount` and `componentDidUpdate`. Coming back to our example, here is a React counter class component that updates the document title right after React makes changes to the DOM: +这就是为什么在 React 类中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器类定义组件。它在 Ract 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 ```js{9-15} class Example extends React.Component { @@ -84,14 +96,19 @@ class Example extends React.Component { ``` Note how **we have to duplicate the code between these two lifecycle methods in class.** +为什么**在这个 class 中我们需要在两个生命周期函数中写重复的代码** This is because in many cases we want to perform the same side effect regardless of whether the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render -- but React class components don't have a method like this. We could extract a separate method but we would still have to call it in two places. +这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 ———— 但 React 的类定义组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 Now let's see how we can do the same with the `useEffect` Hook. +现在让我们来看看如何使用 `useEffect` Hook 做同样的事情。 ### Example Using Hooks {#example-using-hooks} +### 使用 Hooks 的示例 {#example-using-hooks} We've already seen this example at the top of this page, but let's take a closer look at it: +我们已经在本页顶部看到了这个示例,但让我们再仔细看看它: ```js{1,6-8} import React, { useState, useEffect } from 'react'; @@ -115,14 +132,19 @@ function Example() { ``` **What does `useEffect` do?** By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we'll refer to it as our "effect"), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API. +**`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 您的组件需要在渲染后执行某些操作。React会记住你传递的函数(我们将它称之为 "effect"),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,另外我们也可以执行数据获取或调用其他命令式 API。 **Why is `useEffect` called inside a component?** Placing `useEffect` inside the component lets us access the `count` state variable (or any props) right from the effect. We don't need a special API to read it -- it's already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution. +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 他已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 **Does `useEffect` run after every render?** Yes! By default, it runs both after the first render *and* after every update. (We will later talk about [how to customize this](#tip-optimizing-performance-by-skipping-effects).) Instead of thinking in terms of "mounting" and "updating", you might find it easier to think that effects happen "after render". React guarantees the DOM has been updated by the time it runs the effects. +**`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在"渲染之后"这种概念,不用再去考虑"挂载"还是"更新"。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕 ### Detailed Explanation {#detailed-explanation} +### 详细说明 {#detailed-explanation} Now that we know more about effects, these lines should make sense: +现在我们已经对 effect 有了一些了解,下面这些代码应该不难看懂了: ```js function Example() { @@ -134,21 +156,29 @@ function Example() { ``` We declare the `count` state variable, and then we tell React we need to use an effect. We pass a function to the `useEffect` Hook. This function we pass *is* our effect. Inside our effect, we set the document title using the `document.title` browser API. We can read the latest `count` inside the effect because it's in the scope of our function. When React renders our component, it will remember the effect we used, and then run our effect after updating the DOM. This happens for every render, including the first one. +我们声明了 `count` state 变量,接着告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API设置 document 的 title。我们可以在 effct 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 Experienced JavaScript developers might notice that the function passed to `useEffect` is going to be different on every render. This is intentional. In fact, this is what lets us read the `count` value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a _different_ effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result -- each effect "belongs" to a particular render. We will see more clearly why this is useful [later on this page](#explanation-why-effects-run-on-each-update). +经验丰富的JavaScript开发人员可能会注意到,传递给`useEffect`的函数在每个渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 -- 每个 effect "属于"一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 >Tip > >Unlike `componentDidMount` or `componentDidUpdate`, effects scheduled with `useEffect` don't block the browser from updating the screen. This makes your app feel more responsive. The majority of effects don't need to happen synchronously. In the uncommon cases where they do (such as measuring the layout), there is a separate [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook with an API identical to `useEffect`. +> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻止浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 ## Effects with Cleanup {#effects-with-cleanup} +## 须要清理的 Effect Earlier, we looked at how to express side effects that don't require any cleanup. However, some effects do. For example, **we might want to set up a subscription** to some external data source. In that case, it is important to clean up so that we don't introduce a memory leak! Let's compare how we can do it with classes and with Hooks. +之前,我们研究了如何使用不需要任何清理的副作用,还有一些 effect 是需要清理的。例如**我们可能需要订阅一些外部数据源**。这种情况下,清理工作是非常重要的,防止我们引起内存泄露!现在让我们来比较一下如何用 Class 和 Hooks 来实现。 ### Example Using Classes {#example-using-classes-1} +### 使用 Class 的sh示例'i'l {#example-using-classes-1} In a React class, you would typically set up a subscription in `componentDidMount`, and clean it up in `componentWillUnmount`. For example, let's say we have a `ChatAPI` module that lets us subscribe to a friend's online status. Here's how we might subscribe and display that status using a class: +在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清理它。 例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅朋友的在线状态。以下是我们如何使用 class 订阅和显示该状态: + ```js{8-26} class FriendStatus extends React.Component { constructor(props) { @@ -187,16 +217,23 @@ class FriendStatus extends React.Component { ``` Notice how `componentDidMount` and `componentWillUnmount` need to mirror each other. Lifecycle methods force us to split this logic even though conceptually code in both of them is related to the same effect. +你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互依赖。生命周期函数让我们只能拆分这些逻辑,即使他们中的代码都是服务于同一个功能。 + >Note > >Eagle-eyed readers may notice that this example also needs a `componentDidUpdate` method to be fully correct. We'll ignore this for now but will come back to it in a [later section](#explanation-why-effects-run-on-each-update) of this page. +>眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 + ### Example Using Hooks {#example-using-hooks-1} +### 使用 Hooks 的示例 Let's see how we could write this component with Hooks. +让我们看看我们如何用 Hooks 编写这个组件。 You might be thinking that we'd need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that `useEffect` is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up: +您可能认为我们需要单独的 effect 来执行清理工作。但因为添加和删除订阅的代码是如此紧密相关,所以 `useEffect` 的设计是让他们保持在同一个地方。如果你的 effect 返回了一个函数, React 将会在清理的时候执行它。 ```js{10-16} import React, { useState, useEffect } from 'react'; @@ -224,16 +261,22 @@ function FriendStatus(props) { ``` **Why did we return a function from our effect?** This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They're part of the same effect! +**为什么我们要在 effect 中返回一个函数?**这是 effect 可选的清理机制。每一个 effect 都可以返回一个他的清理函数。这让我们可以将添加和移除订阅的逻辑放在一起。他们都是同一个 effect 的一部分。 **When exactly does React clean up an effect?** React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React *also* cleans up effects from the previous render before running the effects next time. We'll discuss [why this helps avoid bugs](#explanation-why-effects-run-on-each-update) and [how to opt out of this behavior in case it creates performance issues](#tip-optimizing-performance-by-skipping-effects) later below. +**React 什么时候清理一个 effect?**React 在组件卸载的时候执行清理。正如我们之前学到的, effect 在每次渲染的时候都会执行。这就是为什么 React *还会*在执行 effect 之前对上一个 effect 进行清理。我们稍后将讨论[为什么这有助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在发生性能问题时选择跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 >Note > >We don't have to return a named function from the effect. We called it `cleanup` here to clarify its purpose, but you could return an arrow function or call it something different. +>我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字 ## Recap {#recap} +## 小结 {#recap} We've learned that `useEffect` lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function: +我们已经了解了 `useEffect` 让我们可以在组件渲染后实现各种不同的副作用。有些副作用可能须要清理,所以他们会返回一个函数。 + ```js useEffect(() => { @@ -245,6 +288,7 @@ We've learned that `useEffect` lets us express different kinds of side effects a ``` Other effects might not have a cleanup phase, and don't return anything. +其他的 effect 可能没有清理阶段,所以不需要返回。 ```js useEffect(() => { @@ -253,20 +297,26 @@ Other effects might not have a cleanup phase, and don't return anything. ``` The Effect Hook unifies both use cases with a single API. +Effect Hook 使用同一个 API 来满足这两种情况。 ------------- **If you feel like you have a decent grasp on how the Effect Hook works, or if you feel overwhelmed, you can jump to the [next page about Rules of Hooks](/docs/hooks-rules.html) now.** +**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者你感到不知所措,你现在可以跳转到[关于 Hook 规则的下一页](/docs/hooks-rules.html)** ------------- ## Tips for Using Effects {#tips-for-using-effects} +## 使用 Effect 的 Tip {#tips-for-using-effects} We'll continue this page with an in-depth look at some aspects of `useEffect` that experienced React users will likely be curious about. Don't feel obligated to dig into them now. You can always come back to this page to learn more details about the Effect Hook. +在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 ### Tip: Use Multiple Effects to Separate Concerns {#tip-use-multiple-effects-to-separate-concerns} +### Tip: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} One of the problems we outlined in the [Motivation](/docs/hooks-intro.html#complex-components-become-hard-to-understand) for Hooks is that class lifecycle methods often contain unrelated logic, but related logic gets broken up into several methods. Here is a component that combines the counter and the friend status indicator logic from the previous examples: +我们使用 Hooks 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 ```js class FriendStatusWithCounter extends React.Component { @@ -304,6 +354,7 @@ class FriendStatusWithCounter extends React.Component { ``` Note how the logic that sets `document.title` is split between `componentDidMount` and `componentDidUpdate`. The subscription logic is also spread between `componentDidMount` and `componentWillUnmount`. And `componentDidMount` contains code for both tasks. +可以看到设置 `document.title` 的逻辑是如何被分割到 `componentDidMount` 和 `componentDidUpdate` 中的,订阅逻辑又是如何被分割到 `componentDidMount` 和 `componentWillUnmount` 中。而且 `componentDidMount` 中同时包含了两个不同功能的代码。 So, how can Hooks solve this problem? Just like [you can use the *State* Hook more than once](/docs/hooks-state.html#tip-using-multiple-state-variables), you can also use several effects. This lets us separate unrelated logic into different effects: From b485fefed5fe2516fc87b500856772b8f61c3b61 Mon Sep 17 00:00:00 2001 From: yueming Date: Tue, 19 Feb 2019 23:34:03 +0800 Subject: [PATCH 02/51] =?UTF-8?q?hooks-effect=20=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/hooks-effect.md | 122 +++++++++++------------------------ 1 file changed, 37 insertions(+), 85 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index e79546bc05..55e54f7a8a 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -6,10 +6,8 @@ next: hooks-rules.html prev: hooks-intro.html --- -*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class. *Hooks* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他 React 功能。 -The *Effect Hook* lets you perform side effects in function components: *Effect Hook* 让你可以在函数定义组件中执行一些副作用(side effect)操作 ```js{1,6-10} @@ -35,34 +33,25 @@ function Example() { } ``` -This snippet is based on the [counter example from the previous page](/docs/hooks-state.html), but we added a new feature to it: we set the document title to a custom message including the number of clicks. 这段代码是基于[上一页中的计数器示例](/docs/hooks-state.html)修改的,我们为计数器增加了一个小功能:将 document 的 title 设置为一句包含了点击次数的消息 -Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you're used to calling these operations "side effects" (or just "effects"), you've likely performed them in your components before. 数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你之前是否将他们称为"副作用"(可能就是你想要的效果),应该都在组件中使用过了。 >Tip > ->If you're familiar with React class lifecycle methods, you can think of `useEffect` Hook as `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` combined. >如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hooks 看做 `componentDidMount` 和 `componentWillUnmount` 这两个函数的结合 -There are two common kinds of side effects in React components: those that don't require cleanup, and those that do. Let's look at this distinction in more detail. -通常来说在 React 组件中有两种副作用操作:须要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 +通常来说在 React 组件中有两种副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 -## Effects Without Cleanup {#effects-without-cleanup} ## 无需清理的 Effect {#effects-without-cleanup} -Sometimes, we want to **run some additional code after React has updated the DOM.** Network requests, manual DOM mutations, and logging are common examples of effects that don't require a cleanup. We say that because we can run them and immediately forget about them. Let's compare how classes and Hooks let us express such side effects. -有时候,我们只是想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清理的操作。因为我们在执行完这些操作之后,就可以忽略他们了。 +有时候,我们只是想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清理的操作。因为我们在执行完这些操作之后,就可以忽略他们了。让我们对比一下使用 class 和 Hook 都是怎么实现这些副作用的。 -### Example Using Classes {#example-using-classes} ### 使用 class 的示例 {#example-using-classes} -In React class components, the `render` method itself shouldn't cause side effects. It would be too early -- we typically want to perform our effects *after* React has updated the DOM. 在 React 的类定义组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。 -This is why in React classes, we put side effects into `componentDidMount` and `componentDidUpdate`. Coming back to our example, here is a React counter class component that updates the document title right after React makes changes to the DOM: 这就是为什么在 React 类中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器类定义组件。它在 Ract 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 ```js{9-15} @@ -95,19 +84,14 @@ class Example extends React.Component { } ``` -Note how **we have to duplicate the code between these two lifecycle methods in class.** 为什么**在这个 class 中我们需要在两个生命周期函数中写重复的代码** -This is because in many cases we want to perform the same side effect regardless of whether the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render -- but React class components don't have a method like this. We could extract a separate method but we would still have to call it in two places. 这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 ———— 但 React 的类定义组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 -Now let's see how we can do the same with the `useEffect` Hook. 现在让我们来看看如何使用 `useEffect` Hook 做同样的事情。 -### Example Using Hooks {#example-using-hooks} ### 使用 Hooks 的示例 {#example-using-hooks} -We've already seen this example at the top of this page, but let's take a closer look at it: 我们已经在本页顶部看到了这个示例,但让我们再仔细看看它: ```js{1,6-8} @@ -131,19 +115,14 @@ function Example() { } ``` -**What does `useEffect` do?** By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we'll refer to it as our "effect"), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API. -**`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 您的组件需要在渲染后执行某些操作。React会记住你传递的函数(我们将它称之为 "effect"),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,另外我们也可以执行数据获取或调用其他命令式 API。 +**`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 "effect"),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 -**Why is `useEffect` called inside a component?** Placing `useEffect` inside the component lets us access the `count` state variable (or any props) right from the effect. We don't need a special API to read it -- it's already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution. -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 他已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 -**Does `useEffect` run after every render?** Yes! By default, it runs both after the first render *and* after every update. (We will later talk about [how to customize this](#tip-optimizing-performance-by-skipping-effects).) Instead of thinking in terms of "mounting" and "updating", you might find it easier to think that effects happen "after render". React guarantees the DOM has been updated by the time it runs the effects. -**`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在"渲染之后"这种概念,不用再去考虑"挂载"还是"更新"。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕 +**`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在"渲染之后"这种概念,不用再去考虑"挂载"还是"更新"。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 -### Detailed Explanation {#detailed-explanation} ### 详细说明 {#detailed-explanation} -Now that we know more about effects, these lines should make sense: 现在我们已经对 effect 有了一些了解,下面这些代码应该不难看懂了: ```js @@ -155,29 +134,21 @@ function Example() { }); ``` -We declare the `count` state variable, and then we tell React we need to use an effect. We pass a function to the `useEffect` Hook. This function we pass *is* our effect. Inside our effect, we set the document title using the `document.title` browser API. We can read the latest `count` inside the effect because it's in the scope of our function. When React renders our component, it will remember the effect we used, and then run our effect after updating the DOM. This happens for every render, including the first one. -我们声明了 `count` state 变量,接着告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API设置 document 的 title。我们可以在 effct 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 +我们声明了 `count` state 变量,并告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API 设置 document 的 title。我们可以在 effct 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 -Experienced JavaScript developers might notice that the function passed to `useEffect` is going to be different on every render. This is intentional. In fact, this is what lets us read the `count` value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a _different_ effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result -- each effect "belongs" to a particular render. We will see more clearly why this is useful [later on this page](#explanation-why-effects-run-on-each-update). -经验丰富的JavaScript开发人员可能会注意到,传递给`useEffect`的函数在每个渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 -- 每个 effect "属于"一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 +经验丰富的JavaScript开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 -- 每个 effect "属于"一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 >Tip > ->Unlike `componentDidMount` or `componentDidUpdate`, effects scheduled with `useEffect` don't block the browser from updating the screen. This makes your app feel more responsive. The majority of effects don't need to happen synchronously. In the uncommon cases where they do (such as measuring the layout), there is a separate [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook with an API identical to `useEffect`. > 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻止浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 -## Effects with Cleanup {#effects-with-cleanup} ## 须要清理的 Effect -Earlier, we looked at how to express side effects that don't require any cleanup. However, some effects do. For example, **we might want to set up a subscription** to some external data source. In that case, it is important to clean up so that we don't introduce a memory leak! Let's compare how we can do it with classes and with Hooks. 之前,我们研究了如何使用不需要任何清理的副作用,还有一些 effect 是需要清理的。例如**我们可能需要订阅一些外部数据源**。这种情况下,清理工作是非常重要的,防止我们引起内存泄露!现在让我们来比较一下如何用 Class 和 Hooks 来实现。 -### Example Using Classes {#example-using-classes-1} -### 使用 Class 的sh示例'i'l {#example-using-classes-1} +### 使用 Class 的示例 {#example-using-classes-1} -In a React class, you would typically set up a subscription in `componentDidMount`, and clean it up in `componentWillUnmount`. For example, let's say we have a `ChatAPI` module that lets us subscribe to a friend's online status. Here's how we might subscribe and display that status using a class: - -在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清理它。 例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅朋友的在线状态。以下是我们如何使用 class 订阅和显示该状态: +在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清理它。 例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态: ```js{8-26} class FriendStatus extends React.Component { @@ -216,23 +187,17 @@ class FriendStatus extends React.Component { } ``` -Notice how `componentDidMount` and `componentWillUnmount` need to mirror each other. Lifecycle methods force us to split this logic even though conceptually code in both of them is related to the same effect. 你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互依赖。生命周期函数让我们只能拆分这些逻辑,即使他们中的代码都是服务于同一个功能。 >Note > ->Eagle-eyed readers may notice that this example also needs a `componentDidUpdate` method to be fully correct. We'll ignore this for now but will come back to it in a [later section](#explanation-why-effects-run-on-each-update) of this page. >眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 - -### Example Using Hooks {#example-using-hooks-1} ### 使用 Hooks 的示例 -Let's see how we could write this component with Hooks. 让我们看看我们如何用 Hooks 编写这个组件。 -You might be thinking that we'd need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that `useEffect` is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up: 您可能认为我们需要单独的 effect 来执行清理工作。但因为添加和删除订阅的代码是如此紧密相关,所以 `useEffect` 的设计是让他们保持在同一个地方。如果你的 effect 返回了一个函数, React 将会在清理的时候执行它。 ```js{10-16} @@ -260,23 +225,17 @@ function FriendStatus(props) { } ``` -**Why did we return a function from our effect?** This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They're part of the same effect! -**为什么我们要在 effect 中返回一个函数?**这是 effect 可选的清理机制。每一个 effect 都可以返回一个他的清理函数。这让我们可以将添加和移除订阅的逻辑放在一起。他们都是同一个 effect 的一部分。 +**为什么我们要在 effect 中返回一个函数?**这是 effect 可选的清理机制。每一个 effect 都可以返回一个它的清理函数。这让我们可以将添加和移除订阅的逻辑放在一起。他们都是同一个 effect 的一部分。 -**When exactly does React clean up an effect?** React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React *also* cleans up effects from the previous render before running the effects next time. We'll discuss [why this helps avoid bugs](#explanation-why-effects-run-on-each-update) and [how to opt out of this behavior in case it creates performance issues](#tip-optimizing-performance-by-skipping-effects) later below. **React 什么时候清理一个 effect?**React 在组件卸载的时候执行清理。正如我们之前学到的, effect 在每次渲染的时候都会执行。这就是为什么 React *还会*在执行 effect 之前对上一个 effect 进行清理。我们稍后将讨论[为什么这有助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在发生性能问题时选择跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 >Note > ->We don't have to return a named function from the effect. We called it `cleanup` here to clarify its purpose, but you could return an arrow function or call it something different. >我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字 -## Recap {#recap} ## 小结 {#recap} -We've learned that `useEffect` lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function: -我们已经了解了 `useEffect` 让我们可以在组件渲染后实现各种不同的副作用。有些副作用可能须要清理,所以他们会返回一个函数。 - +我们已经了解了 `useEffect` 让我们可以在组件渲染后实现各种不同的副作用。有些副作用可能须要清理,所以他们会返回一个函数: ```js useEffect(() => { @@ -287,7 +246,6 @@ We've learned that `useEffect` lets us express different kinds of side effects a }); ``` -Other effects might not have a cleanup phase, and don't return anything. 其他的 effect 可能没有清理阶段,所以不需要返回。 ```js @@ -296,26 +254,20 @@ Other effects might not have a cleanup phase, and don't return anything. }); ``` -The Effect Hook unifies both use cases with a single API. Effect Hook 使用同一个 API 来满足这两种情况。 ------------- -**If you feel like you have a decent grasp on how the Effect Hook works, or if you feel overwhelmed, you can jump to the [next page about Rules of Hooks](/docs/hooks-rules.html) now.** -**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者你感到不知所措,你现在可以跳转到[关于 Hook 规则的下一页](/docs/hooks-rules.html)** +**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者你感到不解,你现在可以跳转到[关于 Hook 规则的下一页](/docs/hooks-rules.html)** ------------- -## Tips for Using Effects {#tips-for-using-effects} ## 使用 Effect 的 Tip {#tips-for-using-effects} -We'll continue this page with an in-depth look at some aspects of `useEffect` that experienced React users will likely be curious about. Don't feel obligated to dig into them now. You can always come back to this page to learn more details about the Effect Hook. 在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 -### Tip: Use Multiple Effects to Separate Concerns {#tip-use-multiple-effects-to-separate-concerns} ### Tip: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} -One of the problems we outlined in the [Motivation](/docs/hooks-intro.html#complex-components-become-hard-to-understand) for Hooks is that class lifecycle methods often contain unrelated logic, but related logic gets broken up into several methods. Here is a component that combines the counter and the friend status indicator logic from the previous examples: 我们使用 Hooks 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 ```js @@ -353,10 +305,9 @@ class FriendStatusWithCounter extends React.Component { // ... ``` -Note how the logic that sets `document.title` is split between `componentDidMount` and `componentDidUpdate`. The subscription logic is also spread between `componentDidMount` and `componentWillUnmount`. And `componentDidMount` contains code for both tasks. 可以看到设置 `document.title` 的逻辑是如何被分割到 `componentDidMount` 和 `componentDidUpdate` 中的,订阅逻辑又是如何被分割到 `componentDidMount` 和 `componentWillUnmount` 中。而且 `componentDidMount` 中同时包含了两个不同功能的代码。 -So, how can Hooks solve this problem? Just like [you can use the *State* Hook more than once](/docs/hooks-state.html#tip-using-multiple-state-variables), you can also use several effects. This lets us separate unrelated logic into different effects: +那么 Hooks 如何解决这个问题呢?就像[你可以使用多个 *State* Hook](/docs/hooks-state.html#tip-using-multiple-state-variables) 一样,你也可以使用多个 effect。这让我们将不相关的逻辑分到不同的 effect 中 ```js{3,8} function FriendStatusWithCounter(props) { @@ -380,13 +331,14 @@ function FriendStatusWithCounter(props) { } ``` -**Hooks lets us split the code based on what it is doing** rather than a lifecycle method name. React will apply *every* effect used by the component, in the order they were specified. +**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effct -### Explanation: Why Effects Run on Each Update {#explanation-why-effects-run-on-each-update} +### Explanation: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} If you're used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let's look at a practical example to see why this design helps us create components with fewer bugs. +如果你已经习惯了使用 class,那么你可能会想知道为什么 effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。 -[Earlier on this page](#example-using-classes-1), we introduced an example `FriendStatus` component that displays whether a friend is online or not. Our class reads `friend.id` from `this.props`, subscribes to the friend status after the component mounts, and unsubscribes during unmounting: +在[本页前面](#example-using-classes-1),我们介绍了一个用于显示好友是否在线的 `FriendStatus` 组件。我们的 class 从 props 中读取 `friend.id`,在组件挂载后订阅好友的状态,并在卸载组件的时候取消订阅: ```js componentDidMount() { @@ -404,9 +356,9 @@ If you're used to classes, you might be wondering why the effect cleanup phase h } ``` -**But what happens if the `friend` prop changes** while the component is on the screen? Our component would continue displaying the online status of a different friend. This is a bug. We would also cause a memory leak or crash when unmounting since the unsubscribe call would use the wrong friend ID. +**但是当组件已经显示在屏幕上时,`friend` prop 发生变化会发生什么?**我们的组件将继续展示原来的好友状态。这是一个 bug。而且我们还会因为取消订阅时使用错误的好友 ID 导致内存泄露的问题。 -In a class component, we would need to add `componentDidUpdate` to handle this case: +在一个类定义组件中,我们需要加上 `componentDidUpdate` 来解决这个问题: ```js{8-19} componentDidMount() { @@ -437,9 +389,9 @@ In a class component, we would need to add `componentDidUpdate` to handle this c } ``` -Forgetting to handle `componentDidUpdate` properly is a common source of bugs in React applications. +忘记正确地处理 `componentDidUpdate` 是 React 应用中一个常见的错误来源。 -Now consider the version of this component that uses Hooks: +现在看一下使用 Hook 的这个版本: ```js function FriendStatus(props) { @@ -452,9 +404,9 @@ function FriendStatus(props) { }); ``` -It doesn't suffer from this bug. (But we also didn't make any changes to it.) +它并不会受到这个 bug 的影响。(虽然我们没有对它做任何改动。) -There is no special code for handling updates because `useEffect` handles them *by default*. It cleans up the previous effects before applying the next effects. To illustrate this, here is a sequence of subscribe and unsubscribe calls that this component could produce over time: +我们并不需要特定的代码来处理更新的逻辑,因为 `useEffect` *默认*就会处理。它会在调用一个 effect 之前对前一个进行清理。为了说明这一点,下面按时间列出一个可能会产生的订阅和取消订阅操作调用序列: ```js // Mount with { friend: { id: 100 } } props @@ -472,11 +424,11 @@ ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run next effect ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect ``` -This behavior ensures consistency by default and prevents bugs that are common in class components due to missing update logic. +这样的默认行为保证了一致性,避免了在类定义组件中常见的,由于没有处理更新逻辑而导致的 bug。 -### Tip: Optimizing Performance by Skipping Effects {#tip-optimizing-performance-by-skipping-effects} +### Tip: 通过跳过 Effect 进行性能优化 {#tip-optimizing-performance-by-skipping-effects} -In some cases, cleaning up or applying the effect after every render might create a performance problem. In class components, we can solve this by writing an extra comparison with `prevProps` or `prevState` inside `componentDidUpdate`: +在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在类定义组件中,我们可以通过在 `componentDidUpdate` 中加入一些对 `prevProps` 或 `prevState` 进行比较的逻辑解决: ```js componentDidUpdate(prevProps, prevState) { @@ -486,7 +438,7 @@ componentDidUpdate(prevProps, prevState) { } ``` -This requirement is common enough that it is built into the `useEffect` Hook API. You can tell React to *skip* applying an effect if certain values haven't changed between re-renders. To do so, pass an array as an optional second argument to `useEffect`: +这是一个很常见的需求,所以它被内置到了 `useEffect` Hook API 中。如果某些特定值在两次重渲染之间没有发生变化,你可以告诉 React **跳过**对 effect 的调用,只要传递一个数组作为 `useEffect` 的第二个可选参数就可以了: ```js{3} useEffect(() => { @@ -494,11 +446,11 @@ useEffect(() => { }, [count]); // Only re-run the effect if count changes ``` -In the example above, we pass `[count]` as the second argument. What does this mean? If the `count` is `5`, and then our component re-renders with `count` still equal to `5`, React will compare `[5]` from the previous render and `[5]` from the next render. Because all items in the array are the same (`5 === 5`), React would skip the effect. That's our optimization. +上面这个示例中,我们传入 `[count]` 作为第二个参数。这个参数是什么作用呢?如果 `count` 的值是 `5`,而且我们的组件重渲染的时候 `count` 还是等于 `5`,React 将对前一次渲染的 `[5]` 和后一次渲染的 `[5]` 进行比较。因为数组中的所有元素都是相等的(`5 === 5`),React 会跳过这个 effect,这就实现了性能的优化。 -When we render with `count` updated to `6`, React will compare the items in the `[5]` array from the previous render to items in the `[6]` array from the next render. This time, React will re-apply the effect because `5 !== 6`. If there are multiple items in the array, React will re-run the effect even if just one of them is different. +当我们渲染的时候,如果 `count` 的值更新成了 `6`,React 将会把前一次渲染时的数组 `[5]` 和这次渲染的数组 `[6]` 中的元素进行对比。这次因为 `5 !== 6`,React 就会再次调用 effect。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。 -This also works for effects that have a cleanup phase: +对于有清理阶段的 effect 这也是同样有效的: ```js{6} useEffect(() => { @@ -509,18 +461,18 @@ useEffect(() => { }, [props.friend.id]); // Only re-subscribe if props.friend.id changes ``` -In the future, the second argument might get added automatically by a build-time transformation. +将来,第二个参数可能会在构建时自动添加。 >Note > ->If you use this optimization, make sure the array includes **any values from the outer scope that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. We'll also discuss other optimization options in the [Hooks API reference](/docs/hooks-reference.html). +>如果你要使用这个优化,请确保数组中包含了**所有外部作用域中会随时间变化并且在 effect 中使用的变量**,否则你的代码会引用到先前渲染中陈旧的变量。我们在[Hooks API 参考](/docs/hooks-reference.html)中还讨论了其他的优化选项。 > ->If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array (`[]`) as a second argument. This tells React that your effect doesn't depend on *any* values from props or state, so it never needs to re-run. This isn't handled as a special case -- it follows directly from how the inputs array always works. While passing `[]` is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, we suggest not making it a habit because it often leads to bugs, [as discussed above](#explanation-why-effects-run-on-each-update). Don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. +>如果你想要执行一个只运行一次的 effect(仅在组件加载和卸载时执行),你可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 proprs 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 -- 它依然遵循输入数组的工作方式。虽然传递 `[]` 更接近于我们熟悉的 `componentDidMount` 和 `componentWillUnmount`,但我们建议不要将它作为一种习惯,因为这经常会[像之前讨论的那样](#explanation-why-effects-run-on-each-update)导致 bug。不要忘记 React 将 `useEffect` 的运行延迟到浏览器完成绘制之后,所以做一些额外的工作并不是什么问题。 -## Next Steps {#next-steps} +## 下一步 {#next-steps} -Congratulations! This was a long page, but hopefully by the end most of your questions about effects were answered. You've learned both the State Hook and the Effect Hook, and there is a *lot* you can do with both of them combined. They cover most of the use cases for classes -- and where they don't, you might find the [additional Hooks](/docs/hooks-reference.html) helpful. +恭喜你!这是一个很长的页面,希望最后你关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 - 如果没有,您可能须要[其他的 Hook](/docs/hooks-reference.html)。 -We're also starting to see how Hooks solve problems outlined in [Motivation](/docs/hooks-intro.html#motivation). We've seen how effect cleanup avoids duplication in `componentDidUpdate` and `componentWillUnmount`, brings related code closer together, and helps us avoid bugs. We've also seen how we can separate effects by their purpose, which is something we couldn't do in classes at all. +我们也看到了 Hooks 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 -At this point you might be questioning how Hooks work. How can React know which `useState` call corresponds to which state variable between re-renders? How does React "match up" previous and next effects on every update? **On the next page we will learn about the [Rules of Hooks](/docs/hooks-rules.html) -- they're essential to making Hooks work.** +此时你可能会好奇 Hooks 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) -- 他们对 Hook 的工作至关重要** From 698dfe28f45eb42b6a03bb81afadf7fa3fd00eff Mon Sep 17 00:00:00 2001 From: yueming Date: Tue, 26 Feb 2019 19:07:46 +0800 Subject: [PATCH 03/51] Update hooks-effect.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改一些问题 --- content/docs/hooks-effect.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 55e54f7a8a..9959b4fe04 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -6,7 +6,7 @@ next: hooks-rules.html prev: hooks-intro.html --- -*Hooks* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他 React 功能。 +*Hooks* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他的 React 功能。 *Effect Hook* 让你可以在函数定义组件中执行一些副作用(side effect)操作 @@ -40,7 +40,7 @@ function Example() { >Tip > ->如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hooks 看做 `componentDidMount` 和 `componentWillUnmount` 这两个函数的结合 +>如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hooks 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的结合 通常来说在 React 组件中有两种副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 @@ -52,7 +52,7 @@ function Example() { 在 React 的类定义组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。 -这就是为什么在 React 类中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器类定义组件。它在 Ract 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 +这就是为什么在 React 类中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器类定义组件。它在 React 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 ```js{9-15} class Example extends React.Component { From d3028abdc71dcfcdc742217431b4f6d8da576865 Mon Sep 17 00:00:00 2001 From: yueming Date: Tue, 26 Feb 2019 19:08:19 +0800 Subject: [PATCH 04/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 9959b4fe04..2e56050f9b 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -40,7 +40,7 @@ function Example() { >Tip > ->如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hooks 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的结合 +>如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的结合 通常来说在 React 组件中有两种副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 From da3cee24ec62c874803d1a633e63375cfe1ba5e2 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:05:05 +0800 Subject: [PATCH 05/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 2e56050f9b..789f12a71e 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -6,7 +6,7 @@ next: hooks-rules.html prev: hooks-intro.html --- -*Hooks* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他的 React 功能。 +*Hook* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他的 React 特性。 *Effect Hook* 让你可以在函数定义组件中执行一些副作用(side effect)操作 From 1f2105aa03f88cb83c09959076441855daa1dfcb Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:06:00 +0800 Subject: [PATCH 06/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 789f12a71e..bc7d25db67 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -40,7 +40,7 @@ function Example() { >Tip > ->如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的结合 +>如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的组合。 通常来说在 React 组件中有两种副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 From dd4f0b0d2903efb43da9620ade7a45dbd14c9840 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:06:29 +0800 Subject: [PATCH 07/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index bc7d25db67..66338a1068 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -52,7 +52,7 @@ function Example() { 在 React 的类定义组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。 -这就是为什么在 React 类中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器类定义组件。它在 React 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 +这就是为什么在 React class中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器 class 组件。它在 React 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 ```js{9-15} class Example extends React.Component { From 834412c3b323ae14913e9035d6aa17eeaf5b7b09 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:07:25 +0800 Subject: [PATCH 08/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 66338a1068..ef78385171 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -473,6 +473,6 @@ useEffect(() => { 恭喜你!这是一个很长的页面,希望最后你关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 - 如果没有,您可能须要[其他的 Hook](/docs/hooks-reference.html)。 -我们也看到了 Hooks 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 +我们也看到了 Hook 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 此时你可能会好奇 Hooks 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) -- 他们对 Hook 的工作至关重要** From 31ee1962f8c32e6fba3fe4a1635b3291a43ce4b5 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:08:30 +0800 Subject: [PATCH 09/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index ef78385171..6e251130d4 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -475,4 +475,4 @@ useEffect(() => { 我们也看到了 Hook 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 -此时你可能会好奇 Hooks 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) -- 他们对 Hook 的工作至关重要** +此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) -- 他们对 Hook 的工作至关重要。** From d2789348d870b541488dcdce4b48bec7a6167516 Mon Sep 17 00:00:00 2001 From: yueming Date: Sun, 17 Mar 2019 16:13:26 +0800 Subject: [PATCH 10/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 6e251130d4..2c18e33709 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -426,9 +426,9 @@ ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last e 这样的默认行为保证了一致性,避免了在类定义组件中常见的,由于没有处理更新逻辑而导致的 bug。 -### Tip: 通过跳过 Effect 进行性能优化 {#tip-optimizing-performance-by-skipping-effects} +### 提示: 通过跳过 Effect 进行性能优化 {#tip-optimizing-performance-by-skipping-effects} -在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在类定义组件中,我们可以通过在 `componentDidUpdate` 中加入一些对 `prevProps` 或 `prevState` 进行比较的逻辑解决: +在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 `componentDidUpdate` 中加入一些对 `prevProps` 或 `prevState` 进行比较的逻辑解决: ```js componentDidUpdate(prevProps, prevState) { From b0dc71a1be89ca4c8319f492d1697c3bbf929a9b Mon Sep 17 00:00:00 2001 From: yueming Date: Sun, 17 Mar 2019 16:14:16 +0800 Subject: [PATCH 11/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 2c18e33709..18c16c55fd 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -37,7 +37,6 @@ function Example() { 数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你之前是否将他们称为"副作用"(可能就是你想要的效果),应该都在组件中使用过了。 - >Tip > >如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的组合。 From b1ff08ee53c83fc8feaf639eb29f552eaa000c4c Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:15:08 +0800 Subject: [PATCH 12/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 18c16c55fd..2379501866 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -89,7 +89,7 @@ class Example extends React.Component { 现在让我们来看看如何使用 `useEffect` Hook 做同样的事情。 -### 使用 Hooks 的示例 {#example-using-hooks} +### 使用 Hook 的示例 {#example-using-hooks} 我们已经在本页顶部看到了这个示例,但让我们再仔细看看它: From ab443dec190e84a1bf827ea9e58926a6b4d6faba Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:16:07 +0800 Subject: [PATCH 13/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 2379501866..905e15e630 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -83,7 +83,7 @@ class Example extends React.Component { } ``` -为什么**在这个 class 中我们需要在两个生命周期函数中写重复的代码** +注意,**在这个 class 中我们需要在两个生命周期函数中写重复的代码** 这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 ———— 但 React 的类定义组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 From c44c0cebae82f59461f2a3cf154837b7b38d3e7b Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:17:16 +0800 Subject: [PATCH 14/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 905e15e630..8af59b1b8e 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -135,7 +135,7 @@ function Example() { 我们声明了 `count` state 变量,并告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API 设置 document 的 title。我们可以在 effct 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 -经验丰富的JavaScript开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 -- 每个 effect "属于"一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 +经验丰富的 JavaScript 开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 -- 每个 effect “属于”一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 >Tip > From 3f0ea6363fb3efea28dd86f2033302afaabd5dbe Mon Sep 17 00:00:00 2001 From: yueming Date: Sun, 17 Mar 2019 16:20:53 +0800 Subject: [PATCH 15/51] Update hooks-effect.md quota --- content/docs/hooks-effect.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 8af59b1b8e..8643525fc4 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -114,11 +114,11 @@ function Example() { } ``` -**`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 "effect"),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 +**`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 **为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 -**`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在"渲染之后"这种概念,不用再去考虑"挂载"还是"更新"。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 +**`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 ### 详细说明 {#detailed-explanation} From 98b3eb3d987bf446aa588b9a369a9da134e20060 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:21:54 +0800 Subject: [PATCH 16/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 8643525fc4..5405270aa6 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -195,7 +195,7 @@ class FriendStatus extends React.Component { ### 使用 Hooks 的示例 -让我们看看我们如何用 Hooks 编写这个组件。 +让我们看看我们如何用 Hook 编写这个组件。 您可能认为我们需要单独的 effect 来执行清理工作。但因为添加和删除订阅的代码是如此紧密相关,所以 `useEffect` 的设计是让他们保持在同一个地方。如果你的 effect 返回了一个函数, React 将会在清理的时候执行它。 From 9cedf1dbf6d77475bbc2faf78fe1e15c5937ac5e Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:22:48 +0800 Subject: [PATCH 17/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 5405270aa6..4b50d705a4 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -8,7 +8,7 @@ prev: hooks-intro.html *Hook* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他的 React 特性。 -*Effect Hook* 让你可以在函数定义组件中执行一些副作用(side effect)操作 +*Effect Hook* 让你可以在函数定义组件中执行一些副作用操作 ```js{1,6-10} import React, { useState, useEffect } from 'react'; From c87f689479805c3b1723f390afcf64effe2bf83f Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:24:52 +0800 Subject: [PATCH 18/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 4b50d705a4..125b6dbbd7 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -116,7 +116,7 @@ function Example() { **`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在函数作用域中了。Hook 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 **`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 From 430364b59252b86cdd7a389091fd636a31ce2515 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:25:30 +0800 Subject: [PATCH 19/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 125b6dbbd7..aa000115ce 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -139,7 +139,7 @@ function Example() { >Tip > -> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻止浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 +> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 ## 须要清理的 Effect From d8af6a9a7530e45e41005e60211c922e9d628dcd Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:27:17 +0800 Subject: [PATCH 20/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index aa000115ce..fd184a179a 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -193,7 +193,7 @@ class FriendStatus extends React.Component { > >眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 -### 使用 Hooks 的示例 +### 使用 Hook 的示例 让我们看看我们如何用 Hook 编写这个组件。 From 43056c4976be9c2ce39b05e6e7d1282599a2aa5b Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:27:50 +0800 Subject: [PATCH 21/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index fd184a179a..cf334ceeb8 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -230,7 +230,7 @@ function FriendStatus(props) { >Note > ->我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字 +>我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字。 ## 小结 {#recap} From 30fcad514282f353c021d6e76c87059f59bc2876 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:28:14 +0800 Subject: [PATCH 22/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index cf334ceeb8..7a6a71894e 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -261,7 +261,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ------------- -## 使用 Effect 的 Tip {#tips-for-using-effects} +## 使用 Effect 的提示 {#tips-for-using-effects} 在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 From 8686274e9ebc3e79012d8a0cdb9091903300530f Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:28:32 +0800 Subject: [PATCH 23/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 7a6a71894e..af440cb772 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -265,7 +265,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 -### Tip: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} +### 提示:使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} 我们使用 Hooks 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 From 37ee94724bef3ca2284a548d30c6b2fa21e45a55 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:28:57 +0800 Subject: [PATCH 24/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index af440cb772..f92d34eb07 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -267,7 +267,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ### 提示:使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} -我们使用 Hooks 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 +我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件: ```js class FriendStatusWithCounter extends React.Component { From 044ad318deafe1200ef128a3882bf2acd65281c8 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:29:24 +0800 Subject: [PATCH 25/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index f92d34eb07..37311260a1 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -332,7 +332,7 @@ function FriendStatusWithCounter(props) { **Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effct -### Explanation: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} +### 解释:为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} If you're used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let's look at a practical example to see why this design helps us create components with fewer bugs. 如果你已经习惯了使用 class,那么你可能会想知道为什么 effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。 From 41266c9999edcfc3696682821e3fc2b1383b7227 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:30:16 +0800 Subject: [PATCH 26/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 37311260a1..70aa2de629 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -330,7 +330,7 @@ function FriendStatusWithCounter(props) { } ``` -**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effct +**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect。 ### 解释:为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} From d79e5731f14ac8f488dd9ecce66216675f983624 Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:31:10 +0800 Subject: [PATCH 27/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 70aa2de629..5a25d2004d 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -357,7 +357,7 @@ If you're used to classes, you might be wondering why the effect cleanup phase h **但是当组件已经显示在屏幕上时,`friend` prop 发生变化会发生什么?**我们的组件将继续展示原来的好友状态。这是一个 bug。而且我们还会因为取消订阅时使用错误的好友 ID 导致内存泄露的问题。 -在一个类定义组件中,我们需要加上 `componentDidUpdate` 来解决这个问题: +在一个 class 组件中,我们需要加上 `componentDidUpdate` 来解决这个问题: ```js{8-19} componentDidMount() { From cbfe08cf9d3080ba769ce7bd388253bf35205d0c Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Sun, 17 Mar 2019 16:31:55 +0800 Subject: [PATCH 28/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 5a25d2004d..08cdac3ae8 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -191,7 +191,7 @@ class FriendStatus extends React.Component { >Note > ->眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 +>细心的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法才完全正确。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 ### 使用 Hook 的示例 From b551c413295827035c1d7b7f4cf419a3bdf7f324 Mon Sep 17 00:00:00 2001 From: yueming Date: Sun, 17 Mar 2019 16:36:27 +0800 Subject: [PATCH 29/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 08cdac3ae8..659292899a 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -85,7 +85,7 @@ class Example extends React.Component { 注意,**在这个 class 中我们需要在两个生命周期函数中写重复的代码** -这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 ———— 但 React 的类定义组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 +这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 ———— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 现在让我们来看看如何使用 `useEffect` Hook 做同样的事情。 @@ -116,7 +116,7 @@ function Example() { **`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在函数作用域中了。Hook 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 **`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 @@ -139,7 +139,7 @@ function Example() { >Tip > -> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 +> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻止浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 ## 须要清理的 Effect @@ -188,12 +188,11 @@ class FriendStatus extends React.Component { 你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互依赖。生命周期函数让我们只能拆分这些逻辑,即使他们中的代码都是服务于同一个功能。 - >Note > ->细心的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法才完全正确。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 +>眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 -### 使用 Hook 的示例 +### 使用 Hooks 的示例 让我们看看我们如何用 Hook 编写这个组件。 @@ -230,7 +229,7 @@ function FriendStatus(props) { >Note > ->我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字。 +>我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字 ## 小结 {#recap} @@ -261,13 +260,13 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ------------- -## 使用 Effect 的提示 {#tips-for-using-effects} +## 使用 Effect 的 Tip {#tips-for-using-effects} 在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 -### 提示:使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} +### Tip: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} -我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件: +我们使用 Hooks 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 ```js class FriendStatusWithCounter extends React.Component { @@ -330,9 +329,9 @@ function FriendStatusWithCounter(props) { } ``` -**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect。 +**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effct -### 解释:为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} +### Explanation: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} If you're used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let's look at a practical example to see why this design helps us create components with fewer bugs. 如果你已经习惯了使用 class,那么你可能会想知道为什么 effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。 @@ -355,9 +354,9 @@ If you're used to classes, you might be wondering why the effect cleanup phase h } ``` -**但是当组件已经显示在屏幕上时,`friend` prop 发生变化会发生什么?**我们的组件将继续展示原来的好友状态。这是一个 bug。而且我们还会因为取消订阅时使用错误的好友 ID 导致内存泄露的问题。 +**但是当组件已经显示在屏幕上时,`friend` prop 发生变化会发生什么?**我们的组件将继续展示原来的好友状态。这是一个 bug。而且我们还会因为取消订阅时使用错误的好友 ID 导致内存泄露或崩溃的问题。 -在一个 class 组件中,我们需要加上 `componentDidUpdate` 来解决这个问题: +在一个类定义组件中,我们需要加上 `componentDidUpdate` 来解决这个问题: ```js{8-19} componentDidMount() { From d71624b465935799c6014a8b04781b3edb0e4490 Mon Sep 17 00:00:00 2001 From: yueming Date: Sun, 17 Mar 2019 16:42:02 +0800 Subject: [PATCH 30/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 659292899a..9d0ecf519f 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -116,7 +116,7 @@ function Example() { **`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在函数作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 **`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 @@ -139,7 +139,7 @@ function Example() { >Tip > -> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻止浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 +> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 ## 须要清理的 Effect @@ -190,9 +190,9 @@ class FriendStatus extends React.Component { >Note > ->眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 +>眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法才是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 -### 使用 Hooks 的示例 +### 使用 Hook 的示例 让我们看看我们如何用 Hook 编写这个组件。 @@ -229,7 +229,7 @@ function FriendStatus(props) { >Note > ->我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字 +>我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字。 ## 小结 {#recap} @@ -260,13 +260,13 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ------------- -## 使用 Effect 的 Tip {#tips-for-using-effects} +## 使用 Effect 的提示 {#tips-for-using-effects} 在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 -### Tip: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} +### 提示: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} -我们使用 Hooks 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 +我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 ```js class FriendStatusWithCounter extends React.Component { @@ -329,9 +329,9 @@ function FriendStatusWithCounter(props) { } ``` -**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effct +**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect -### Explanation: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} +### 解释: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} If you're used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let's look at a practical example to see why this design helps us create components with fewer bugs. 如果你已经习惯了使用 class,那么你可能会想知道为什么 effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。 From 99447635f6128668c9d5382d865ca9149f96ed7b Mon Sep 17 00:00:00 2001 From: Yuqing Chen Date: Tue, 19 Mar 2019 22:28:39 +0800 Subject: [PATCH 31/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 9d0ecf519f..24cddd77ae 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -116,7 +116,7 @@ function Example() { **`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 -- 它已经在函数作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它——它已经在函数作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 **`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 From f33156a8fe0d6764cf2588382caaab28a5fa0917 Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Wed, 20 Mar 2019 14:49:39 +0800 Subject: [PATCH 32/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 1a2c5c113d..e2a0ec8f14 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -486,4 +486,4 @@ useEffect(() => { 我们也看到了 Hook 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 -此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) -- 他们对 Hook 的工作至关重要。** +此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) —— 他们对 Hook 的工作至关重要。** From dafc56379d5f5f062e928abcdc8988a1f4e4183c Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Wed, 20 Mar 2019 14:50:11 +0800 Subject: [PATCH 33/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index e2a0ec8f14..d77fb306d9 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -116,7 +116,7 @@ function Example() { **`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它——它已经在函数作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经在函数作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 **`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 From 86362f0c9166b19bdf98ff9919d7e0db93ceb38b Mon Sep 17 00:00:00 2001 From: yueming Date: Wed, 20 Mar 2019 14:54:50 +0800 Subject: [PATCH 34/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index d77fb306d9..ba81c17b5d 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -337,7 +337,6 @@ function FriendStatusWithCounter(props) { ### 解释: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} -If you're used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let's look at a practical example to see why this design helps us create components with fewer bugs. 如果你已经习惯了使用 class,那么你可能会想知道为什么 effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。 在[本页前面](#example-using-classes-1),我们介绍了一个用于显示好友是否在线的 `FriendStatus` 组件。我们的 class 从 props 中读取 `friend.id`,在组件挂载后订阅好友的状态,并在卸载组件的时候取消订阅: From a9caf11faf3e1a41690ef3a5ce04d472221b7c46 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 20 Mar 2019 15:17:25 +0800 Subject: [PATCH 35/51] docs:modify hooks-effect doc --- content/docs/hooks-effect.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index ba81c17b5d..0c1fa5a934 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -85,7 +85,7 @@ class Example extends React.Component { 注意,**在这个 class 中我们需要在两个生命周期函数中写重复的代码** -这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 ———— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 +这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 现在让我们来看看如何使用 `useEffect` Hook 做同样的事情。 @@ -133,9 +133,9 @@ function Example() { }); ``` -我们声明了 `count` state 变量,并告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API 设置 document 的 title。我们可以在 effct 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 +我们声明了 `count` state 变量,并告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API 设置 document 的 title。我们可以在 effect 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 -经验丰富的 JavaScript 开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 -- 每个 effect “属于”一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 +经验丰富的 JavaScript 开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 >Tip > @@ -471,13 +471,13 @@ useEffect(() => { >Note > ->如果你要使用这个优化,请确保数组中包含了**所有外部作用域中会随时间变化并且在 effect 中使用的变量**,否则你的代码会引用到先前渲染中陈旧的变量。Learn more about [how to deal with functions](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) and [what to do when the array changes too often](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often). +>如果你要使用这个优化,请确保数组中包含了**所有外部作用域中会随时间变化并且在 effect 中使用的变量**,否则你的代码会引用到先前渲染中陈旧的变量。了解更多关于[如何处理函数](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)以及[数组频繁变化时的措施](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)内容。 > ->如果你想要执行一个只运行一次的 effect(仅在组件加载和卸载时执行),你可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 proprs 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 —— 它依然遵循输入数组的工作方式。 +>如果你想要执行一个只运行一次的 effect(仅在组件加载和卸载时执行),你可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 —— 它依然遵循输入数组的工作方式。 > ->If you pass an empty array (`[]`), the props and state inside the effect will always have their initial values. While passing `[]` as the second argument is closer to the familiar `componentDidMount` and `componentWillUnmount` mental model, there are usually [better](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) [solutions](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often) to avoid re-running effects too often. Also, don't forget that React defers running `useEffect` until after the browser has painted, so doing extra work is less of a problem. +>如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。由于传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,所以我们可以用[更好的](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方法](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过过于频繁的重复调用 effect。除此之外,记住,React 直到浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此做额外的工作不是大问题。 > ->We recommend using the [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) rule as part of our [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) package. It warns when dependencies are specified incorrectly and suggests a fix. +>推荐使用 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则来作为我们的 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 的部分内容。他会在错误指定依赖的时候发出警告并给出修复建议。 ## 下一步 {#next-steps} @@ -485,4 +485,4 @@ useEffect(() => { 我们也看到了 Hook 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 -此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?Reat 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) —— 他们对 Hook 的工作至关重要。** +此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?React 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) —— 他们对 Hook 的工作至关重要。** From b83b49c5b479b35f560e2af59a5021ee2441c806 Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Wed, 20 Mar 2019 16:29:41 +0800 Subject: [PATCH 36/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 0c1fa5a934..d1d73d83b6 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -37,7 +37,7 @@ function Example() { 数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你之前是否将他们称为"副作用"(可能就是你想要的效果),应该都在组件中使用过了。 ->Tip +>提示 > >如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的组合。 @@ -137,7 +137,7 @@ function Example() { 经验丰富的 JavaScript 开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 ->Tip +>提示 > > 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 @@ -188,7 +188,7 @@ class FriendStatus extends React.Component { 你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互依赖。生命周期函数让我们只能拆分这些逻辑,即使他们中的代码都是服务于同一个功能。 ->Note +>注意 > >眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法才是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 @@ -227,7 +227,7 @@ function FriendStatus(props) { **React 什么时候清理一个 effect?**React 在组件卸载的时候执行清理。正如我们之前学到的, effect 在每次渲染的时候都会执行。这就是为什么 React *还会*在执行 effect 之前对上一个 effect 进行清理。我们稍后将讨论[为什么这有助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在发生性能问题时选择跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 ->Note +>注意 > >我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字。 From 7fa2218fcc3ff5cfa4982ce1c791d82178c7b24b Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Fri, 22 Mar 2019 10:11:26 +0800 Subject: [PATCH 37/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index d1d73d83b6..bfb47b9802 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -41,7 +41,7 @@ function Example() { > >如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的组合。 -通常来说在 React 组件中有两种副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别 +在 React 组件中有两种常见副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别。 ## 无需清理的 Effect {#effects-without-cleanup} From 015a97b3d4793732c9dfec6266c05a698ee5a3b2 Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Fri, 22 Mar 2019 10:12:06 +0800 Subject: [PATCH 38/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index bfb47b9802..15229b5db9 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -483,6 +483,6 @@ useEffect(() => { 恭喜你!这是一个很长的页面,希望最后你关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 - 如果没有,您可能须要[其他的 Hook](/docs/hooks-reference.html)。 -我们也看到了 Hook 如何解决[使用目的部分](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免重复地使用 `componentDidUpdate` 和 `componentWillUnmount`,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 +我们也看到了 Hook 如何解决[动机](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免了 `componentDidUpdate` 和 `componentWillUnmount` 中的重复,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?React 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) —— 他们对 Hook 的工作至关重要。** From a453766f45d74f1a6aaa799387fd6c87b32cd513 Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Fri, 22 Mar 2019 10:14:03 +0800 Subject: [PATCH 39/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 15229b5db9..3c93b0b241 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -477,7 +477,7 @@ useEffect(() => { > >如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。由于传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,所以我们可以用[更好的](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方法](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过过于频繁的重复调用 effect。除此之外,记住,React 直到浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此做额外的工作不是大问题。 > ->推荐使用 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则来作为我们的 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 的部分内容。他会在错误指定依赖的时候发出警告并给出修复建议。 +>我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。他会在错误指定依赖的时候发出警告并给出修复建议。 ## 下一步 {#next-steps} From e0093d269ca8d363aeb4839ac32027d4885a94d5 Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Fri, 22 Mar 2019 10:14:46 +0800 Subject: [PATCH 40/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 3c93b0b241..37553bb517 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -333,7 +333,7 @@ function FriendStatusWithCounter(props) { } ``` -**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect +**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect。 ### 解释: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} From e8c42247af12cac4ff33aae6e5956631653c87b6 Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Fri, 22 Mar 2019 10:14:59 +0800 Subject: [PATCH 41/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 37553bb517..bc916d855a 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -270,7 +270,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ### 提示: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} -我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件 +我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件: ```js class FriendStatusWithCounter extends React.Component { From ef54af07de5e2c605021c129f9be5e076c0aef18 Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Mon, 25 Mar 2019 18:33:48 +0800 Subject: [PATCH 42/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index bc916d855a..5736f9151d 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -35,7 +35,7 @@ function Example() { 这段代码是基于[上一页中的计数器示例](/docs/hooks-state.html)修改的,我们为计数器增加了一个小功能:将 document 的 title 设置为一句包含了点击次数的消息 -数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你之前是否将他们称为"副作用"(可能就是你想要的效果),应该都在组件中使用过了。 +数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你知不知道这些操作还有"副作用"这个名字,应该都已经在组件中使用过它们了。 >提示 > From fe840a77e0e80a3b1d8c2d923ffaa07d2b802c7b Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Mon, 25 Mar 2019 18:34:24 +0800 Subject: [PATCH 43/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 5736f9151d..57b9b4cf89 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -309,7 +309,7 @@ class FriendStatusWithCounter extends React.Component { 可以看到设置 `document.title` 的逻辑是如何被分割到 `componentDidMount` 和 `componentDidUpdate` 中的,订阅逻辑又是如何被分割到 `componentDidMount` 和 `componentWillUnmount` 中。而且 `componentDidMount` 中同时包含了两个不同功能的代码。 -那么 Hooks 如何解决这个问题呢?就像[你可以使用多个 *State* Hook](/docs/hooks-state.html#tip-using-multiple-state-variables) 一样,你也可以使用多个 effect。这让我们将不相关的逻辑分到不同的 effect 中 +那么 Hooks 如何解决这个问题呢?就像[你可以使用多个 *State* Hook](/docs/hooks-state.html#tip-using-multiple-state-variables) 一样,你也可以使用多个 effect。这让我们将不相关的逻辑分到不同的 effect 中: ```js{3,8} function FriendStatusWithCounter(props) { From 10708691fc3da7cff58dabef751000e7c5866096 Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Mon, 25 Mar 2019 18:34:43 +0800 Subject: [PATCH 44/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 57b9b4cf89..16344de481 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -8,7 +8,7 @@ prev: hooks-state.html *Hook* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他的 React 特性。 -*Effect Hook* 让你可以在函数定义组件中执行一些副作用操作 +*Effect Hook* 让你可以在函数组件中执行一些副作用操作 ```js{1,6-10} import React, { useState, useEffect } from 'react'; From f910ac2c0be072d8f16ed3d27ac0435ae352d0e2 Mon Sep 17 00:00:00 2001 From: Alan Zhang Date: Mon, 25 Mar 2019 18:35:40 +0800 Subject: [PATCH 45/51] Update content/docs/hooks-effect.md Co-Authored-By: Crazydogs --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 16344de481..1b95963ea3 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -260,7 +260,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ------------- -**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者你感到不解,你现在可以跳转到[关于 Hook 规则的下一页](/docs/hooks-rules.html)** +**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者暂时难以消化更多内容,你现在就可以跳转到[下一页学习使用 Hook 的规则](/docs/hooks-rules.html)*。* ------------- From 22f53f79681cc3dfd04f68cce0e4678006610d17 Mon Sep 17 00:00:00 2001 From: yueming Date: Mon, 25 Mar 2019 18:37:08 +0800 Subject: [PATCH 46/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 1b95963ea3..fa588fb099 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -260,7 +260,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 ------------- -**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者暂时难以消化更多内容,你现在就可以跳转到[下一页学习使用 Hook 的规则](/docs/hooks-rules.html)*。* +**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者暂时难以消化更多内容,你现在就可以跳转到[下一页学习使用 Hook 的规则](/docs/hooks-rules.html)。** ------------- From b06533ff0777fdfb290e5ebdde0f8365e567d9d5 Mon Sep 17 00:00:00 2001 From: yueming Date: Mon, 25 Mar 2019 18:42:28 +0800 Subject: [PATCH 47/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index fa588fb099..fa2602b981 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -475,7 +475,7 @@ useEffect(() => { > >如果你想要执行一个只运行一次的 effect(仅在组件加载和卸载时执行),你可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 —— 它依然遵循输入数组的工作方式。 > ->如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。由于传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,所以我们可以用[更好的](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方法](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过过于频繁的重复调用 effect。除此之外,记住,React 直到浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此做额外的工作不是大问题。 +>如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。尽管传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,但我们有[更好的](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方法](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过过于频繁的重复调用 effect。除此之外,记住,React 直到浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此做额外的工作不是大问题。 > >我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。他会在错误指定依赖的时候发出警告并给出修复建议。 From 68f9c309a9ed589a5e6e932d6053714b7dc87dae Mon Sep 17 00:00:00 2001 From: yueming Date: Mon, 25 Mar 2019 18:52:54 +0800 Subject: [PATCH 48/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index fa2602b981..b19dc362ef 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -268,7 +268,7 @@ Effect Hook 使用同一个 API 来满足这两种情况。 在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 -### 提示: 使用多个 Effect 来隔离不同的问题 {#tip-use-multiple-effects-to-separate-concerns} +### 提示: 使用多个 Effect 分离关注点 {#tip-use-multiple-effects-to-separate-concerns} 我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件: From 9ccb2e8d13f7da0c4aeeb2157ae05a86710cba53 Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Tue, 26 Mar 2019 10:08:57 +0800 Subject: [PATCH 49/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index b19dc362ef..77c8676637 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -33,7 +33,7 @@ function Example() { } ``` -这段代码是基于[上一页中的计数器示例](/docs/hooks-state.html)修改的,我们为计数器增加了一个小功能:将 document 的 title 设置为一句包含了点击次数的消息 +这段代码是基于[上一页中的计数器示例](/docs/hooks-state.html)修改的,我们为计数器增加了一个小功能:将 document 的 title 设置为一句包含了点击次数的消息。 数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你知不知道这些操作还有"副作用"这个名字,应该都已经在组件中使用过它们了。 @@ -83,7 +83,7 @@ class Example extends React.Component { } ``` -注意,**在这个 class 中我们需要在两个生命周期函数中写重复的代码** +注意,**在这个 class 中,我们需要在两个生命周期函数中编写重复的代码。** 这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 @@ -147,7 +147,7 @@ function Example() { ### 使用 Class 的示例 {#example-using-classes-1} -在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清理它。 例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态: +在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清理它。例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态: ```js{8-26} class FriendStatus extends React.Component { @@ -186,17 +186,17 @@ class FriendStatus extends React.Component { } ``` -你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互依赖。生命周期函数让我们只能拆分这些逻辑,即使他们中的代码都是服务于同一个功能。 +你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互对应。生命周期函数迫使我们拆分这些逻辑代码,即使这两部分代码都作用于相同的副作用。 >注意 > ->眼尖的读者可能已经注意到了,这个示例还需要一个 `componentDidUpdate` 方法才是完全正确的。我们先暂时忽略这一点,并在本页的[后面一部分](#explanation-why-effects-run-on-each-update)再关注它。 +>眼尖的读者可能已经注意到了,这个示例还需要编写 `componentDidUpdate` 方法才能保证完全正确。我们先暂时忽略这一点,本页的[后续部分](#explanation-why-effects-run-on-each-update)会继续介绍它。 ### 使用 Hook 的示例 -让我们看看我们如何用 Hook 编写这个组件。 +如何使用 Hook 编写这个组件。 -您可能认为我们需要单独的 effect 来执行清理工作。但因为添加和删除订阅的代码是如此紧密相关,所以 `useEffect` 的设计是让他们保持在同一个地方。如果你的 effect 返回了一个函数, React 将会在清理的时候执行它。 +你可能认为我们需要单独的 effect 来执行清理工作。但因为添加和删除订阅的代码是如此紧密相关,所以 `useEffect` 的设计是让他们保持在同一个地方。如果你的 effect 返回了一个函数, React 将会在清理的时候执行它。 ```js{6-16} import React, { useState, useEffect } from 'react'; @@ -225,7 +225,7 @@ function FriendStatus(props) { **为什么我们要在 effect 中返回一个函数?**这是 effect 可选的清理机制。每一个 effect 都可以返回一个它的清理函数。这让我们可以将添加和移除订阅的逻辑放在一起。他们都是同一个 effect 的一部分。 -**React 什么时候清理一个 effect?**React 在组件卸载的时候执行清理。正如我们之前学到的, effect 在每次渲染的时候都会执行。这就是为什么 React *还会*在执行 effect 之前对上一个 effect 进行清理。我们稍后将讨论[为什么这有助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在发生性能问题时选择跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 +**React 什么时候清理一个 effect?**React 在组件卸载的时候执行清理。正如我们之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React *还会*在执行 effect 之前对上一个 effect 进行清理。我们稍后将讨论[为什么这有助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在发生性能问题时选择跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 >注意 > @@ -390,7 +390,7 @@ function FriendStatusWithCounter(props) { } ``` -忘记正确地处理 `componentDidUpdate` 是 React 应用中一个常见的错误来源。 +忘记正确地处理 `componentDidUpdate` 是 React 应用中一个常见的 bug 来源。 现在看一下使用 Hook 的这个版本: @@ -481,7 +481,7 @@ useEffect(() => { ## 下一步 {#next-steps} -恭喜你!这是一个很长的页面,希望最后你关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 - 如果没有,您可能须要[其他的 Hook](/docs/hooks-reference.html)。 +恭喜你!这是一个很长的页面,希望最后你关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 —— 如果没有,你可以查看[其他的 Hook](/docs/hooks-reference.html)。 我们也看到了 Hook 如何解决[动机](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免了 `componentDidUpdate` 和 `componentWillUnmount` 中的重复,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 From de09e1a80958562681f8d4dbc7cba717c9d3f521 Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Tue, 26 Mar 2019 11:59:17 +0800 Subject: [PATCH 50/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 138 +++++++++++++++++------------------ 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index 77c8676637..a505213890 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -6,9 +6,9 @@ next: hooks-rules.html prev: hooks-state.html --- -*Hook* 是 React 16.8 的新增特性。它可以让你在不使用 class 的情况下使用 state 和一些其他的 React 特性。 +*Hook* 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 -*Effect Hook* 让你可以在函数组件中执行一些副作用操作 +*Effect Hook* 可以让你在函数组件中执行副作用操作 ```js{1,6-10} import React, { useState, useEffect } from 'react'; @@ -33,25 +33,25 @@ function Example() { } ``` -这段代码是基于[上一页中的计数器示例](/docs/hooks-state.html)修改的,我们为计数器增加了一个小功能:将 document 的 title 设置为一句包含了点击次数的消息。 +这段代码基于[上一章节中的计数器示例](/docs/hooks-state.html)进行修改,我们为计数器增加了一个小功能:将 document 的 title 设置为包含了点击次数的消息。 -数据获取,设置订阅以及手动更改 React 组件中的 DOM 都是副作用的示例。无论你知不知道这些操作还有"副作用"这个名字,应该都已经在组件中使用过它们了。 +数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。不管你知不知道这些操作,或是“副作用”这个名字,应该都在组件中使用过它们。 >提示 > >如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`,`componentDidUpdate` 和 `componentWillUnmount` 这三个函数的组合。 -在 React 组件中有两种常见副作用操作:需要清理的和不需要清理的。我们来更仔细地看一下他们之间的区别。 +在 React 组件中有两种常见副作用操作:需要清除的和不需要清除的。我们来更仔细地看一下他们之间的区别。 -## 无需清理的 Effect {#effects-without-cleanup} +## 无需清除的 effect {#effects-without-cleanup} -有时候,我们只是想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清理的操作。因为我们在执行完这些操作之后,就可以忽略他们了。让我们对比一下使用 class 和 Hook 都是怎么实现这些副作用的。 +有时候,我们只想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。因为我们在执行完这些操作之后,就可以忽略他们了。让我们对比一下使用 class 和 Hook 都是怎么实现这些副作用的。 ### 使用 class 的示例 {#example-using-classes} -在 React 的类定义组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。 +在 React 的 class 组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。 -这就是为什么在 React class中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到我们的示例中来,这是一个 React 计数器 class 组件。它在 React 对 DOM 进行操作之后,立刻更新了 document 的 title 属性 +这就是为什么在 React class 中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到示例中,这是一个 React 计数器的 class 组件。它在 React 对 DOM 进行操作之后,立即更新了 document 的 title 属性 ```js{9-15} class Example extends React.Component { @@ -85,13 +85,13 @@ class Example extends React.Component { 注意,**在这个 class 中,我们需要在两个生命周期函数中编写重复的代码。** -这是因为很多情况下我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 +这是因为很多情况下,我们希望在组件加载和更新时执行同样的操作。从概念上说,我们希望它在每次渲染之后执行 —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。 -现在让我们来看看如何使用 `useEffect` Hook 做同样的事情。 +现在让我们来看看如何使用 `useEffect` 执行相同的操作。 ### 使用 Hook 的示例 {#example-using-hooks} -我们已经在本页顶部看到了这个示例,但让我们再仔细看看它: +我们在本章节开始时已经看到了这个示例,但让我们再仔细观察它: ```js{1,6-8} import React, { useState, useEffect } from 'react'; @@ -114,15 +114,15 @@ function Example() { } ``` -**`useEffect` 做了什么?** 通过使用这个 Hook,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式 API。 +**`useEffect` 做了什么?** 通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。 -**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经在函数作用域中了。Hooks 使用 JavaScript 的闭包机制,而不是在 JavaScript 已经提供了解决方案的情况下还引入特定的 React API。 +**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。 -**`useEffect` 会在每次渲染后都执行吗?**是的,默认情况下,它在第一次渲染之后*和*每次更新之后都运行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时候,DOM 都已经更新完毕。 +**`useEffect` 会在每次渲染后都执行吗?** 是的,默认情况下,它在第一次渲染之后*和*每次更新之后都会执行。(我们稍后会谈到[如何控制它](#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的时,DOM 都已经更新完毕。 ### 详细说明 {#detailed-explanation} -现在我们已经对 effect 有了一些了解,下面这些代码应该不难看懂了: +现在我们已经对 effect 有了大致了解,下面这些代码应该不难看懂了: ```js function Example() { @@ -133,21 +133,21 @@ function Example() { }); ``` -我们声明了 `count` state 变量,并告诉 React 我们需要使用一个 effect。接着传递一个函数给 `useEffect` Hook。这个函数就是我们的 effect。然后使用 `document.title` 浏览器 API 设置 document 的 title。我们可以在 effect 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染我们的组件时,会记住我们使用的 effect,并在更新完 DOM 后运行它。这个过程在每次渲染时都会发生,包括首次渲染。 +我们声明了 `count` state 变量,并告诉 React 我们需要使用 effect。紧接着传递函数给 `useEffect` Hook。此函数就是我们的 effect。然后使用 `document.title` 浏览器 API 设置 document 的 title。我们可以在 effect 中获取到最新的 `count` 值,因为他在函数的作用域内。当 React 渲染组件时,会保存已使用的 effect,并在更新完 DOM 后执行它。这个过程在每次渲染时都会发生,包括首次渲染。 -经验丰富的 JavaScript 开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成一个_新的_ effect,替换掉之前的。某种意义上来说,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。我们将在[本页后面(#explanation-why-effects-run-on-each-update)]更清楚地看到为什么这很有意义。 +经验丰富的 JavaScript 开发人员可能会注意到,传递给 `useEffect` 的函数在每次渲染中都会有所不同,这是刻意为之的。事实上这正是我们可以在 effect 中获取最新的 `count` 的值,而不用担心其过期的原因。每次我们重新渲染,都会生成_新的_ effect,替换掉之前的。某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect “属于”一次特定的渲染。我们将在[本章节后续部分(#explanation-why-effects-run-on-each-update)]更清楚地了解这样做的意义。 >提示 > -> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的 app 看起来响应更快。大多数情况下,effect 不需要同步地执行。 在个别情况下(例如测量布局),有一个单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook,其API与`useEffect`相同。 +> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook 供你使用,其 API 与`useEffect`相同。 -## 须要清理的 Effect +## 需要清除的 effect -之前,我们研究了如何使用不需要任何清理的副作用,还有一些 effect 是需要清理的。例如**我们可能需要订阅一些外部数据源**。这种情况下,清理工作是非常重要的,防止我们引起内存泄露!现在让我们来比较一下如何用 Class 和 Hooks 来实现。 +之前,我们研究了如何使用不需要清除的副作用,还有一些副作用是需要清除的。例如**订阅外部数据源**。这种情况下,清除工作是非常重要的,可以防止引起内存泄露!现在让我们来比较一下如何用 Class 和 Hook 来实现。 ### 使用 Class 的示例 {#example-using-classes-1} -在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清理它。例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态: +在 React class 中,你通常会在 `componentDidMount` 中设置订阅,并在 `componentWillUnmount` 中清除它。例如,假设我们有一个 `ChatAPI` 模块,它允许我们订阅好友的在线状态。以下是我们如何使用 class 订阅和显示该状态: ```js{8-26} class FriendStatus extends React.Component { @@ -186,17 +186,17 @@ class FriendStatus extends React.Component { } ``` -你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互对应。生命周期函数迫使我们拆分这些逻辑代码,即使这两部分代码都作用于相同的副作用。 +你会注意到 `componentDidMount` 和 `componentWillUnmount` 之间相互对应。使用生命周期函数迫使我们拆分这些逻辑代码,即使这两部分代码都作用于相同的副作用。 >注意 > ->眼尖的读者可能已经注意到了,这个示例还需要编写 `componentDidUpdate` 方法才能保证完全正确。我们先暂时忽略这一点,本页的[后续部分](#explanation-why-effects-run-on-each-update)会继续介绍它。 +>眼尖的读者可能已经注意到了,这个示例还需要编写 `componentDidUpdate` 方法才能保证完全正确。我们先暂时忽略这一点,本章节中[后续部分](#explanation-why-effects-run-on-each-update)会介绍它。 ### 使用 Hook 的示例 如何使用 Hook 编写这个组件。 -你可能认为我们需要单独的 effect 来执行清理工作。但因为添加和删除订阅的代码是如此紧密相关,所以 `useEffect` 的设计是让他们保持在同一个地方。如果你的 effect 返回了一个函数, React 将会在清理的时候执行它。 +你可能认为需要单独的 effect 来执行清除操作。但由于添加和删除订阅的代码的紧密性,所以 `useEffect` 的设计是在同一个地方执行。如果你的 effect 返回一个函数,React 将会在执行清除操作时调用它: ```js{6-16} import React, { useState, useEffect } from 'react'; @@ -223,17 +223,17 @@ function FriendStatus(props) { } ``` -**为什么我们要在 effect 中返回一个函数?**这是 effect 可选的清理机制。每一个 effect 都可以返回一个它的清理函数。这让我们可以将添加和移除订阅的逻辑放在一起。他们都是同一个 effect 的一部分。 +**为什么要在 effect 中返回一个函数?** 这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。 -**React 什么时候清理一个 effect?**React 在组件卸载的时候执行清理。正如我们之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React *还会*在执行 effect 之前对上一个 effect 进行清理。我们稍后将讨论[为什么这有助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在发生性能问题时选择跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 +**React 何时清除 effect?** React 会在组件卸载的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React *会*在执行当前 effect 之前对上一个 effect 进行清除。我们稍后将讨论[为什么这将助于避免 bug](#explanation-why-effects-run-on-each-update)以及[如何在遇到性能问题时跳过此行为](#tip-optimizing-performance-by-skipping-effects)。 >注意 > ->我们并不是必须在 effect 中返回一个命名的函数。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给它命一个别的名字。 +>并不是必须为 effect 中返回的函数命名。这里我们将其命名为 `cleanup` 是为了表明此函数的目的,但其实也可以返回一个箭头函数或者给起一个别的名字。 ## 小结 {#recap} -我们已经了解了 `useEffect` 让我们可以在组件渲染后实现各种不同的副作用。有些副作用可能须要清理,所以他们会返回一个函数: +了解了 `useEffect` 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数: ```js useEffect(() => { @@ -248,7 +248,7 @@ function FriendStatus(props) { }); ``` -其他的 effect 可能没有清理阶段,所以不需要返回。 +其他的 effect 可能不必清除,所以不需要返回。 ```js useEffect(() => { @@ -256,21 +256,21 @@ function FriendStatus(props) { }); ``` -Effect Hook 使用同一个 API 来满足这两种情况。 +effect Hook 使用同一个 API 来满足这两种情况。 ------------- -**如果你感觉你对 Effect Hook 的机制已经有很好的把握,或者暂时难以消化更多内容,你现在就可以跳转到[下一页学习使用 Hook 的规则](/docs/hooks-rules.html)。** +**如果你对 Effect Hook 的机制已经有很好的把握,或者暂时难以消化更多内容,你现在就可以跳转到[下一章节学习 Hook 的规范](/docs/hooks-rules.html)。** ------------- ## 使用 Effect 的提示 {#tips-for-using-effects} -在本页中我们将继续深入了解 `useEffect` 的某些方面,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时返回此页面以了解有关 Effect Hook 的更多详细信息。 +在本节中将继续深入了解 `useEffect` 的某些特性,有经验的 React 使用者可能会对此感兴趣。你不一定要在现在了解他们,你可以随时查看此页面以了解有关 Effect Hook 的更多详细信息。 -### 提示: 使用多个 Effect 分离关注点 {#tip-use-multiple-effects-to-separate-concerns} +### 提示: 使用多个 Effect 实现关注点分离 {#tip-use-multiple-effects-to-separate-concerns} -我们使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含了不相关的逻辑,但又把相关的逻辑分隔到几个不同的方法中的问题。下面这是一个组合了前面示例中的计数器和朋友状态指示器逻辑的组件: +使用 Hook 其中一个[目的](/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。下述代码是将前述示例中的计数器和好友在线状态指示器逻辑组合在一起的组件: ```js class FriendStatusWithCounter extends React.Component { @@ -307,9 +307,9 @@ class FriendStatusWithCounter extends React.Component { // ... ``` -可以看到设置 `document.title` 的逻辑是如何被分割到 `componentDidMount` 和 `componentDidUpdate` 中的,订阅逻辑又是如何被分割到 `componentDidMount` 和 `componentWillUnmount` 中。而且 `componentDidMount` 中同时包含了两个不同功能的代码。 +可以发现设置 `document.title` 的逻辑是如何被分割到 `componentDidMount` 和 `componentDidUpdate` 中的,订阅逻辑又是如何被分割到 `componentDidMount` 和 `componentWillUnmount` 中的。而且 `componentDidMount` 中同时包含了两个不同功能的代码。 -那么 Hooks 如何解决这个问题呢?就像[你可以使用多个 *State* Hook](/docs/hooks-state.html#tip-using-multiple-state-variables) 一样,你也可以使用多个 effect。这让我们将不相关的逻辑分到不同的 effect 中: +那么 Hook 如何解决这个问题呢?就像[你可以使用多个 *state* 的 Hook](/docs/hooks-state.html#tip-using-multiple-state-variables) 一样,你也可以使用多个 effect。这会将不相关逻辑分离到不同的 effect 中: ```js{3,8} function FriendStatusWithCounter(props) { @@ -333,13 +333,13 @@ function FriendStatusWithCounter(props) { } ``` -**Hook 允许我们按照代码的用途分割他们**,而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect。 +**Hook 允许我们按照代码的用途分离他们,** 而不是像生命周期函数那样。React 将按照 effect 声明的顺序依次调用组件中的*每一个* effect。 ### 解释: 为什么每次更新的时候都要运行 Effect {#explanation-why-effects-run-on-each-update} 如果你已经习惯了使用 class,那么你可能会想知道为什么 effect 在每次重渲染时都会执行,而不是只在卸载组件的时候执行一次。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。 -在[本页前面](#example-using-classes-1),我们介绍了一个用于显示好友是否在线的 `FriendStatus` 组件。我们的 class 从 props 中读取 `friend.id`,在组件挂载后订阅好友的状态,并在卸载组件的时候取消订阅: +在[本章节开始时](#example-using-classes-1),我们介绍了一个用于显示好友是否在线的 `FriendStatus` 组件。从 class 中 props 读取 `friend.id`,然后在组件挂载后订阅好友的状态,并在卸载组件的时候取消订阅: ```js componentDidMount() { @@ -357,9 +357,9 @@ function FriendStatusWithCounter(props) { } ``` -**但是当组件已经显示在屏幕上时,`friend` prop 发生变化会发生什么?**我们的组件将继续展示原来的好友状态。这是一个 bug。而且我们还会因为取消订阅时使用错误的好友 ID 导致内存泄露或崩溃的问题。 +**但是当组件已经显示在屏幕上时,`friend` prop 发生变化时会发生什么?** 我们的组件将继续展示原来的好友状态。这是一个 bug。而且我们还会因为取消订阅时使用错误的好友 ID 导致内存泄露或崩溃的问题。 -在一个类定义组件中,我们需要加上 `componentDidUpdate` 来解决这个问题: +在 class 组件中,我们需要添加 `componentDidUpdate` 来解决这个问题: ```js{8-19} componentDidMount() { @@ -370,12 +370,12 @@ function FriendStatusWithCounter(props) { } componentDidUpdate(prevProps) { - // Unsubscribe from the previous friend.id + // 取消订阅之前的 friend.id ChatAPI.unsubscribeFromFriendStatus( prevProps.friend.id, this.handleStatusChange ); - // Subscribe to the next friend.id + // 订阅新的 friend.id ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange @@ -390,9 +390,9 @@ function FriendStatusWithCounter(props) { } ``` -忘记正确地处理 `componentDidUpdate` 是 React 应用中一个常见的 bug 来源。 +忘记正确地处理 `componentDidUpdate` 是 React 应用中常见的 bug 来源。 -现在看一下使用 Hook 的这个版本: +现在看一下使用 Hook 的版本: ```js function FriendStatus(props) { @@ -406,31 +406,31 @@ function FriendStatus(props) { }); ``` -它并不会受到这个 bug 的影响。(虽然我们没有对它做任何改动。) +它并不会受到此 bug 影响。(虽然我们没有对它做任何改动。) -我们并不需要特定的代码来处理更新的逻辑,因为 `useEffect` *默认*就会处理。它会在调用一个 effect 之前对前一个进行清理。为了说明这一点,下面按时间列出一个可能会产生的订阅和取消订阅操作调用序列: +并不需要特定的代码来处理更新逻辑,因为 `useEffect` *默认*就会处理。它会在调用一个新的 effect 之前对前一个 effect 进行清理。为了说明这一点,下面按时间列出一个可能会产生的订阅和取消订阅操作调用序列: ```js // Mount with { friend: { id: 100 } } props -ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run first effect +ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // 运行第一个 effect // Update with { friend: { id: 200 } } props -ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect -ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // Run next effect +ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一个 effect +ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // 运行下一个 effect // Update with { friend: { id: 300 } } props -ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect -ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run next effect +ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一个 effect +ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // 运行下一个 effect // Unmount -ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect +ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最好一个 effect ``` -这样的默认行为保证了一致性,避免了在类定义组件中常见的,由于没有处理更新逻辑而导致的 bug。 +此默认行为保证了一致性,避免了在 class 组件中因为没有处理更新逻辑而导致常见的 bug。 ### 提示: 通过跳过 Effect 进行性能优化 {#tip-optimizing-performance-by-skipping-effects} -在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 `componentDidUpdate` 中加入一些对 `prevProps` 或 `prevState` 进行比较的逻辑解决: +在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。在 class 组件中,我们可以通过在 `componentDidUpdate` 中添加对 `prevProps` 或 `prevState` 的比较逻辑解决: ```js componentDidUpdate(prevProps, prevState) { @@ -440,19 +440,19 @@ componentDidUpdate(prevProps, prevState) { } ``` -这是一个很常见的需求,所以它被内置到了 `useEffect` Hook API 中。如果某些特定值在两次重渲染之间没有发生变化,你可以告诉 React **跳过**对 effect 的调用,只要传递一个数组作为 `useEffect` 的第二个可选参数就可以了: +这是很常见的需求,所以它被内置到了 `useEffect` 的 Hook API 中。如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React **跳过**对 effect 的调用,只要传递数组作为 `useEffect` 的第二个可选参数即可: ```js{3} useEffect(() => { document.title = `You clicked ${count} times`; -}, [count]); // Only re-run the effect if count changes +}, [count]); // 仅在 count 更改时更新 ``` 上面这个示例中,我们传入 `[count]` 作为第二个参数。这个参数是什么作用呢?如果 `count` 的值是 `5`,而且我们的组件重渲染的时候 `count` 还是等于 `5`,React 将对前一次渲染的 `[5]` 和后一次渲染的 `[5]` 进行比较。因为数组中的所有元素都是相等的(`5 === 5`),React 会跳过这个 effect,这就实现了性能的优化。 -当我们渲染的时候,如果 `count` 的值更新成了 `6`,React 将会把前一次渲染时的数组 `[5]` 和这次渲染的数组 `[6]` 中的元素进行对比。这次因为 `5 !== 6`,React 就会再次调用 effect。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。 +当渲染时,如果 `count` 的值更新成了 `6`,React 将会把前一次渲染时的数组 `[5]` 和这次渲染的数组 `[6]` 中的元素进行对比。这次因为 `5 !== 6`,React 就会再次调用 effect。如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。 -对于有清理阶段的 effect 这也是同样有效的: +对于有清除操作的 effect 同样适用: ```js{10} useEffect(() => { @@ -464,25 +464,25 @@ useEffect(() => { return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; -}, [props.friend.id]); // Only re-subscribe if props.friend.id changes +}, [props.friend.id]); // 仅在 props.friend.id 发生变化时,重新订阅 ``` -将来,第二个参数可能会在构建时自动添加。 +未来版本,可能会在构建时自动添加第二个参数。 ->Note +>注意: > ->如果你要使用这个优化,请确保数组中包含了**所有外部作用域中会随时间变化并且在 effect 中使用的变量**,否则你的代码会引用到先前渲染中陈旧的变量。了解更多关于[如何处理函数](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)以及[数组频繁变化时的措施](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)内容。 +>如果你要使用此优化方式,请确保数组中包含了**所有外部作用域中会随时间变化并且在 effect 中使用的变量**,否则你的代码会引用到先前渲染中的旧变量。参阅文档,了解更多关于[如何处理函数](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)以及[数组频繁变化时的措施](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)内容。 > ->如果你想要执行一个只运行一次的 effect(仅在组件加载和卸载时执行),你可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 —— 它依然遵循输入数组的工作方式。 +>如果你想要执行只运行一次的 effect(仅在组件挂载和卸载时执行),你可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 —— 依然遵循输入数组的工作方式。 > ->如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。尽管传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,但我们有[更好的](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方法](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过过于频繁的重复调用 effect。除此之外,记住,React 直到浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此做额外的工作不是大问题。 +>如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。尽管传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,但我们有[更好的](/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方式](/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过于频繁的重复调用 effect。除此之外,请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此会使得额外操作很方便。 > ->我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。他会在错误指定依赖的时候发出警告并给出修复建议。 +>我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。此规则会在添加错误依赖时发出警告并给出修复建议。 ## 下一步 {#next-steps} -恭喜你!这是一个很长的页面,希望最后你关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 —— 如果没有,你可以查看[其他的 Hook](/docs/hooks-reference.html)。 +恭喜你!完成本章节学习,希望关于 effect 的大多数问题都得到了解答。你已经学习了 State Hook 和 Effect Hook,将它们结合起来你可以做很多事情了。它们涵盖了大多数使用 class 的用例 —— 如果没有,你可以查看[其他的 Hook](/docs/hooks-reference.html)。 -我们也看到了 Hook 如何解决[动机](/docs/hooks-intro.html#motivation)中提到的问题。我们看到 effect 的清理机制如何避免了 `componentDidUpdate` 和 `componentWillUnmount` 中的重复,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 +我们看到了 Hook 如何解决[简介章节中动机](/docs/hooks-intro.html#motivation)部分提出的问题。我们也发现 effect 的清除机制可以避免了 `componentDidUpdate` 和 `componentWillUnmount` 中的重复,同时让相关的代码关联更加紧密,帮助我们避免一些 bug。我们还看到了我们如何根据 effect 的功能分隔他们,这是在 class 中无法做到的。 -此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?React 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一页中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) —— 他们对 Hook 的工作至关重要。** +此时你可能会好奇 Hook 是如何工作的。在两次渲染间,React如何知道哪个 `useState` 调用对应于哪个 state 变量?React 又是如何匹配前后两次渲染中的每一个 effect 的?**在下一章节中我们会学习[使用 Hooks 的规则](/docs/hooks-rules.html) —— 这对 Hook 的工作至关重要。** From effe0def06415ae85656f5e9e5d033e38c8434b5 Mon Sep 17 00:00:00 2001 From: QiChang Li Date: Tue, 26 Mar 2019 14:07:53 +0800 Subject: [PATCH 51/51] Update hooks-effect.md --- content/docs/hooks-effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/hooks-effect.md b/content/docs/hooks-effect.md index a505213890..65c4c0eaae 100644 --- a/content/docs/hooks-effect.md +++ b/content/docs/hooks-effect.md @@ -139,7 +139,7 @@ function Example() { >提示 > -> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook 供你使用,其 API 与`useEffect`相同。 +> 与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 [`useLayoutEffect`](/docs/hooks-reference.html#uselayouteffect) Hook 供你使用,其 API 与 `useEffect` 相同。 ## 需要清除的 effect