hooks-pattern

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Hooks Pattern

Hooks模式

React 16.8 introduced a new feature called Hooks. Hooks make it possible to use React state and lifecycle methods, without having to use an ES2015 class component.
Although Hooks are not necessarily a design pattern, Hooks play a very important role in your application design. Many traditional design patterns can be replaced by Hooks.
React 16.8 引入了一项名为Hooks的新特性。借助Hooks,无需使用ES2015类组件即可使用React状态和生命周期方法。
尽管Hooks不一定属于设计模式,但它在应用设计中扮演着非常重要的角色。许多传统设计模式都可以被Hooks替代。

When to Use

适用场景

  • Use this when you need to add state or lifecycle behavior to functional components
  • This is helpful for extracting and reusing stateful logic across multiple components
  • Use this instead of class components for cleaner, more composable code
  • 当你需要为函数式组件添加状态或生命周期行为时使用
  • 有助于提取并在多个组件之间复用有状态逻辑
  • 替代类组件,编写更简洁、更具组合性的代码

Instructions

使用说明

  • Use
    useState
    for local state and
    useEffect
    for side effects in functional components
  • Create custom hooks (prefixed with
    use
    ) to encapsulate and share reusable logic
  • Follow the Rules of Hooks: only call hooks at the top level and only in React functions
  • Avoid unnecessary
    useEffect
    — compute derived state directly in the component body
  • Let the React Compiler handle memoization instead of manual
    useMemo
    /
    useCallback
    where possible
  • 在函数式组件中使用
    useState
    管理本地状态,使用
    useEffect
    处理副作用
  • 创建自定义Hooks(以
    use
    为前缀)来封装和共享可复用逻辑
  • 遵循Hooks规则:仅在React函数的顶层调用Hooks
  • 避免不必要的
    useEffect
    ——直接在组件主体中计算派生状态
  • 尽可能让React编译器处理记忆化,而非手动使用
    useMemo
    /
    useCallback

Details

详细内容

Class components

类组件

Before Hooks were introduced in React, we had to use class components in order to add state and lifecycle methods to components. A typical class component in React can look something like:
js
class MyComponent extends React.Component {
  /* Adding state and binding custom methods */
  constructor() {
    super()
    this.state = { ... }

    this.customMethodOne = this.customMethodOne.bind(this)
    this.customMethodTwo = this.customMethodTwo.bind(this)
  }

  /* Lifecycle Methods */
  componentDidMount() { ...}
  componentWillUnmount() { ... }

  /* Custom methods */
  customMethodOne() { ... }
  customMethodTwo() { ... }

  render() { return { ... }}
}
A class component can contain a state in its constructor, lifecycle methods such as
componentDidMount
and
componentWillUnmount
to perform side effects based on a component's lifecycle, and custom methods to add extra logic to a class.
Although we can still use class components after the introduction of React Hooks, using class components can have some downsides! Let's look at some of the most common issues when using class components.
在React引入Hooks之前,我们必须使用类组件才能为组件添加状态和生命周期方法。一个典型的React类组件如下所示:
js
class MyComponent extends React.Component {
  /* 添加状态并绑定自定义方法 */
  constructor() {
    super()
    this.state = { ... }

    this.customMethodOne = this.customMethodOne.bind(this)
    this.customMethodTwo = this.customMethodTwo.bind(this)
  }

  /* 生命周期方法 */
  componentDidMount() { ...}
  componentWillUnmount() { ... }

  /* 自定义方法 */
  customMethodOne() { ... }
  customMethodTwo() { ... }

  render() { return { ... }}
}
类组件可以在构造函数中包含状态,通过
componentDidMount
componentWillUnmount
等生命周期方法根据组件生命周期执行副作用,还可以添加自定义方法来扩展类的逻辑。
尽管在React Hooks引入后我们仍可使用类组件,但类组件存在一些弊端!让我们看看使用类组件时最常见的几个问题。

Understanding ES2015 classes

理解ES2015类

