首页 / React 19 入门教程 / 自定义 Hook

React 19 入门教程

自定义 Hook

本教程共 50 篇 · 第 32 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

React自定义Hookuse开头逻辑复用状态逻辑

32. 自定义 Hook

本节目标:学会创建自定义 Hook,在组件间复用状态逻辑,让代码更干净、更好维护。

为什么要自定义 Hook

两个组件有相同的逻辑,复制粘贴是最差的选择。改一个忘了改另一个,bug 就这么来的。

// 两个组件重复的逻辑
function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() { setIsOnline(true); }
    function handleOffline() { setIsOnline(false); }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => { /* 清理 */ };
  }, []);
  return <span>{isOnline ? '在线' : '离线'}</span>;
}

function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    // 完全一样的代码又写了一遍...
  }, []);
  return <button disabled={!isOnline}>保存</button>;
}

提取自定义 Hook

把重复逻辑提取成一个 use 开头的函数:

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);

  useEffect(() => {
    function handleOnline() { setIsOnline(true); }
    function handleOffline() { setIsOnline(false); }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

现在两个组件都变简洁了:

function StatusBar() {
  const isOnline = useOnlineStatus();
  return <span>{isOnline ? '在线' : '离线'}</span>;
}

function SaveButton() {
  const isOnline = useOnlineStatus();
  return <button disabled={!isOnline}>保存</button>;
}

自定义 Hook 的本质

自定义 Hook 就是”用了其他 Hook 的函数”。它本身没有特殊能力,只是逻辑复用的一种方式。

命名规范

  • 必须以 use 开头useOnlineStatususeWindowSizeuseLocalStorage
  • 遵循 Hook 规则:只在顶层调用,只在函数组件或自定义 Hook 里调用
  • 命名要有意义:从名字能看出它做什么

实战:useLocalStorage

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// 使用
function Settings() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      当前:{theme}
    </button>
  );
}

实战:useDebounce

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}

// 使用
function SearchBox() {
  const [query, setQuery] = useState('');
  const debouncedQuery = useDebounce(query, 300);

  // debouncedQuery 在用户停止输入 300ms 后才更新
  // 用它发请求,避免每次按键都请求
}

实战:useWindowSize

function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  useEffect(() => {
    function handleResize() {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

// 使用
function ResponsiveComponent() {
  const { width } = useWindowSize();
  return <p>窗口宽度:{width}px</p>;
}

Hook 之间可以传递信息

自定义 Hook 内部可以调用其他 Hook,也可以调用其他自定义 Hook:

function useUser(id) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(setUser);
  }, [id]);

  return user;
}

function useUserPosts(id) {
  const user = useUser(id); // 调用另一个自定义 Hook
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    if (user) {
      fetch(`/api/users/${user.id}/posts`)
        .then(res => res.json())
        .then(setPosts);
    }
  }, [user]);

  return posts;
}

自定义 Hook 的返回值

可以返回任何类型,看使用场景:

// 返回单个值
function useOnlineStatus() { return isOnline; }

// 返回数组(类似 useState)
function useLocalStorage(key, init) { return [value, setValue]; }

// 返回对象(属性多时更清晰)
function useMousePosition() {
  return { x, y };
}

// 返回带方法的对象
function useCounter(initial) {
  const [count, setCount] = useState(initial);
  const increment = () => setCount(c => c + 1);
  const reset = () => setCount(initial);
  return { count, increment, reset };
}

本节要点

  • 自定义 Hook 以 use 开头,用于复用状态逻辑
  • 本质是”用了其他 Hook 的函数”
  • Hook 之间可以互相调用
  • 返回值灵活:单个值、数组、对象都行