如何编写自定义 Hooks

如何编写自定义 Hooks

自定义 Hook 是把组件里可复用的「状态 + 副作用」逻辑抽成以 use 开头的函数。它本身不是新特性,而是对已有 Hooks 的组合与封装,便于在多个组件间共享行为。

命名与约定

  • 函数名必须以 use 开头,例如 useWindowSizeuseFetch
  • 内部可以调用 useStateuseEffect 等任意 Hooks。
  • 不要在普通工具函数里偷偷调用 Hooks;Hooks 只能写在自定义 Hook 或组件顶层

这样命名也方便 ESLint 的 eslint-plugin-react-hooks 做规则检查。

最小示例:封装窗口宽度

import { useState, useEffect } from "react";

export function useWindowWidth() {
  const [width, setWidth] = useState(() =>
    typeof window !== "undefined" ? window.innerWidth : 0,
  );

  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth);
    }
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return width;
}

在组件中使用:

function Header() {
  const width = useWindowWidth();
  return <span>当前宽度:{width}px</span>;
}

带参数的 Hook:根据 key 拉取数据

import { useState, useEffect } from "react";

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

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);

    fetch(url)
      .then((res) => {
        if (!res.ok) throw new Error(res.statusText);
        return res.json();
      })
      .then((json) => {
        if (!cancelled) setData(json);
      })
      .catch((e) => {
        if (!cancelled) setError(e);
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, [url]);

  return { data, error, loading };
}

url 变化会重新请求;卸载或 url 快速切换时用 cancelled 避免把旧请求结果写回 state。

返回值设计

常见两种风格,按团队习惯统一即可:

风格示例说明
元组return [value, setValue]类似 useState,调用处解构简短
对象return { data, error, loading }字段多时可读性更好,扩展方便

不要踩的坑

  1. 条件里调用 Hooks
    自定义 Hook 内部也必须在顶层调用 useState / useEffect,不能写在 if (x) { useEffect(...) } 里。

  2. 把「非共享」逻辑硬抽
    只用一次的简单逻辑不必强行抽 Hook,避免过度抽象。

  3. 闭包拿到过期的 state
    若在 setInterval、事件回调里读 state,注意依赖与 useEffect 依赖数组 是否完整,必要时用函数式更新或 useRef 存最新值。

  4. SSR
    访问 windowdocument 时先判断环境或放在 useEffect 里,避免服务端报错。

与「高阶组件 / render props」对比

自定义 Hook 通常更直观:无额外组件嵌套,逻辑与 UI 分离清晰。旧项目里 HOC、render props 仍会遇到,新代码优先 Hooks 即可。

小结

  • 自定义 Hook = use 开头的函数 + 内部组合官方 Hooks
  • 适合抽离:订阅、请求、浏览器尺寸、本地存储、防抖节流等跨组件逻辑。
  • 注意依赖、清理函数、竞态,与在组件里写 useEffect 时相同。

更多模式可参考 React 文档:自定义 Hookopen in new window

Last Updated 4/9/2026, 6:16:02 AM