Since class components were the only component that could handle state and lifecycle methods before React Hooks, we often ended up having to refactor functional components into a class components, in order to add the extra functionality.
In this example, we have a simple
div
that functions as a button.
js
function Button() {
  return <div className="btn">disabled</div>;
}
Instead of always displaying
disabled
, we want to change it to
enabled
when the user clicks on the button, and add some extra CSS styling to the button when that happens.
In order to do that, we need to add state to the component in order to know whether the status is
enabled
or
disabled
. This means that we'd have to refactor the functional component entirely, and make it a class component that keeps track of the button's state.
js
export default class Button extends React.Component {
  constructor() {
    super();
    this.state = { enabled: false };
  }

  render() {
    const { enabled } = this.state;
    const btnText = enabled ? "enabled" : "disabled";

    return (
      <div
        className={`btn enabled-${enabled}`}
        onClick={() => this.setState({ enabled: !enabled })}
      >
        {btnText}
      </div>
    );
  }
}
In this example, the component is very small and refactoring wasn't a such a great deal. However, your real-life components probably contain of many more lines of code, which makes refactoring the component a lot more difficult.
Besides having to make sure you don't accidentally change any behavior while refactoring the component, you also need to understand how ES2015 classes work. Why do we have to
bind
the custom methods? What does the
constructor
do? Where does the
this
keyword come from? It can be difficult to know how to refactor a component properly without accidentally changing the data flow.
由于在React Hooks出现之前,类组件是唯一能处理状态和生命周期方法的组件类型,我们常常需要将函数式组件重构为类组件,以添加额外功能。
举个例子,我们有一个简单的
div
作为按钮。
js
function Button() {
  return <div className="btn">disabled</div>;
}
我们希望用户点击按钮时,将文本从
disabled
改为
enabled
,并为此添加一些额外的CSS样式。
要实现这一点,我们需要为组件添加状态,以跟踪按钮是处于
enabled
还是
disabled
状态。这意味着我们必须完全重构函数式组件,将其转换为跟踪按钮状态的类组件。
js
export default class Button extends React.Component {
  constructor() {
    super();
    this.state = { enabled: false };
  }

  render() {
    const { enabled } = this.state;
    const btnText = enabled ? "enabled" : "disabled";

    return (
      <div
        className={`btn enabled-${enabled}`}
        onClick={() => this.setState({ enabled: !enabled })}
      >
        {btnText}
      </div>
    );
  }
}
在这个例子中,组件很小,重构起来不算麻烦。但实际项目中的组件可能包含更多代码,这会让重构工作变得困难得多。
除了要确保重构时不会意外改变任何行为外,你还需要理解ES2015类的工作原理。为什么我们必须绑定自定义方法?构造函数的作用是什么?
this
关键字来自哪里?如果不了解这些,就很难正确重构组件而不意外改变数据流。

Restructuring

重构复杂度

The common way to share code among several components, is by using the Higher Order Component or Render Props pattern. Although both patterns are valid and a good practice, adding those patterns at a later point in time requires you to restructure your application.
Besides having to restructure your app, which is trickier the bigger your components are, having many wrapping components in order to share code among deeper nested components can lead to something that's best referred to as a wrapper hell. It's not uncommon to open your dev tools and seeing a structure similar to:
js
<WrapperOne>
  <WrapperTwo>
    <WrapperThree>
      <WrapperFour>
        <WrapperFive>
          <Component>
            <h1>Finally in the component!</h1>
          </Component>
        </WrapperFive>
      </WrapperFour>
    </WrapperThree>
  </WrapperTwo>
</WrapperOne>
The wrapper hell can make it difficult to understand how data is flowing through your application, which can make it harder to figure out why unexpected behavior is happening.
在多个组件之间共享代码的常见方式是使用高阶组件(HOC)或渲染属性(Render Props)模式。尽管这两种模式都是有效的最佳实践,但在后期引入这些模式时需要重构应用。
除了重构应用(组件越大,重构越棘手),为了在深层嵌套组件之间共享代码而添加的大量包装组件,可能会导致所谓的**“包装地狱”**。打开开发者工具时,你可能会看到类似这样的结构:
js
<WrapperOne>
  <WrapperTwo>
    <WrapperThree>
      <WrapperFour>
        <WrapperFive>
          <Component>
            <h1>终于到目标组件了!</h1>
          </Component>
        </WrapperFive>
      </WrapperFour>
    </WrapperThree>
  </WrapperTwo>
