CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
React 核心/课时/useMemo、useCallback 与自定义 Hook
RawBlame
课时目录14 节

React 核心

  1. 01React 是什么
  2. 02JSX 与渲染
  3. 03函数组件
  4. 04Props 与 State
  5. 05事件处理
  6. 06条件渲染与列表
  7. 07useEffect
  8. 08useRef 与 DOM
  9. 09useMemo、useCallback 与自定义 Hook
  10. 10表单与受控组件
  11. 11Context 共享状态
  12. 12React Router 入门
  13. 13数据获取与错误边界
  14. 14综合练习与 Next.js 方向

useMemo、useCallback 与自定义 Hook

性能优化直觉、抽取复用逻辑。

35 分钟

useMemo、useCallback 与自定义 Hook

这一课你将能做到

  1. 说出 useMemo / useCallback 是「按需优化」,不是默认必写
  2. 写出一个最小自定义 Hook(如 useToggle)
  3. 知道自定义 Hook 必须以 use 开头的约定

为什么要学

你会在网上看到满屏 useMemo。
入门时更重要的是:先写对,再量是否卡,再优化。

自定义 Hook 则是把「有状态的逻辑」复用出去——开关、倒计时、窗口宽度——这比过早 memo 更值得学。
人话:Hook 就是「能调用其他 Hook 的函数」;你自己写的也算,只要名字以 use 开头。

本站多处开关、抽屉,逻辑相似。复制三份 useState 不如一个 useToggle。

先建立图像

useMemo(() => 计算结果, [依赖])     → 记住某个「值」
useCallback(() => 函数, [依赖])     → 记住同一个「函数引用」
useXxx(...)                       → 你自己的 Hooks 组合

React 编译器等工具在变强:官方也不再鼓励到处手写 memo。入门把自定义 Hook 写对,比背优化口诀重要。

跟着做

步骤 1:自定义 Hook useToggle(完整可抄)

import { useState, useCallback } from "react";

export function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn((v) => !v), []);
  const setTrue = useCallback(() => setOn(true), []);
  const setFalse = useCallback(() => setOn(false), []);
  return { on, toggle, setTrue, setFalse };
}

export function Panel() {
  const { on, toggle } = useToggle();
  return (
    <button type="button" onClick={toggle}>
      {on ? "开" : "关"}
    </button>
  );
}

把开关逻辑挪出组件,路径页、设置页都能用。
useCallback 在这里只是让返回的函数引用稳定;没有 React.memo 的子组件时,收益很小,但展示「Hook 里也能用别的 Hook」。

步骤 2:useMemo 示例(理解即可)

import { useMemo, useState } from "react";

export function VisibleList({ items }) {
  const [showDone, setShowDone] = useState(true);
  const visible = useMemo(() => {
    return items.filter((x) => (showDone ? true : !x.done));
  }, [items, showDone]);

  return (
    <>
      <button type="button" onClick={() => setShowDone((v) => !v)}>
        切换
      </button>
      <ul>
        {visible.map((x) => (
          <li key={x.id}>{x.title}</li>
        ))}
      </ul>
    </>
  );
}

若 filter 很轻、列表很小,直接在渲染里算完全可以。列表极大、计算明显卡顿再 memo。

步骤 3:Hooks 规则(自定义 Hook 同样遵守)

  • 只在函数组件或自定义 Hook 顶层调用
  • 不要放进 if / for / 嵌套函数
  • 每次渲染调用顺序必须一样,React 靠顺序对上上次的 state

步骤 4:自定义 Hook 抽复用

function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = () => setOn(v => !v);
  return { on, toggle };
}

名称以 use 开头;内部可调其它 Hook。

讲清楚要点

  1. 自定义 Hook 就是函数,里面可以调 useState/useEffect 等。
  2. useCallback 单独不会变快;常配合 React.memo 子组件,避免父渲染导致子因「新函数 props」重绘。入门阶段少纠缠。
  3. 官方在推编译器优化——别把 memo 当信仰。可读性优先。
  4. 抽 Hook 的时机:同一段 state+事件在两个组件出现,或组件里「开关逻辑」比 JSX 还长。
  5. 不要把无关状态塞进同一个 Hook,以免复用时拖来一堆用不到的字段。

你可能会懵的地方

  • 为何必须叫 useXxx?
    约定 + ESLint 靠前缀识别 Hooks。叫 toggleHook 会漏检规则。

  • 可以在 if 里 useState 吗?
    不可以。组件和自定义 Hook 都一样。

  • 自定义 Hook 会共享 state 吗?
    不会。每个调用组件各有一份。要共享用提升或 Context。

  • 和普通工具函数差别?
    普通函数不能调用 Hooks。纯计算(价格格式化)继续用普通函数即可。

  • 先优化还是先对?
    先正确。useMemo/useCallback 有成本;在测量到卡顿或稳定依赖再加。

错法对照

错法问题正解
每个变量都 memo更难读,未必更快先正确,再量再优化
复制粘贴同一段 useState+effect难维护抽自定义 Hook
Hook 里 if 包 useState顺序错乱顶层调用
以为两个组件调同一 Hook 就共享数据各记各的状态提升 / Context
无依据包一层 memo噪音有证据再优化
自定义 Hook 里条件调用 Hook破坏规则顶层无条件调用

自测三问

  1. 自定义 Hook 解决什么复用问题?两个组件调用同一 Hook 会共享一份 state 吗?
  2. 为什么说 useMemo 不是默认标配?
  3. Hooks 为何不能放在条件语句里?

小结一句话

自定义 Hook 复用有状态逻辑;memo/callback 是优化工具,写对再按需使用。

动手加深

把「收藏开关」抽成 useToggle,两处 UI 复用;确认刷新逻辑仍正确。

跟着做(加厚 · react-hooks-advanced)

步骤:最小可运行对照

在 Vite 或 Next 练习页粘贴本课核心示例,改一个输入,确认屏幕变化符合预期。再故意写一个错法表里的写法,看控制台/界面如何坏,然后改回。

每个概念都问:state 在谁手里?UI 哪一块会因它重绘?

操作顺序:打开对应文件 → 改一处可观察点(文案、类型、className 或分支)→ 保存看结果 → 复原或留下有意义的小改动。

步骤:错法故意练一次

先按「用 jQuery 式直接改 DOM 冒充完成」做 30 秒(或在注释里写出若这样做会怎样),再改成「改数据(state/props)让 UI 推导出来」。 目的不是留坑,而是让失败现象进长期记忆。

你可能会懵的地方(加厚)

  • 热更新后状态丢了?
    正常:整树重挂会重置 state。用 key 或外提状态保留;开发期别被吓到。

  • 和文档示例不一致?
    以你锁的 React 大版本 + react.dev 为准;旧博客的 class 组件示例仅作历史阅读。

  • 这课和前后课怎么接?
    回看大纲上一课的小结一句、下一课标题;用自己的话写「本课产出什么可观察结果」。写不出来就重做跟着做。

错法对照(加厚)

错法问题正解
用 jQuery 式直接改 DOM 冒充完成假完成/埋雷改数据(state/props)让 UI 推导出来
只看不敲第二天全忘至少跑通本课步骤
跳过自测三问以为会了闭卷答再对稿

延伸

  • Reusing Logic with Custom Hooks
  • 下一课:表单与受控组件。
加载本节练习…
← 上一课useRef 与 DOM下一课 →表单与受控组件