React Hooks 实战 10 个高频场景

跳出 "API 记忆陷阱" 的核心在于 理解设计思想 而非死记语法。以下通过实际场景解析 Hooks 的本质逻辑:


场景 1:状态独立封装

问题:组件状态逻辑重复
解法useState + 逻辑隔离

// 封装计数器逻辑
const useCounter = (initial = 0) => {
  const [count, setCount] = useState(initial);
  const increment = () => setCount(c => c + 1);
  return { count, increment }; // 返回状态与方法
};

// 组件调用
function Button() {
  const { count, increment } = useCounter();
  return <button onClick={increment}>{count}</button>;
}

关键思想:将状态与更新方法捆绑为原子单元


场景 2:副作用精准控制

问题:请求重复触发
解法useEffect 依赖项精细化

useEffect(() => {
  const fetchData = async () => {
    const res = await fetch(`/api/data?id=${userId}`);
    setData(await res.json());
  };
  fetchData();
}, [userId]); // 仅当 userId 变更时执行

核心洞察:依赖项数组是副作用与数据流的合约


场景 3:复杂状态机

问题:多状态联动混乱
解法useReducer 状态机模式

const [state, dispatch] = useReducer((state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      return { ...state, list: [...state.list, action.payload] };
    case 'CLEAR':
      return { ...state, list: [] };
    default:
      return state;
  }
}, { list: [] });

设计本质:用 Action 描述状态变迁意图


场景 4:DOM 测量延迟

问题:组件渲染后获取 DOM 尺寸
解法useRef + useLayoutEffect

const ref = useRef(null);
useLayoutEffect(() => {
  console.log('DOM 宽度:', ref.current.offsetWidth); 
}, []);
return <div ref={ref}>测量元素</div>;

逻辑关系useLayoutEffect 在浏览器绘制前同步执行


场景 5:防抖搜索优化

问题:输入时频繁请求
解法useRef 存储定时器 + useEffect 清理

const [query, setQuery] = useState('');
const timerRef = useRef();

useEffect(() => {
  clearTimeout(timerRef.current);
  timerRef.current = setTimeout(() => {
    searchAPI(query); // 500ms 后执行
  }, 500);
}, [query]);

性能本质:副作用清理是防抖/节流的安全网


场景 6:上下文穿透嵌套

问题:多层传递 props
解法useContext 跨层级通信

const ThemeContext = createContext('light');

// 顶层注入
<ThemeContext.Provider value="dark">
  <Child />
</ThemeContext.Provider>

// 底层获取
const theme = useContext(ThemeContext); // 直接获取 dark

架构思想:上下文是组件树的隐式管道


场景 7:持久化函数引用

问题:函数重复创建导致子组件无效渲染
解法useCallback 记忆函数

const handleSubmit = useCallback(() => {
  submitForm(data); 
}, [data]); // data 不变则函数引用不变

<ExpensiveComponent onSubmit={handleSubmit} />

渲染优化本质:通过引用稳定性避免子组件重渲


场景 8:复杂计算缓存

问题:重复计算耗性能
解法useMemo 缓存计算结果

const sortedList = useMemo(() => {
  return bigArray.sort((a, b) => a.value - b.value); 
}, [bigArray]); // bigArray 不变则复用结果

算法本质:空间换时间的思想具象化


场景 9:自定义 Hook 抽象

问题:监听窗口尺寸逻辑复用
解法:封装为自定义 Hook

const useWindowSize = () => {
  const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
  
  useEffect(() => {
    const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);
  
  return size; // 返回尺寸对象
};

// 任意组件使用
const { width } = useWindowSize();

工程思想:将副作用组合为可插拔单元


场景 10:命令式暴露方法

问题:父组件调用子组件函数
解法useImperativeHandle + forwardRef

// 子组件
const Input = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus()
  }));
  return <input ref={inputRef} />;
});

// 父组件
const parentRef = useRef();
<Input ref={parentRef} />
<button onClick={() => parentRef.current.focus()}>聚焦</button>

设计哲学:在声明式体系中安全引入命令式操作


跳出陷阱的思维框架

  1. Hook 本质是状态机:每个 Hook 管理特定类型的状态/副作用
  2. 依赖数组即数据流图:声明状态与副作用的拓扑关系
  3. 引用稳定是优化核心:通过 useMemo/useCallback 维持不变性
  4. 自定义 Hook 是乐高积木:组合原子逻辑形成复杂功能

当理解到 Hooks 是 React 的状态引擎 而非 API 列表时,你将自然摆脱记忆负担。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