</WrapperOne>
“包装地狱”会让你难以理解应用中的数据流,从而更难排查意外行为的原因。

Complexity

逻辑复杂度

As we add more logic to class components, the size of the component increases fast. Logic within that component can get tangled and unstructured, which can make it difficult for developers to understand where certain logic is used in the class component. This can make debugging and optimizing performance more difficult.
Lifecycle methods also require quite a lot of duplication in the code.
Although a component may be small, the logic within the component can already be quite tangled. Whereas certain parts are specific for the
counter
logic, other parts are specific for the
width
logic. As your component grows, it can get increasingly difficult to structure logic within your component, find related logic within the component.
Besides tangled logic, we're also duplicating some logic within the lifecycle methods. In both
componentDidMount
and
componentWillUnmount
, we're customizing the behavior of the app based on the window's
resize
event.
随着我们为类组件添加更多逻辑,组件的体积会迅速增大。组件内的逻辑会变得混乱且结构不清晰,这会让开发者难以理解类组件中特定逻辑的使用位置,从而增加调试和性能优化的难度。
生命周期方法还会导致大量代码重复。
即使组件很小,其内部的逻辑也可能已经相当混乱。某些部分属于
counter
逻辑,其他部分属于
width
逻辑。随着组件的增长,你会越来越难以组织组件内的逻辑,也难以在组件中找到相关逻辑。
除了逻辑混乱,我们还在生命周期方法中重复了一些逻辑。在
componentDidMount
componentWillUnmount
中,我们都根据窗口的
resize
事件自定义应用行为。

Hooks

Hooks

It's quite clear that class components aren't always a great feature in React. In order to solve the common issues that React developers can run into when using class components, React introduced React Hooks. React Hooks are functions that you can use to manage a components state and lifecycle methods. React Hooks make it possible to:
  • add state to a functional component
  • manage a component's lifecycle without having to use lifecycle methods such as
    componentDidMount
    and
    componentWillUnmount
  • reuse the same stateful logic among multiple components throughout the app
First, let's take a look at how we can add state to a functional component, using React Hooks.
显然,类组件并非React中总是好用的特性。为了解决React开发者使用类组件时遇到的常见问题,React引入了React Hooks。React Hooks是一些函数,可用于管理组件的状态和生命周期方法。借助React Hooks,你可以:
  • 为函数式组件添加状态
  • 无需使用
    componentDidMount
    componentWillUnmount
    等生命周期方法即可管理组件的生命周期
  • 在应用的多个组件之间复用相同的有状态逻辑
首先,让我们看看如何使用React Hooks为函数式组件添加状态。

State Hook

状态Hook

