diff --git a/content/docs/hooks-reference.md b/content/docs/hooks-reference.md index 24145eac21..96cc43a583 100644 --- a/content/docs/hooks-reference.md +++ b/content/docs/hooks-reference.md @@ -1,22 +1,22 @@ --- id: hooks-reference -title: Hooks API Reference +title: Hook API 索引 permalink: docs/hooks-reference.html prev: hooks-custom.html next: hooks-faq.html --- -*Hooks* are a new addition in React 16.8. They let you use state and other React features without writing a class. +*Hook* 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 -This page describes the APIs for the built-in Hooks in React. +本页面主要描述 React 中内置的 Hook API。 -If you're new to Hooks, you might want to check out [the overview](/docs/hooks-overview.html) first. You may also find useful information in the [frequently asked questions](/docs/hooks-faq.html) section. +如果你刚开始接触 Hook,那么可能需要先查阅 [Hook 概览](/docs/hooks-overview.html)。你也可以在 [Hooks FAQ](/docs/hooks-faq.html) 章节中获取有用的信息。 -- [Basic Hooks](#basic-hooks) +- [基础 Hook](#basic-hooks) - [`useState`](#usestate) - [`useEffect`](#useeffect) - [`useContext`](#usecontext) -- [Additional Hooks](#additional-hooks) +- [额外的 Hook](#additional-hooks) - [`useReducer`](#usereducer) - [`useCallback`](#usecallback) - [`useMemo`](#usememo) @@ -25,7 +25,7 @@ If you're new to Hooks, you might want to check out [the overview](/docs/hooks-o - [`useLayoutEffect`](#uselayouteffect) - [`useDebugValue`](#usedebugvalue) -## Basic Hooks {#basic-hooks} +## 基础 Hook {#basic-hooks} ### `useState` {#usestate} @@ -33,25 +33,25 @@ If you're new to Hooks, you might want to check out [the overview](/docs/hooks-o const [state, setState] = useState(initialState); ``` -Returns a stateful value, and a function to update it. +返回一个 state,以及更新 state 的函数。 -During the initial render, the returned state (`state`) is the same as the value passed as the first argument (`initialState`). +在初始渲染期间,返回的状态 (`state`) 与传入的第一个参数 (`initialState`) 值相同。。 -The `setState` function is used to update the state. It accepts a new state value and enqueues a re-render of the component. +`setState` 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。 ```js setState(newState); ``` -During subsequent re-renders, the first value returned by `useState` will always be the most recent state after applying updates. +在后续的重新渲染中,`useState` 返回的第一个值将始终是更新后最新的 state。 ->Note +> 注意 > ->React guarantees that `setState` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list. +>React 会确保 `setState` 函数的标识是稳定的,并且不会在组件重新渲染时发生变化。这就是为什么可以安全地从 `useEffect` 或 `useCallback` 的依赖列表中省略 `setState`。 -#### Functional updates {#functional-updates} +#### 函数式更新 {#functional-updates} -If the new state is computed using the previous state, you can pass a function to `setState`. The function will receive the previous value, and return an updated value. Here's an example of a counter component that uses both forms of `setState`: +如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 `setState`。该函数将接收先前的 state,并返回一个更新后的值。下面的计数器组件示例展示了 `setState` 的两种用法: ```js function Counter({initialCount}) { @@ -67,24 +67,24 @@ function Counter({initialCount}) { } ``` -The "+" and "-" buttons use the functional form, because the updated value is based on the previous value. But the "Reset" button uses the normal form, because it always sets the count back to the initial value. +“+” 和 “-” 按钮采用函数式形式,因为被更新的 state 需要基于之前的 state。但是“重置”按钮则采用普通形式,因为它总是把 count 设置回初始值。 -> Note +> 注意 > -> Unlike the `setState` method found in class components, `useState` does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax: +> 与 class 组件中的 `setState` 方法不同,`useState` 不会自动合并更新对象。你可以用函数式的 `setState` 结合展开运算符来达到合并更新对象的效果。 > > ```js > setState(prevState => { -> // Object.assign would also work +> // 也可以使用 Object.assign > return {...prevState, ...updatedValues}; > }); > ``` > -> Another option is `useReducer`, which is more suited for managing state objects that contain multiple sub-values. +> `useReducer` 是另一种可选方案,它更适合用于管理包含多个子值的 state 对象。 -#### Lazy initial state {#lazy-initial-state} +#### 惰性初始 state {#lazy-initial-state} -The `initialState` argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render: +`initialState` 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用: ```js const [state, setState] = useState(() => { @@ -93,11 +93,11 @@ const [state, setState] = useState(() => { }); ``` -#### Bailing out of a state update {#bailing-out-of-a-state-update} +#### 跳过 state 更新 {#bailing-out-of-a-state-update} -If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).) +调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 [`Object.is` 比较算法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description) 来比较 state。) -Note that React may still need to render that specific component again before bailing out. That shouldn't be a concern because React won't unnecessarily go "deeper" into the tree. If you're doing expensive calculations while rendering, you can optimize them with `useMemo`. +需要注意的是,React 可能仍需要在跳过渲染前渲染该组件。不过由于 React 不会对组件树的“深层”节点进行不必要的渲染,所以大可不必担心。如果你在渲染期间执行了高开销的计算,则可以使用 `useMemo` 来进行优化。 ### `useEffect` {#useeffect} @@ -105,45 +105,45 @@ Note that React may still need to render that specific component again before ba useEffect(didUpdate); ``` -Accepts a function that contains imperative, possibly effectful code. +该 Hook 接收一个包含命令式、且可能有副作用代码的函数。 -Mutations, subscriptions, timers, logging, and other side effects are not allowed inside the main body of a function component (referred to as React's _render phase_). Doing so will lead to confusing bugs and inconsistencies in the UI. +在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。 -Instead, use `useEffect`. The function passed to `useEffect` will run after the render is committed to the screen. Think of effects as an escape hatch from React's purely functional world into the imperative world. +使用 `useEffect` 完成副作用操作。赋值给 `useEffect` 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。 -By default, effects run after every completed render, but you can choose to fire it [only when certain values have changed](#conditionally-firing-an-effect). +默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 [在只有某些值改变的时候](#conditionally-firing-an-effect) 才执行。 -#### Cleaning up an effect {#cleaning-up-an-effect} +#### 清除 effect {#cleaning-up-an-effect} -Often, effects create resources that need to be cleaned up before the component leaves the screen, such as a subscription or timer ID. To do this, the function passed to `useEffect` may return a clean-up function. For example, to create a subscription: +通常,组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点,`useEffect` 函数需返回一个清除函数。以下就是一个创建订阅的例子: ```js useEffect(() => { const subscription = props.source.subscribe(); return () => { - // Clean up the subscription + // 清除订阅 subscription.unsubscribe(); }; }); ``` -The clean-up function runs before the component is removed from the UI to prevent memory leaks. Additionally, if a component renders multiple times (as they typically do), the **previous effect is cleaned up before executing the next effect**. In our example, this means a new subscription is created on every update. To avoid firing an effect on every update, refer to the next section. +为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染(通常如此),则**在执行下一个 effect 之前,上一个 effect 就已被清除**。在上述示例中,意味着组件的每一次更新都会创建新的订阅。若想避免每次更新都触发 effect 的执行,请参阅下一小节。 -#### Timing of effects {#timing-of-effects} +#### effect 的执行时机 {#timing-of-effects} -Unlike `componentDidMount` and `componentDidUpdate`, the function passed to `useEffect` fires **after** layout and paint, during a deferred event. This makes it suitable for the many common side effects, like setting up subscriptions and event handlers, because most types of work shouldn't block the browser from updating the screen. +与 `componentDidMount`、`componentDidUpdate` 不同的是,在浏览器完成布局与绘制**之后**,传给 `useEffect` 的函数会延迟调用。这使得它适用于许多常见的副作用场景,比如如设置订阅和事件处理等情况,因此不应在函数中执行阻塞浏览器更新屏幕的操作。 -However, not all effects can be deferred. For example, a DOM mutation that is visible to the user must fire synchronously before the next paint so that the user does not perceive a visual inconsistency. (The distinction is conceptually similar to passive versus active event listeners.) For these types of effects, React provides one additional Hook called [`useLayoutEffect`](#uselayouteffect). It has the same signature as `useEffect`, and only differs in when it is fired. +然而,并非所有 effect 都可以被延迟执行。例如,在浏览器执行下一次绘制前,用户可见的 DOM 变更就必须同步执行,这样用户才不会感觉到视觉上的不一致。(概念上类似于被动监听事件和主动监听事件的区别。)React 为此提供了一个额外的 [`useLayoutEffect`](#uselayouteffect) Hook 来处理这类 effect。它和 `useEffect` 的结构相同,区别只是调用时机不同。 -Although `useEffect` is deferred until after the browser has painted, it's guaranteed to fire before any new renders. React will always flush a previous render's effects before starting a new update. +虽然 `useEffect` 会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。React 将在组件更新前刷新上一轮渲染的 effect。 -#### Conditionally firing an effect {#conditionally-firing-an-effect} +#### effect 的条件执行 {#conditionally-firing-an-effect} -The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes. +默认情况下,effect 会在每轮组件渲染完成后执行。这样的话,一旦 effect 的依赖发生变化,它就会被重新创建。 -However, this may be overkill in some cases, like the subscription example from the previous section. We don't need to create a new subscription on every update, only if the `source` props has changed. +然而,在某些场景下这么做可能会矫枉过正。比如,在上一章节的订阅示例中,我们不需要在每次组件更新时都创建新的订阅,而是仅需要在 `source` props 改变时重新创建。 -To implement this, pass a second argument to `useEffect` that is the array of values that the effect depends on. Our updated example now looks like this: +要实现这一点,可以给 `useEffect` 传递第二个参数,它是 effect 所依赖的值数组。更新后的示例如下: ```js useEffect( @@ -157,20 +157,20 @@ useEffect( ); ``` -Now the subscription will only be recreated when `props.source` changes. +此时,只有当 `props.source` 改变后才会重新创建订阅。 ->Note +>注意 > ->If you use this optimization, make sure the array includes **all values from the component scope (such as props and state) that change over time and that are used by the effect**. Otherwise, your code will reference stale values from previous renders. 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 values change 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) 的内容。 > ->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 dependencies array always works. +>如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组(`[]`)作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循输入数组的工作方式。 > ->If you pass an empty array (`[]`), the props and state as 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. +>我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。此规则会在添加错误依赖时发出警告并给出修复建议。 -The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that's what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +依赖项数组不会作为参数传给 effect 函数。虽然从概念上来说它表现为:所有 effect 函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。 ### `useContext` {#usecontext} @@ -178,27 +178,27 @@ The array of dependencies is not passed as arguments to the effect function. Con const value = useContext(MyContext); ``` -Accepts a context object (the value returned from `React.createContext`) and returns the current context value for that context. The current context value is determined by the `value` prop of the nearest `` above the calling component in the tree. +接收一个 context 对象(`React.createContext` 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 `` 的 `value` prop 决定。 -When the nearest `` above the component updates, this Hook will trigger a rerender with the latest context `value` passed to that `MyContext` provider. +当组件上层最近的 `` 更新时,该 Hook 会触发重渲染,并将最新的 context 的 `value` 值传递给 `MyContext` 的 provider。 -Don't forget that the argument to `useContext` must be the *context object itself*: +别忘记 `useContext` 的参数必须是 *context 对象本身*: - * **Correct:** `useContext(MyContext)` - * **Incorrect:** `useContext(MyContext.Consumer)` - * **Incorrect:** `useContext(MyContext.Provider)` + * **正确:** `useContext(MyContext)` + * **错误:** `useContext(MyContext.Consumer)` + * **错误:** `useContext(MyContext.Provider)` -A component calling `useContext` will always re-render when the context value changes. If re-rendering the component is expensive, you can [optimize it by using memoization](https://github.com/facebook/react/issues/15156#issuecomment-474590693). +调用了 `useContext` 的组件总会在 context 值变化时重新渲染。如果重渲染组件的开销较大,你可以 [通过使用 memoization 来优化](https://github.com/facebook/react/issues/15156#issuecomment-474590693)。 ->Tip +>提示 > ->If you're familiar with the context API before Hooks, `useContext(MyContext)` is equivalent to `static contextType = MyContext` in a class, or to ``. +>如果你在接触 Hook 前已经对 context API 比较熟悉,那应该可以理解,`useContext(MyContext)` 相当于 class 组件中的 `static contextType = MyContext` 或者 ``。 > ->`useContext(MyContext)` only lets you *read* the context and subscribe to its changes. You still need a `` above in the tree to *provide* the value for this context. +>`useContext(MyContext)` 只是让你能够*读取* context 的值以及订阅 context 的变化。你仍然需要在上层组件树中使用 `` 来为下层组件*提供* context。 -## Additional Hooks {#additional-hooks} +## 额外的 Hook {#additional-hooks} -The following Hooks are either variants of the basic ones from the previous section, or only needed for specific edge cases. Don't stress about learning them up front. +以下介绍的 Hook,有些是上一节中基础 Hook 的变体,有些则仅在特殊情况下会用到。不用特意预先学习它们。 ### `useReducer` {#usereducer} @@ -206,11 +206,11 @@ The following Hooks are either variants of the basic ones from the previous sect const [state, dispatch] = useReducer(reducer, initialArg, init); ``` -An alternative to [`useState`](#usestate). Accepts a reducer of type `(state, action) => newState`, and returns the current state paired with a `dispatch` method. (If you're familiar with Redux, you already know how this works.) +[`useState`](#usestate) 的替代方案。它接收一个形如 `(state, action) => newState` 的 reducer,并返回当前的 state 以及与其配套的 `dispatch` 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。) -`useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because [you can pass `dispatch` down instead of callbacks](/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down). +在某些场景下,`useReducer` 会比 `useState` 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 `useReducer` 还能给那些会触发深更新的组件做性能优化,因为[你可以向子组件传递 `dispatch` 而不是回调函数](/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down) 。 -Here's the counter example from the [`useState`](#usestate) section, rewritten to use a reducer: +以下是用 reducer 重写 [`useState`](#usestate) 一节的计数器示例: ```js const initialState = {count: 0}; @@ -238,13 +238,13 @@ function Counter({initialState}) { } ``` ->Note +>注意 > ->React guarantees that `dispatch` function identity is stable and won't change on re-renders. This is why it's safe to omit from the `useEffect` or `useCallback` dependency list. +>React 会确保 `dispatch` 函数的标识是稳定的,并且不会在组件重新渲染时改变。这就是为什么可以安全地从 `useEffect` 或 `useCallback` 的依赖列表中省略 `dispatch`。 -#### Specifying the initial state {#specifying-the-initial-state} +#### 指定初始 state {#specifying-the-initial-state} -There’s two different ways to initialize `useReducer` state. You may choose either one depending on the use case. The simplest way to pass the initial state as a second argument: +有两种不同初始化 `useReducer` state 的方式,你可以根据使用场景选择其中的一种。将初始 state 作为第二个参数传入 `useReducer` 是最简单的方法: ```js{3} const [state, dispatch] = useReducer( @@ -253,15 +253,15 @@ There’s two different ways to initialize `useReducer` state. You may choose ei ); ``` ->Note +>注意 > ->React doesn’t use the `state = initialState` argument convention popularized by Redux. The initial value sometimes needs to depend on props and so is specified from the Hook call instead. If you feel strongly about this, you can call `useReducer(reducer, undefined, reducer)` to emulate the Redux behavior, but it's not encouraged. +>React 不使用 `state = initialState` 这一由 Redux 推广开来的参数约定。有时候初始值依赖于 props,因此需要在调用 Hook 时指定。如果你特别喜欢上述的参数约定,可以通过调用 `useReducer(reducer, undefined, reducer)` 来模拟 Redux 的行为,但我们不鼓励你这么做。 -#### Lazy initialization {#lazy-initialization} +#### 惰性初始化 {#lazy-initialization} -You can also create the initial state lazily. To do this, you can pass an `init` function as the third argument. The initial state will be set to `init(initialArg)`. +你可以选择惰性地创建初始 state。为此,需要将 `init` 函数作为 `useReducer` 的第三个参数传入,这样初始 state 将被设置为 `init(initialArg)`。 -It lets you extract the logic for calculating the initial state outside the reducer. This is also handy for resetting the state later in response to an action: +这么做可以将用于计算 state 的逻辑提取到 reducer 外部,这也为将来对重置 state 的 action 做处理提供了便利: ```js{1-3,11-12,19,24} function init(initialCount) { @@ -297,11 +297,11 @@ function Counter({initialCount}) { } ``` -#### Bailing out of a dispatch {#bailing-out-of-a-dispatch} +#### 跳过 dispatch {#bailing-out-of-a-dispatch} -If you return the same value from a Reducer Hook as the current state, React will bail out without rendering the children or firing effects. (React uses the [`Object.is` comparison algorithm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).) +如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行。(React 使用 [`Object.is` 比较算法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description) 来比较 state。) -Note that React may still need to render that specific component again before bailing out. That shouldn't be a concern because React won't unnecessarily go "deeper" into the tree. If you're doing expensive calculations while rendering, you can optimize them with `useMemo`. +需要注意的是,React 可能仍需要在跳过渲染前再次渲染该组件。不过由于 React 不会对组件树的“深层”节点进行不必要的渲染,所以大可不必担心。如果你在渲染期间执行了高开销的计算,则可以使用 `useMemo` 来进行优化。 ### `useCallback` {#usecallback} @@ -314,17 +314,17 @@ const memoizedCallback = useCallback( ); ``` -Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) callback. +返回一个 [memoized](https://en.wikipedia.org/wiki/Memoization) 回调函数。 -Pass an inline callback and an array of dependencies. `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. `shouldComponentUpdate`). +把内联回调函数及依赖项数组作为参数传入 `useCallback`,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 `shouldComponentUpdate`)的子组件时,它将非常有用。 -`useCallback(fn, deps)` is equivalent to `useMemo(() => fn, deps)`. +`useCallback(fn, deps)` 相当于 `useMemo(() => fn, deps)`。 -> Note +> 注意 > -> The array of dependencies is not passed as arguments to the callback. Conceptually, though, that's what they represent: every value referenced inside the callback should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +> 依赖项数组不会作为参数传给回调函数。虽然从概念上来说它表现为:所有回调函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。 > -> 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. +> 我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。此规则会在添加错误依赖时发出警告并给出修复建议。 ### `useMemo` {#usememo} @@ -332,21 +332,21 @@ Pass an inline callback and an array of dependencies. `useCallback` will return const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); ``` -Returns a [memoized](https://en.wikipedia.org/wiki/Memoization) value. +返回一个 [memoized](https://en.wikipedia.org/wiki/Memoization) 值。 -Pass a "create" function and an array of dependencies. `useMemo` will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render. +把“创建”函数和依赖项数组作为参数传入 `useMemo`,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。 -Remember that the function passed to `useMemo` runs during rendering. Don't do anything there that you wouldn't normally do while rendering. For example, side effects belong in `useEffect`, not `useMemo`. +记住,传入 `useMemo` 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 `useEffect` 的适用范畴,而不是 `useMemo`。 -If no array is provided, a new value will be computed on every render. +如果没有提供依赖项数组,`useMemo` 在每次渲染时都会计算新的值。 -**You may rely on `useMemo` as a performance optimization, not as a semantic guarantee.** In the future, React may choose to "forget" some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without `useMemo` — and then add it to optimize performance. + **你可以把 `useMemo` 作为性能优化的手段,但不要把它当成语义上的保证。**将来,React 可能会选择“遗忘”以前的一些 memoized 值,并在下次渲染时重新计算它们,比如为离屏组件释放内存。先编写在没有 `useMemo` 的情况下也可以执行的代码 —— 之后再在你的代码中添加 `useMemo`,以达到优化性能的目的。 -> Note +> 注意 > -> The array of dependencies is not passed as arguments to the function. Conceptually, though, that's what they represent: every value referenced inside the function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically. +> 依赖项数组不会作为参数传给“创建”函数。然虽然从概念上来说它表现为:所有“创建”函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。 > -> 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. +> 我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。此规则会在添加错误依赖时发出警告并给出修复建议。 ### `useRef` {#useref} @@ -354,15 +354,15 @@ If no array is provided, a new value will be computed on every render. const refContainer = useRef(initialValue); ``` -`useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument (`initialValue`). The returned object will persist for the full lifetime of the component. +`useRef` 返回一个可变的 ref 对象,其 `.current` 属性被初始化为传入的参数(`initialValue`)。返回的 ref 对象在组件的整个生命周期内保持不变。 -A common use case is to access a child imperatively: +一个常见的用例便是命令式地访问子组件: ```js function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { - // `current` points to the mounted text input element + // `current` 指向已挂载到 DOM 上的文本输入元素 inputEl.current.focus(); }; return ( @@ -374,15 +374,15 @@ function TextInputWithFocusButton() { } ``` -Essentially, `useRef` is like a "box" that can hold a mutable value in its `.current` property. +本质上,`useRef` 就像是可以在其 `.current` 属性中保存一个可变值的“盒子”。 -You might be familiar with refs primarily as a way to [access the DOM](/docs/refs-and-the-dom.html). If you pass a ref object to React with `
`, React will set its `.current` property to the corresponding DOM node whenever that node changes. +你应该熟悉 ref 这一种[访问 DOM](/docs/refs-and-the-dom.html) 的主要方式。如果你将 ref 对象以 `
` 形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 `.current` 属性设置为相应的 DOM 节点。 -However, `useRef()` is useful for more than the `ref` attribute. It's [handy for keeping any mutable value around](/docs/hooks-faq.html#is-there-something-like-instance-variables) similar to how you'd use instance fields in classes. +然而,`useRef()` 比 `ref` 属性更有用。它可以[很方便地保存任何可变值](/docs/hooks-faq.html#is-there-something-like-instance-variables),其类似于在 class 中使用实例字段的方式。 -This works because `useRef()` creates a plain JavaScript object. The only difference between `useRef()` and creating a `{current: ...}` object yourself is that `useRef` will give you the same ref object on every render. +这是因为它创建的是一个普通 Javascript 对象。而 `useRef()` 和自建一个 `{current: ...}` 对象的唯一区别是,`useRef` 会在每次渲染时返回同一个 ref 对象。 -Keep in mind that `useRef` *doesn't* notify you when its content changes. Mutating the `.current` property doesn't cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a [callback ref](/docs/hooks-faq.html#how-can-i-measure-a-dom-node) instead. +请记住,当 ref 对象内容发生变化时,`useRef` 并*不会*通知你。变更 `.current` 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用[回调 ref](/docs/hooks-faq.html#how-can-i-measure-a-dom-node) 来实现。 ### `useImperativeHandle` {#useimperativehandle} @@ -391,7 +391,7 @@ Keep in mind that `useRef` *doesn't* notify you when its content changes. Mutati useImperativeHandle(ref, createHandle, [deps]) ``` -`useImperativeHandle` customizes the instance value that is exposed to parent components when using `ref`. As always, imperative code using refs should be avoided in most cases. `useImperativeHandle` should be used with `forwardRef`: +`useImperativeHandle` 可以让你在使用 `ref` 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。`useImperativeHandle` 应当与 `forwardRef` 一起使用: ```js function FancyInput(props, ref) { @@ -406,21 +406,21 @@ function FancyInput(props, ref) { FancyInput = forwardRef(FancyInput); ``` -In this example, a parent component that renders `` would be able to call `fancyInputRef.current.focus()`. +在本例中,渲染 `` 的父组件可以调用 `fancyInputRef.current.focus()`。 ### `useLayoutEffect` {#uselayouteffect} -The signature is identical to `useEffect`, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside `useLayoutEffect` will be flushed synchronously, before the browser has a chance to paint. +其函数签名与 `useEffect` 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,`useLayoutEffect` 内部的更新计划将被同步刷新。 -Prefer the standard `useEffect` when possible to avoid blocking visual updates. +尽可能使用标准的 `useEffect` 以避免阻塞视觉更新。 -> Tip +> 提示 > -> If you're migrating code from a class component, note `useLayoutEffect` fires in the same phase as `componentDidMount` and `componentDidUpdate`. However, **we recommend starting with `useEffect` first** and only trying `useLayoutEffect` if that causes a problem. +> 如果你正在将代码从 class 组件迁移到使用 Hook 的函数组件,则需要注意 `useLayoutEffect` 与 `componentDidMount`、`componentDidUpdate` 的调用阶段是一样的。但是,我们推荐你**一开始先用 `useEffect`**,只有当它出问题的时再尝试使用 `useLayoutEffect`。 > ->If you use server rendering, keep in mind that *neither* `useLayoutEffect` nor `useEffect` can run until the JavaScript is downloaded. This is why React warns when a server-rendered component contains `useLayoutEffect`. To fix this, either move that logic to `useEffect` (if it isn't necessary for the first render), or delay showing that component until after the client renders (if the HTML looks broken until `useLayoutEffect` runs). +>如果你使用服务端渲染,请记住,*无论* `useLayoutEffect` *还是* `useEffect` 都无法在 Javascript 代码加载完成之前执行。这就是为什么在服务端渲染组件中引入 `useLayoutEffect` 代码时会触发 React 告警。解决这个问题,需要将代码逻辑移至 `useEffect` 中(如果首次渲染不需要这段逻辑的情况下),或是将该组件延迟到客户端渲染完成后再显示(如果直到 `useLayoutEffect` 执行之前 HTML 都显示错乱的情况下)。 > ->To exclude a component that needs layout effects from the server-rendered HTML, render it conditionally with `showChild && ` and defer showing it with `useEffect(() => { setShowChild(true); }, [])`. This way, the UI doesn't appear broken before hydration. +>若要从服务端渲染的 HTML 中排除依赖布局 effect 的组件,可以通过使用 `showChild && ` 进行条件渲染,并使用 `useEffect(() => { setShowChild(true); }, [])` 延迟展示组件。这样,在客户端渲染完成之前,UI 就不会像之前那样显示错乱了。 ### `useDebugValue` {#usedebugvalue} @@ -428,9 +428,9 @@ Prefer the standard `useEffect` when possible to avoid blocking visual updates. useDebugValue(value) ``` -`useDebugValue` can be used to display a label for custom hooks in React DevTools. +`useDebugValue` 可用于在 React 开发者工具中显示自定义 hook 的标签。 -For example, consider the `useFriendStatus` custom Hook described in ["Building Your Own Hooks"](/docs/hooks-custom.html): +例如,[“自定义 Hook”](/docs/hooks-custom.html) 章节中描述的名为 `useFriendStatus` 的自定义 Hook: ```js{6-8} function useFriendStatus(friendID) { @@ -438,7 +438,7 @@ function useFriendStatus(friendID) { // ... - // Show a label in DevTools next to this Hook + // 在开发者工具中的这个 Hook 旁边显示标签 // e.g. "FriendStatus: Online" useDebugValue(isOnline ? 'Online' : 'Offline'); @@ -446,17 +446,17 @@ function useFriendStatus(friendID) { } ``` -> Tip +> 提示 > -> We don't recommend adding debug values to every custom Hook. It's most valuable for custom Hooks that are part of shared libraries. +> 我们不推荐你向每个自定义 Hook 添加 debug 值。当它作为共享库的一部分时才最有价值。 -#### Defer formatting debug values {#defer-formatting-debug-values} +#### 延迟格式化 debug 值 {#defer-formatting-debug-values} -In some cases formatting a value for display might be an expensive operation. It's also unnecessary unless a Hook is actually inspected. +在某些情况下,格式化值的显示可能是一项开销很大的操作。除非需要检查 Hook,否则没有必要这么做。 -For this reason `useDebugValue` accepts a formatting function as an optional second parameter. This function is only called if the Hooks are inspected. It receives the debug value as a parameter and should return a formatted display value. +因此,`useDebugValue` 接受一个格式化函数作为可选的第二个参数。该函数只有在 Hook 被检查时才会被调用。它接受 debug 值作为参数,并且会返回一个格式化的显示值。 -For example a custom Hook that returned a `Date` value could avoid calling the `toDateString` function unnecessarily by passing the following formatter: +例如,一个返回 `Date` 值的自定义 Hook 可以通过格式化函数来避免不必要的 `toDateString` 函数调用: ```js useDebugValue(date, date => date.toDateString());