React Hooks

从 React 16.8 起,Hooks 让函数组件也能拥有状态、副作用等能力,还能把逻辑抽出来复用。如今新项目几乎全部采用 Hooks 写法,class 组件逐渐退居幕后。本节总览最核心的 useState、useEffect、useRef、useContext 以及自定义 Hook 的套路。

为什么需要 Hooks

class 组件时代有三大痛点:this 绑定容易出错;副作用与无关代码挤在同一个生命周期方法里;状态逻辑难以跨组件复用,只能靠渲染属性、高阶组件层层嵌套。Hooks 把「会变的状态、有副作用的操作」拆成独立的小函数,随取随用。

useState:给组件加状态

useState 返回一个数组:当前值与更新函数。调用更新函数后,组件会重新渲染。

import { useState } from 'react';
export default function Counter() {
  const [count, setCount] = useState(0); // 初始值为 0
  return (
    <button onClick={() => setCount(count + 1)}>
      点击了 {count} 次
    </button>
  );
}

useEffect:处理副作用

数据请求、定时器、事件监听、修改标题等都属于副作用,统一放进 useEffect,它接收一个函数和依赖数组。

useEffect(() => {
  document.title = `当前计数:${count}`; // 每次渲染后同步标题
}, [count]); // 依赖数组:count 变化才再次执行

useEffect(() => {
  const timer = setInterval(() => console.log('tick'), 1000);
  return () => clearInterval(timer); // 清理函数:卸载时自动执行
}, []); // 空数组:只在挂载时执行一次

依赖数组不写时每次渲染都执行;写 [] 只在挂载时执行一次;写 [a, b] 则在 a 或 b 变化时执行。需要清理的副作用(定时器、订阅等)应在函数里返回一个清理函数。

useRef 与 useContext

useRef 保存不触发渲染的值,常用于直接操作 DOM;useContext 跨层级读取共享数据,避免一层层传 props。

import { createContext, useContext, useRef } from 'react';
const Theme = createContext('light');

function Toolbar() {
  const inputRef = useRef(null); // 初始值 null
  const theme = useContext(Theme); // 直接取到 "dark"
  return <input ref={inputRef} placeholder={`${theme} 主题的输入框`} />;
}

export default function App() {
  return (
    <Theme.Provider value="dark">
      <Toolbar />
    </Theme.Provider>
  );
}

自定义 Hook 与使用规则

名字以 use 开头、内部调用其它 Hook 的函数就是自定义 Hook,能把一段逻辑抽出来给多个组件共用。

import { useEffect, useState } from 'react';
// 监听窗口宽度,任何组件都能用
function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const onResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  return width;
}

export default function Panel() {
  const width = useWindowWidth(); // 窗口变化时自动更新
  return <p>窗口宽度:{width}px</p>;
}

使用 Hook 必须遵守两条铁律:其一,只在最顶层调用,不要在 if、循环或嵌套函数里调用,保证每次渲染的调用顺序一致;其二,只在函数组件或自定义 Hook 中调用,普通函数里调用会直接报错。

小结

Hooks 让函数组件集齐了状态、副作用与逻辑复用三种能力:状态交给 useState、副作用交给 useEffect、跨层数据用 useContext、可复用的逻辑封装成自定义 Hook。牢记「顶层调用」规则,就能避开绝大多数坑。

笔记加载中…