React provides a hook that manages state within a functional component, called
useState
.
Let's see how a class component can be restructured into a functional component, using the
useState
hook. We have a class component called
Input
, which simply renders an input field. The value of
input
in the state updates, whenever the user types anything in the input field.
js
class Input extends React.Component {
  constructor() {
    super();
    this.state = { input: "" };

    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(e) {
    this.setState({ input: e.target.value });
  }

  render() {
    <input onChange={handleInput} value={this.state.input} />;
  }
}
In order to use the
useState
hook, we need to access the
useState
method that React provides for us. The
useState
method expects an argument: this is the initial value of the state, an empty string in this case.
We can destructure two values from the
useState
method:
  1. The current value of the state.
  2. The method with which we can update the state.
js
const [value, setValue] = React.useState(initialValue);
The first value can be compared to a class component's
this.state.[value]
. The second value can be compared to a class component's
this.setState
method.
Since we're dealing with the value of an input, let's call the current value of the state
input
, and the method in order to update the state
setInput
. The initial value should be an empty string.
js
const [input, setInput] = React.useState("");
We can now refactor the
Input
class component into a stateful functional component.
js
function Input() {
  const [input, setInput] = React.useState("");

  return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}
The value of the
input
field is equal to the current value of the
input
state, just like in the class component example. When the user types in the input field, the value of the
input
state updates accordingly, using the
setInput
method.
React提供了一个用于管理函数式组件状态的Hook,名为
useState
让我们看看如何使用
useState
Hook将类组件重构为函数式组件。我们有一个名为
Input
的类组件,它仅渲染一个输入框。当用户在输入框中输入内容时,状态中的
input
值会更新。
js
class Input extends React.Component {
  constructor() {
    super();
    this.state = { input: "" };

    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(e) {
    this.setState({ input: e.target.value });
  }

  render() {
    <input onChange={handleInput} value={this.state.input} />;
  }
}
要使用
useState
Hook,我们需要调用React提供的
useState
方法。
useState
方法接受一个参数:状态的初始值,这里是空字符串。
我们可以从
useState
方法中解构出两个值:
  1. 状态的当前值
  2. 用于更新状态的方法。
js
const [value, setValue] = React.useState(initialValue);
第一个值相当于类组件中的
this.state.[value]
。 第二个值相当于类组件中的
this.setState
方法。
由于我们处理的是输入框的值,我们将状态的当前值命名为
input
,将更新状态的方法命名为
setInput
。初始值为空字符串。
js
const [input, setInput] = React.useState("");
现在我们可以将
Input
类组件重构为有状态的函数式组件。
js
function Input() {
  const [input, setInput] = React.useState("");

  return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}
输入框的值等于
input
状态的当前值,与类组件示例中的情况相同。当用户在输入框中输入内容时,
input
状态的值会通过
setInput
方法相应更新。

Effect Hook

副作用Hook

We've seen we can use the
useState
component to handle state within a functional component, but another benefit of class components was the possibility to add lifecycle methods to a component.
With the
useEffect
hook, we can "hook into" a components lifecycle. The
useEffect
hook effectively combines the
componentDidMount
,
componentDidUpdate
, and
componentWillUnmount
lifecycle methods.
js
componentDidMount() { ... }
useEffect(() => { ... }, [])

componentWillUnmount() { ... }
useEffect(() => { return () => { ... } }, [])

componentDidUpdate() { ... }
useEffect(() => { ... })
Let's use the input example we used in the State Hook section. Whenever the user is typing anything in the input field, we also want to log that value to the console.
We need to use a
useEffect
hook that "listens" to the
input
value. We can do so, by adding
input
to the dependency array of the
useEffect
hook. The dependency array is the second argument that the
useEffect
hook receives.
js
useEffect(() => {
  console.log(`The user typed ${input}`);
}, [input]);
The value of the input now gets logged to the console whenever the user types a value.
我们已经了解到可以使用
useState
组件处理函数式组件内的状态,但类组件的另一个优势是可以为组件添加生命周期方法。
借助
useEffect
Hook,我们可以“钩入”组件的生命周期。
useEffect
Hook实际上结合了
componentDidMount
componentDidUpdate
componentWillUnmount
这三个生命周期方法。
js
componentDidMount() { ... }
useEffect(() => { ... }, [])

componentWillUnmount() { ... }
useEffect(() => { return () => { ... } }, [])

componentDidUpdate() { ... }
useEffect(() => { ... })
让我们使用状态Hook部分的输入框示例。每当用户在输入框中输入内容时,我们还希望将该值记录到控制台。
我们需要使用一个“监听”
input
值的
useEffect
Hook。我们可以通过将
input
添加到
useEffect
Hook的依赖数组中来实现这一点。依赖数组是
useEffect
Hook接收的第二个参数。
js
useEffect(() => {
  console.log(`The user typed ${input}`);
}, [input]);
现在,每当用户输入内容时,输入框的值都会被记录到控制台。

Custom Hooks

自定义Hooks

Besides the built-in hooks that React provides (
useState
,
useEffect
,
useReducer
,
useRef
,
useContext
,
useMemo
,
useImperativeHandle
,
useLayoutEffect
,
useDebugValue
,
useCallback
), we can easily create our own custom hooks.
You may have noticed that all hooks start with
use
. It's important to start your hooks with
use
in order for React to check if it violates the Rules of Hooks.
Let's say we want to keep track of certain keys the user may press when writing the input. Our custom hook should be able to receive the key we want to target as its argument.
js
function useKeyPress(targetKey) {
  const [keyPressed, setKeyPressed] = React.useState(false);

  function handleDown({ key }) {
    if (key === targetKey) {
      setKeyPressed(true);
    }
  }

  function handleUp({ key }) {
    if (key === targetKey) {
      setKeyPressed(false);
    }
  }

  React.useEffect(() => {
    window.addEventListener("keydown", handleDown);
    window.addEventListener("keyup", handleUp);

    return () => {
      window.removeEventListener("keydown", handleDown);
      window.removeEventListener("keyup", handleUp);
    };
  }, []);

  return keyPressed;
}
Instead of keeping the key press logic local to the
Input
component, we can now reuse the
useKeyPress
hook throughout multiple components, without having to rewrite the same logic over and over.
Another great advantage of Hooks, is that the community can build and share hooks. Here are some websites that list all the hooks built by the community, and ready to use in your application:
By rewriting the counter and width example using React Hooks, we can break the logic of the
App
function into several pieces:
  • useCounter
    : A custom hook that returns the current value of
    count
    , an
    increment
    method, and a
    decrement
    method.
  • useWindowWidth
    : A custom hook that returns the window's current width.
  • App
    : A functional, stateful component that returns the
    Counter
    and
    Width
    component.
By using React Hooks instead of a class component, we were able to break the logic down into smaller, reusable pieces that separated the logic.
Using React Hooks just made it much clearer to separate the logic of our component into several smaller pieces. Reusing the same stateful logic just became much easier, and we no longer have to rewrite functional components into class components if we want to make the component stateful. A good knowledge of ES2015 classes is no longer required, and having reusable stateful logic increases the testability, flexibility and readability of components.
除了React提供的内置Hook(
useState
useEffect
useReducer
useRef
useContext
useMemo
useImperativeHandle
useLayoutEffect
useDebugValue
useCallback
),我们还可以轻松创建自己的自定义Hook。
你可能已经注意到,所有Hook都以
use
开头。为了让React检查是否违反Hooks规则,你的自定义Hook必须以
use
开头。
假设我们要跟踪用户在输入时按下的特定按键。我们的自定义Hook应该能够接收我们要监听的按键作为参数。
js
function useKeyPress(targetKey) {
  const [keyPressed, setKeyPressed] = React.useState(false);

  function handleDown({ key }) {
    if (key === targetKey) {
      setKeyPressed(true);
    }
  }

  function handleUp({ key }) {
    if (key === targetKey) {
      setKeyPressed(false);
    }
  }

  React.useEffect(() => {
    window.addEventListener("keydown", handleDown);
    window.addEventListener("keyup", handleUp);

    return () => {
      window.removeEventListener("keydown", handleDown);
      window.removeEventListener("keyup", handleUp);
    };
  }, []);

  return keyPressed;
}
现在,我们可以在多个组件中复用
useKeyPress
Hook,而无需重复编写相同的逻辑,而不是将按键监听逻辑局限在
Input
组件中。
Hooks的另一个巨大优势是社区可以构建和共享Hook。以下是一些列出了社区构建的、可直接在应用中使用的Hook的网站:
通过使用React Hooks重写计数器和窗口宽度示例,我们可以将
App
函数的逻辑拆分为几个部分:
  • useCounter
    :一个自定义Hook,返回
    count
    的当前值、
    increment
    方法和
    decrement
    方法。
  • useWindowWidth
    :一个自定义Hook,返回窗口的当前宽度。
  • App
    :一个有状态的函数式组件,返回
    Counter
    Width
    组件。
通过使用React Hooks而非类组件,我们能够将逻辑拆分为更小的、可复用的部分,实现逻辑分离。
使用React Hooks让我们能够更清晰地分离组件的逻辑,将其拆分为几个更小的部分。复用相同的有状态逻辑变得更加容易,而且如果我们想让组件变为有状态,也无需将函数式组件重写为类组件。不再需要掌握ES2015类的相关知识,复用有状态逻辑还能提高组件的可测试性、灵活性和可读性。

Additional Hooks guidance

额外的Hooks指南

Like other components, there are special functions that are used when you want to add Hooks to the code you have written. Here's a brief overview of some common Hook functions:
与其他组件一样,在现有代码中添加Hooks时需要使用一些特殊函数。以下是一些常见Hook函数的简要概述:

useState

useState

The
useState
Hook enables developers to update and manipulate state inside function components without needing to convert it to a class component. One advantage of this Hook is that it is simple and does not require as much complexity as other React Hooks.
useState
Hook允许开发者在函数式组件中更新和操作状态,而无需将其转换为类组件。该Hook的一个优势是简单,不像其他React Hook那样复杂。

useEffect

useEffect

The
useEffect
Hook is used to run code during major lifecycle events in a function component. The main body of a function component does not allow mutations, subscriptions, timers, logging, and other side effects. If they are allowed, it could lead to confusing bugs and inconsistencies within the UI. The useEffect hook prevents all of these "side effects" and allows the UI to run smoothly. It is a combination of
componentDidMount
,
componentDidUpdate
, and
componentWillUnmount
, all in one place.
useEffect
Hook用于在函数式组件的主要生命周期事件中运行代码。函数式组件的主体不允许进行突变、订阅、定时器、日志记录和其他副作用操作。如果允许这些操作,可能会导致UI出现令人困惑的错误和不一致性。
useEffect
Hook可以防止所有这些“副作用”,确保UI平稳运行。它将
componentDidMount
componentDidUpdate
componentWillUnmount
三者的功能整合到了一个地方。

useContext

useContext

The
useContext
Hook accepts a context object, which is the value returned from
React.createContext
, and returns the current context value for that context. The useContext Hook also works with the React Context API in order to share data throughout the app without the need to pass your app props down through various levels.
It should be noted that the argument passed to the
useContext
hook must be the context object itself and any component calling the
useContext
always re-renders whenever the context value changes.
useContext
Hook接受一个上下文对象(即
React.createContext
返回的值),并返回该上下文的当前上下文值。
useContext
Hook还与React Context API配合使用,无需通过多层组件传递props即可在整个应用中共享数据。
需要注意的是,传递给
useContext
Hook的参数必须是上下文对象本身,并且每当上下文值发生变化时,调用
useContext
的组件都会重新渲染。

useReducer

useReducer

The
useReducer
Hook gives an alternative to
setState
and is especially preferable to it when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. It takes on a
reducer
function and an initial state input and returns the current state and a
dispatch
function as output by means of array destructuring.
useReducer
also optimizes the performance of components that trigger deep updates.
useReducer
Hook是
setState
的替代方案,当你有涉及多个子值的复杂状态逻辑,或者下一个状态依赖于前一个状态时,使用它尤为合适。它接受一个
reducer
函数和初始状态作为输入,通过数组解构返回当前状态和
dispatch
函数作为输出。
useReducer
还能优化触发深层更新的组件的性能。

Pros

优点

Fewer lines of code

代码行数更少

Hooks allows you group code by concern and functionality, and not by lifecycle. This makes the code not only cleaner and concise but also shorter. Below is a comparison of a simple stateful component of a searchable product data table using React, and how it looks in Hooks after using the
useState
keyword.
Hooks允许我们按关注点和功能对代码进行分组,而非按生命周期。这不仅让代码更简洁、更紧凑,还更短。以下是一个简单的可搜索产品数据表的有状态组件的对比,展示了使用React类组件和使用Hooks后的差异。

Stateful components

类组件实现

js
class TweetSearchResults extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filterText: "",
      inThisLocation: false,
    };

