一、什么是 React Hook?

React Hook 是 React 16.8 版本中引入的一项革命性特性。它是一系列特殊的函数,允许你在 函数组件 中使用 React 的特性,如状态(state)、生命周期(lifecycle)、上下文(context)等。

简单来说,Hook 让你“钩入” React 的功能。


二、为什么需要 Hook? (解决了什么问题?)

在 Hook 出现之前,函数组件只能做简单的 UI 渲染(无状态组件),如果要使用状态或生命周期,就必须编写类组件(class component)。但这带来了几个问题:

  1. 状态逻辑难以复用: 在组件之间复用状态逻辑很麻烦,需要用到 render props 或高阶组件等复杂模式,导致“嵌套地狱”。
  2. 复杂组件难以理解: 一个类组件的生命周期函数(如 componentDidMount)中可能包含多个不相关的逻辑(如设置订阅、获取数据),而相关的逻辑又被拆分到不同的生命周期中(如清理工作在 componentWillUnmount 中),导致代码混乱。
  3. 令人困惑的 this: 类组件中的 this 指向问题对于初学者和资深开发者来说都很令人困惑。

Hook 的出现,使得函数组件的能力得到了极大的增强,现在完全可以替代绝大多数类组件。


三、常用的内置 Hook (Built-in Hooks)

1. 基础 Hook (最核心的三个)
  • useState (状态钩子)

    • 作用: 在函数组件中添加和管理内部状态(state)。
    • 示例
      import { useState } from 'react';
      
      function Counter() {
        // count 是当前的状态值,setCount 是更新它的函数
        const [count, setCount] = useState(0);
      
        return (
          <div>
            <p>你点击了 {count} 次</p>
            <button onClick={() => setCount(count + 1)}>
              点击我
            </button>
          </div>
        );
      }
      
  • useEffect (副作用钩子)

    • 作用: 在函数组件中执行副作用操作。它融合了类组件中的 componentDidMount, componentDidUpdate, 和 componentWillUnmount 三个生命周期的功能。
    • 常见副作用: 数据获取、设置订阅、手动更改 DOM。
    • 示例
      import { useState, useEffect } from 'react';
      
      function Example() {
        const [data, setData] = useState(null);
      
        // useEffect 接收一个函数(“effect”)
        useEffect(() => {
          // 此处的代码会在组件渲染后执行(相当于 componentDidMount + componentDidUpdate)
          fetch('https://api.example.com/data')
            .then(response => response.json())
            .then(data => setData(data));
          
          // 可选的清理函数(相当于 componentWillUnmount)
          return () => {
            // 在这里执行清理工作,比如取消网络请求、清除定时器、移除事件监听器等
          };
        }, []); // 依赖项数组。如果为空数组 [],则 effect 只会在组件挂载后执行一次
      
        return <div>{/* 渲染数据 */}</div>;
      }
      
  • useContext (上下文钩子)

    • 作用: 无需组件嵌套即可订阅 React 的 Context,从而在整个应用层级共享数据。
    • 示例
      import { createContext, useContext } from 'react';
      
      const ThemeContext = createContext('light');
      
      function ThemedButton() {
        // 直接获取 Context 的值
        const theme = useContext(ThemeContext);
        return <button className={theme}>我的主题是 {theme}</button>;
      }
      
2. 额外的常用 Hook
  • useReduceruseState 的替代方案,适用于复杂的状态逻辑,类似于 Redux 的工作流程。
  • useCallback: 缓存函数,避免非必要的重渲染,用于性能优化。
  • useMemo: 缓存计算结果,避免每次渲染都进行重复计算,用于性能优化。
  • useRef: 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数。常用作访问 DOM 节点或存储任意可变值。
  • useLayoutEffect: 与 useEffect 类似,但会在所有 DOM 变更之后同步调用 effect。主要用于读取 DOM 布局并同步触发重渲染。

四、Hook 的使用规则 (两条铁律)

使用 Hook 必须遵守以下规则:

  1. 只在最顶层使用 Hook不要在循环、条件判断或嵌套函数中调用 Hook。必须确保 Hook 在每次渲染时都以完全相同的顺序被调用。这是 React 能正确管理 Hook 状态的保证。
  2. 只在 React 函数中调用 Hook: 只能在 React 的函数组件自定义 Hook 中调用 Hook。不要在普通的 JavaScript 函数中调用。

ESLint 的 eslint-plugin-react-hooks 插件会自动强制执行这些规则。


五、自定义 Hook (Custom Hooks)

这是 Hook 最强大的特性之一:自定义 Hook 可以将组件逻辑提取到可重用的函数中

自定义 Hook 是一个以 use 开头的 JavaScript 函数,它内部可以调用其他的 Hook。

示例:自定义一个获取数据的 Hook

// useFetch.js (自定义 Hook)
import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      })
      .catch(error => {
        setError(error);
        setLoading(false);
      });
  }, [url]); // 当 url 变化时,会再次执行

  return { data, loading, error };
}

// MyComponent.js (使用自定义 Hook 的组件)
function MyComponent() {
  // 像使用内置 Hook 一样使用它
  const { data, loading, error } = useFetch('https://api.example.com/data');

  if (loading) return <div>加载中...</div>;
  if (error) return <div>出错啦!</div>;
  return <div>{JSON.stringify(data)}</div>;
}

六、总结

特性 类组件 函数组件 + Hook
状态 this.statethis.setState() useState, useReducer
生命周期 componentDidMount, componentDidUpdate useEffect
代码复用 高阶组件、Render Props 自定义 Hook (更简单清晰)
代码组织 逻辑分散在不同的生命周期中 相关逻辑组织在同一个 Hook 中
学习成本 需要理解 this 和生命周期 更接近于纯 JavaScript 函数

结论:Hook 是 React 的未来。对于新项目和新组件,强烈推荐使用函数组件配合 Hook 进行开发。

更多推荐