    this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
    this.handleInThisLocationChange =
      this.handleInThisLocationChange.bind(this);
  }

  handleFilterTextChange(filterText) {
    this.setState({
      filterText: filterText,
    });
  }

  handleInThisLocationChange(inThisLocation) {
    this.setState({
      inThisLocation: inThisLocation,
    });
  }

  render() {
    return (
      <div>
        <SearchBar
          filterText={this.state.filterText}
          inThisLocation={this.state.inThisLocation}
          onFilterTextChange={this.handleFilterTextChange}
          onInThisLocationChange={this.handleInThisLocationChange}
        />
        <TweetList
          tweets={this.props.tweets}
          filterText={this.state.filterText}
          inThisLocation={this.state.inThisLocation}
        />
      </div>
    );
  }
}
Same component with Hooks
js
const TweetSearchResults = ({ tweets }) => {
  const [filterText, setFilterText] = useState("");
  const [inThisLocation, setInThisLocation] = useState(false);
  return (
    <div>
      <SearchBar
        filterText={filterText}
        inThisLocation={inThisLocation}
        setFilterText={setFilterText}
        setInThisLocation={setInThisLocation}
      />
      <TweetList
        tweets={tweets}
        filterText={filterText}
        inThisLocation={inThisLocation}
      />
    </div>
  );
};
js
class TweetSearchResults extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      filterText: "",
      inThisLocation: false,
    };

    this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
    this.handleInThisLocationChange =
      this.handleInThisLocationChange.bind(this);
  }

  handleFilterTextChange(filterText) {
    this.setState({
      filterText: filterText,
    });
  }

  handleInThisLocationChange(inThisLocation) {
    this.setState({
      inThisLocation: inThisLocation,
    });
  }

  render() {
    return (
      <div>
        <SearchBar
          filterText={this.state.filterText}
          inThisLocation={this.state.inThisLocation}
          onFilterTextChange={this.handleFilterTextChange}
          onInThisLocationChange={this.handleInThisLocationChange}
        />
        <TweetList
          tweets={this.props.tweets}
          filterText={this.state.filterText}
          inThisLocation={this.state.inThisLocation}
        />
      </div>
    );
  }
}
使用Hooks的相同组件
js
const TweetSearchResults = ({ tweets }) => {
  const [filterText, setFilterText] = useState("");
  const [inThisLocation, setInThisLocation] = useState(false);
  return (
    <div>
      <SearchBar
        filterText={filterText}
        inThisLocation={inThisLocation}
        setFilterText={setFilterText}
        setInThisLocation={setInThisLocation}
      />
      <TweetList
        tweets={tweets}
        filterText={filterText}
        inThisLocation={inThisLocation}
      />
    </div>
  );
};

Simplifies complex components

简化复杂组件

JavaScript classes can be difficult to manage, hard to use with hot reloading and may not minify as well. React Hooks solves these problems and ensures functional programming is made easy. With the implementation of Hooks, We don't need to have class components.
JavaScript类难以管理,与热重载配合使用时存在问题,而且可能无法很好地压缩。React Hooks解决了这些问题,让函数式编程变得简单。通过使用Hooks,我们不再需要类组件。

Reusing stateful logic

复用有状态逻辑

Classes in JavaScript encourage multiple levels of inheritance that quickly increase overall complexity and potential for errors. However, Hooks allow you to use state, and other React features without writing a class. With React, you can always reuse stateful logic without the need to rewrite the code over and over again. This reduces the chances of errors and allows for composition with plain functions.
JavaScript类会鼓励多层继承,这会迅速增加整体复杂度和出错概率。然而,Hooks允许你在不编写类的情况下使用状态和其他React特性。借助React,你可以始终复用有状态逻辑,而无需重复编写代码。这减少了出错的可能性,并允许通过普通函数进行组合。

Sharing non-visual logic

共享非可视化逻辑

Until the implementation of Hooks, React had no way of extracting and sharing non-visual logic. This eventually led to more complexities, such as the HOC patterns and Render props, just to solve a common problem. But, the introduction of Hooks has solved this problem because it allows for the extraction of stateful logic to a simple JavaScript function.
There are of course some potential downsides to Hooks worth keeping in mind:
  • Have to respect its rules, without a linter plugin, it is difficult to know which rule has been broken.
  • Need a considerable time practicing to use properly (Exp: useEffect).
  • Be aware of the wrong use (Exp: useCallback, useMemo).
Note (React 18+): Avoiding Unnecessary Effects
Modern best practices refine how we use effects and event handlers. It's easy to overuse
useEffect
— the new React docs caution: "If there is no external system involved, you shouldn't need an Effect. Removing unnecessary Effects will make your code easier to follow, faster to run, and less error-prone." (react.dev)
Instead of using an effect to set derived state (e.g.,
useEffect(() => setFullName(firstName + ' ' + lastName), [firstName, lastName])
), compute it directly in the component body:
const fullName = firstName + ' ' + lastName
. Similarly, handle user events like form submission or logging directly in event handlers rather than in effects. This reduces double renders and "stale closure" bugs.
Automatic Memoization via React Compiler: New in React 19 is the React Optimizing Compiler (sometimes called React Forget) which can handle many
useCallback
and
useMemo
optimizations for you. When enabled, the compiler statically analyzes your component code and automatically hoists stable functions and memoizes values that don't need to change. The official React docs note that the compiler "handles memoization for you, eliminating the need for manual useMemo, useCallback, and React.memo" in many cases. Writing clean functional components (no side-effects in render, pure computations) will allow the compiler to maximize optimizations.
在Hooks出现之前,React没有提取和共享非可视化逻辑的方法。这最终导致了更多的复杂性,比如为了解决一个常见问题而使用HOC模式和渲染属性。但是,Hooks的引入解决了这个问题,因为它允许将有状态逻辑提取到一个简单的JavaScript函数中。
当然,Hooks也有一些需要注意的潜在缺点:
  • 必须遵守其规则,如果没有linter插件,很难知道违反了哪条规则。
  • 需要大量练习才能正确使用(例如
    useEffect
    )。
  • 要注意错误用法(例如
    useCallback
    useMemo
    )。
注意(React 18+):避免不必要的副作用
现代最佳实践优化了我们使用副作用和事件处理程序的方式。很容易过度使用
useEffect
——新的React文档警告:“如果不涉及外部系统,你应该不需要使用副作用。移除不必要的副作用会让你的代码更易于理解、运行更快且更不易出错。”react.dev
不要使用副作用来设置派生状态(例如
useEffect(() => setFullName(firstName + ' ' + lastName), [firstName, lastName])
),而是直接在组件主体中计算:
const fullName = firstName + ' ' + lastName
。同样,直接在事件处理程序中处理表单提交或日志记录等用户事件,而非在副作用中处理。这减少了重复渲染和“过时闭包”错误。
通过React Compiler实现自动记忆化:React 19中的新特性是React优化编译器(有时称为React Forget),它可以为你处理许多
useCallback
useMemo
优化。启用后,编译器会静态分析你的组件代码,并自动提升稳定函数并记忆不需要更改的值官方React文档指出,编译器“为你处理记忆化,在许多情况下消除了手动使用
useMemo
useCallback
React.memo
的需求”。编写简洁的函数式组件(渲染时无副作用、纯计算)将使编译器最大化优化效果。

React Hooks vs Classes

React Hooks vs 类组件

When Hooks were introduced to React, it created a new problem: how do we know when to use function components with Hooks and class components? With the help of Hooks, it is possible to get state and partial lifecycle Hooks even in function components. Hooks also allow you to use local state and other React features without writing a class.
Here are some differences between Hooks and Classes to help you decide:
React HooksClasses
It helps avoid multiple hierarchies and make code clearerGenerally, when you use HOC or renderProps, you have to restructure your App with multiple hierarchies when you try to see it in DevTools
It provides uniformity across React components.Classes confuse both humans and machines due to the need to understand binding and the context in which functions are called.
当Hooks被引入React时,产生了一个新问题:我们如何知道何时使用带Hooks的函数式组件,何时使用类组件?借助Hooks,即使在函数式组件中也可以获取状态和部分生命周期Hook。Hooks还允许你在不编写类的情况下使用本地状态和其他React特性。
以下是Hooks和类组件之间的一些差异,可帮助你做出选择:
React Hooks类组件
避免多层嵌套,让代码更清晰通常,当你使用HOC或渲染属性时,在开发者工具中查看应用时,你会发现应用被重构为多层嵌套结构
在React组件之间提供一致性类组件会让人类和机器都感到困惑,因为需要理解绑定以及函数调用的上下文

Source

来源