资讯动态

React Hooks 深入浅出:从原理到实践,彻底搞懂核心机制与常见坑

发布时间:2026/10/9 13:08:21 来源:尧图企业网站定制
前段时间有个后辈去面前端回来跟我复盘的时候说面试官问了一句“React Hooks 你理解吗常用的有哪些”他直接背出了八九个 Hook 的名字列举了 useState、useEffect、useMemo面试官点了点头接着追问了一句“那为什么 Hooks 不能写在条件判断里”他当场卡住了。这个问题其实特别典型——“常用的有哪些”几乎人人都能列出一串但“理解”两个字考的是你有没有真正踩过它的运行逻辑。我在团队里带人也有几年了面试候选人时也爱问这个问题。我关心的从来不只是那个 API 清单而是候选人对机制的理解、对状态的思考方式以及对踩坑场景的敏感度。这篇我就把 React Hooks 的来龙去脉、底层逻辑、常用 API 的实战用法和常见坑完整梳理一遍。内容既适合准备面试的人也适合已经用 Hooks 写了一阵子、但总在依赖数组和闭包问题上犯迷糊的开发者。1. 从 Class 时代走到 Hooks先搞清楚它真正解决的是什么1.1 Class 组件时代的四个结构性痛点在 Hooks 出现之前React 组件的状态逻辑有两个主要载体Class 组件和高阶组件HOC。我在 2018 年前后写过不少 Class 组件说实话Class 不是不能用但它的别扭之处会在项目大了以后集中爆发。第一是this撞墙。Class 组件里到处都是事件处理函数的绑定问题.bind(this)、箭头函数、构造器里预绑定写法五花八门新人进来看到一堆this.handleClick this.handleClick.bind(this)直接懵掉。一旦函数被作为回调传下去this丢了就是线上 bug。Hooks 把状态和函数都变成普通变量彻底绕开了这堵墙。第二是生命周期把逻辑撕碎了。一个常见场景组件里既要在componentDidMount里发请求和监听事件又要在componentDidUpdate里根据 props 变化重新拉数据最后还要在componentWillUnmount里清理监听。一个完整的功能逻辑被拆成三段散落在三个生命周期函数里如果页面里有五六个这样的功能你就只能在生命周期函数里来回跳着找代码。Hooks 的做法是用useEffect把“同一个副作用”的启动、更新、清理放在同一处从“按时间切片”变成“按功能聚合”。第三是逻辑复用困难。Class 时代复用有状态逻辑主流方案是 HOC 和 Render Props。HOC 层层嵌套之后调试 call stack 深不见底还会出现 props 命名冲突Render Props 的嵌套回调也难看得要命。而且这两种方案都会给组件树额外包一层节点项目大了开发工具里能看到一堆WithHoverWithRouterWithAuth之类的“包裹地狱”。自定义 Hook 则可以让两个组件直接共享逻辑不增加任何多余的嵌套层级。第四是 Class 组件本身对人和编译器都不友好。constructor里忘记调super(props)、方法绑定错误这类基础问题反复出现代码压缩和 tree shaking 对 Class 的处理也不如函数灵活。函数组件配合 Hooks 之后状态逻辑、副作用、派生数据全部以普通函数的形式组织心智负担明显小一截。1.2 Hooks 的本质把 React 特性“钩”进函数组件官方文档的说法是Hooks 让你在不编写 Class 的情况下使用状态和其他 React 特性。这个定义准确但对很多人来说太抽象了。我的理解是函数组件本身只是一个“纯函数”——输入 props输出 JSX。它天然是确定性的同一次渲染同样的 props产出一模一样的 UI。这种方式简单、可靠可它没有状态、没有副作用、没有生命周期一套纯函数模型在真实业务里撑不住。Hooks 就是一组“接口”把这些原本只有 Class 才有的能力一个一个再装回到函数组件身上。useState装上的是内部状态能力useEffect装上的是副作用管理能力useContext装上的是跨层级取数据的能力useMemo/useCallback装上的是性能优化的能力。函数组件相当于一台没有外设的裸机Hooks 就是外设接口你需要什么就插什么。这也是“Hooks”这个名字的由来它是一组钩子把 React 的运行时能力挂到函数上。我建议所有学 Hooks 的人都在脑子里建立这个模型而不是只把它当成“在函数组件里写状态的语法糖”。2. 为什么函数组件能“记住”状态Fiber 链表和调用顺序2.1 状态到底存在哪里很多人第一次看useState的代码都会好奇函数组件每次渲染都重新执行一遍执行完毕局部变量全部销毁那下一次渲染时useState是怎么把上一次的值取出来的它总得有个地方“存”吧。答案是状态不是存在函数作用域里而是存在 React 内部的 Fiber 节点上。Fiber 是 React 16 开始引入的架构可以把它理解成每个组件实例对应的一个“内存对象”里面保存了组件类型、props、state、副作用标记、以及一个非常关键的东西——Hooks 链表。每次调用useState、useEffect、useRef这些 Hook 时React 并不是凭空变出一个状态来而是沿着当前 Fiber 节点上维护的这个 Hooks 链表顺序往下取节点。第一次渲染时链表是空的React 一个个创建 Hook 节点填进去后续渲染时React 按同样的顺序去链表里把对应的节点捞出来把之前保存的 state 返回给你。所以有一句话特别重要Hooks 是“附着”在 Fiber 节点上的而它的身份由调用顺序决定不是由名字决定。这也是为什么两个useState调用之间不能插入条件判断——React 根本不在乎你给变量起什么名字它只认第几个调用。2.2 顺序即契约条件调用为什么是灾难假设你写了这样的代码function Component({ showName }) { const [count, setCount] useState(0); if (showName) { const [name, setName] useState(张三); } const [age, setAge] useState(20); }第一次渲染时showName是true链表结构是节点0count、节点1name、节点2age。第二次渲染showName变成false条件不成立name那个 Hook 没执行此时setAge对应的调用顺序落到了链表里第1个节点上——而第1个节点里存的是name的值。于是age拿到的是张三链表错位状态全部乱套。React 为了保证不出现这种错乱干脆设了一条硬性规则Hooks 只能在组件顶层调用。所谓“顶层”就是不能在条件、循环、嵌套函数里调用。这很大程度上不是道德要求而是技术约束——只要调用顺序稳定React 就能用位置匹配状态一旦顺序不稳定整个匹配机制就崩了。你可以把 Hooks 链表想象成一排固定编号的储物柜你每次都得从第一个柜子开始按顺序打开跳过一个柜子再开后面的拿到的肯定不是自己放进去的东西。这个机制也解释了为什么自定义 Hook 必须以use开头命名。它不只是一个约定因为 React 有专门的 lint 规则eslint-plugin-react-hooks去检查一个函数内部有没有调用其他 Hooks而它判断一个函数是不是 Hook 的方式就是看函数名是否以use开头。名字不对检查规则就不生效条件调用就可能在项目里悄悄溜达进去。3. 高频三件套useState / useEffect / useRef 的正确姿势3.1 useState 不只是 setState 的替代品useState是最基础的状态 Hook但它的用法细节比大多数人认识的要多。第一初始值可以是惰性初始化。如果初始值需要经过复杂计算比如从 localStorage 读取、遍历数组聚合不要直接写成useState(expensiveFn())这样每次渲染都会执行一次expensiveFn()然后把结果丢掉——纯白算。要写useState(() expensiveFn())把计算放进函数里React 只在首次渲染时执行一次之后完全跳过。第二更新函数有两种形态const [count, setCount] useState(0); // 直接传值 setCount(count 1); // 函数式更新 setCount(prev prev 1);在事件处理函数里两种写法大多数时候等价但如果在异步队列里连续更新或者依赖的是“旧状态”函数式更新就是唯一的正确选择。典型例子是 setInterval 回调里想基于最新状态更新计数。闭包会捕获旧值直接setCount(count 1)永远在旧值基础上加函数式更新传进去的prev永远是 React 当前最新的值不受闭包影响。第三不要贪多。多个互不相关的 state 建议拆成多个useState调用而不是塞进一个useState({ count, name, list })对象里。拆分之后每个状态独立更新可读性好调试图也好定位。但如果是多个状态存在必然联动比如互斥选中项、一组表单字段那不如用一个对象或者直接上useReducer。3.2 useEffect 的时机、清理与依赖useEffect是面试官最喜欢深挖的一个 Hook。很多人把它当成“componentDidMount 和 componentDidUpdate 的结合体”这个理解不算错但太粗了如果只停留在这一层很容易写出有问题的代码。关键在于useEffect的声明式心智模型它不是“在某个生命周期时刻执行副作用”而是“在本次渲染完成之后如果依赖变了就同步这一份副作用”。每次渲染React 都会记录当次渲染的 effect 函数和依赖数组。渲染提交到浏览器以后React 再回头检查这次的依赖和上次的依赖是否一样如果不一样就执行这次的 effect如果有清理函数则在上一次执行后、下一次执行前调用它。依赖数组常见的三种形态依赖写法执行时机典型场景不写依赖每次渲染后都执行极少需要大概率是设计有问题[]空数组仅挂载后执行一次初始化请求、埋点上报、注册一次性监听[prop, state]依赖变化时执行数据请求、联动计算、防抖空数组对应的清理函数会在组件卸载时执行这替代了componentWillUnmount但又不完全等价清理函数在“依赖变化、下一次 effect 执行前”也会先执行。这个行为经常被忽略比如你用[]注册了一个window.addEventListener清理函数写了卸载时正常移除但如果依赖数组里加了某个 props那么每次该 props 变化时都会先清理旧监听、再注册新监听这样反而能保证监听器里永远用的是最新 props 值。还有一个容易出错的地方useEffect里发请求时竞态问题。假设用户在快速切换筛选条件请求 A 发出去了还没回来请求 B 又发出去如果 A 比 B 晚返回页面就可能显示旧数据。解决办法是在 effect 里维护一个“当前请求是否过期”的标志useEffect(() { let cancelled false; fetchData(filter).then(res { if (!cancelled) setData(res); }); return () { cancelled true; }; }, [filter]);清理函数在依赖变化时执行把上一次的cancelled置为true旧请求的响应即使回来了也不会更新状态。这套写法在 React Native 里同样适用尤其是处理启动阶段的长列表加载能明显减少“页面数据被覆盖”的怪异现象。3.3 useRef 的两种正经用途useRef经常被误解为“可以在组件里随便改的变量”。它的正确定义是返回一个在组件整个生命周期内保持不变的 ref 对象其.current属性可以写入任何值写入不会触发重新渲染。用途一操作 DOM。把 ref 传给 JSX 的ref属性挂载后.current就是对应的 DOM 节点可以在useEffect里读取尺寸、绑定事件、聚焦输入框。这个用法在画布类应用里特别常见比如初始化一个 2D 画布实例就先把 canvas 的 ref 拿到再在useEffect里初始化 drawing 实例。用途二跨渲染保存可变值。比如定时器 id、上一次的 props、最新的事件回调。经典场景useEffect里开了setInterval回调里需要读取最新的 state如果把state写进依赖数组定时器会被反复重启如果不写闭包捕获的是旧值。解法就是用 ref 保存最新状态const countRef useRef(0); countRef.current count; // 每次渲染重新赋值 useEffect(() { const timer setInterval(() { console.log(countRef.current); }, 1000); return () clearInterval(timer); }, []);这里countRef.current每次渲染都会更新而定时器闭包里读取的是 ref 对象本身。ref 对象是同一个引用所以定时器每次读到的是最新值。要注意的是不要在渲染过程中“读取”可变 ref 来参与 UI 渲染ref 变化不会触发重新渲染读出来也只会拿到旧画面。ref 更适合用在“渲染完成之后”的逻辑里比如事件回调、定时器、副作用内部。useRef的初始值也做了惰性处理useRef(null)和useRef(initialValue)里的initialValue只会参与首次初始化后续渲染都会被忽略不会像useState那样每次渲染都去走一遍初始化逻辑这一点面试时经常被拿出来考。4. 复杂状态与跨组件数据useReducer / useContext / 自定义 Hook4.1 useReducer 什么时候真的比 useState 合适useReducer是一个暗藏实力的 Hook。它的 API 是 React 原生的不依赖 Redux用法也简单const [state, dispatch] useReducer(reducer, initialState);reducer是一个纯函数接收当前 state 和 action返回新 state。什么时候该用它我个人的判断标准是当状态更新逻辑复杂到“写完 setState 后解释不清状态为什么变成这样”的时候。举个例子购物车状态有三个变量商品列表、选中项、优惠券。加购、删购、切换选中、应用优惠券每种操作都要根据当前状态算出一系列新值如果用useState每个操作都得写一长串setXxx还容易漏。改用useReducer后每个 action 类型对应一个分支更新逻辑集中且可测试function cartReducer(state, action) { switch (action.type) { case ADD_ITEM: return { ...state, items: [...state.items, action.payload] }; case TOGGLE_SELECT: return { ...state, selected: state.selected.includes(action.payload) ? state.selected.filter(id id ! action.payload) : [...state.selected, action.payload], }; default: return state; } }面试时谈到useReducer有经验的面试官会追问reducer 里为什么要返回新对象而不是直接改原对象这个问题考的是 React 的更新机制——React 靠Object.is比较新旧状态来决定是否重新渲染只有返回新引用React 才能知道状态变了。直接改原对象返回同一个引用React 会认为什么都没发生页面不刷新。这也是所有状态 Hook 的共同底线状态不可变更新要产生新引用。4.2 useContext 的便利和它带来的重渲染代价useContext解决了 props 层层传递的问题。在典型的多层嵌套场景里登录用户信息、主题配置、国际化的 locale如果一路用 props 传下去中间层组件会被迫接收并转发大量自己根本用不到的数据组件接口臃肿不堪。用 Context 后中间层可以完全不知道这些数据的存在只有真正消费数据的组件才调用useContext。但useContext有一个必须在实战里正视的性能问题一旦 Context 的值变化所有消费了这个 Context 的组件都会重新渲染。注意是“所有”不管那个组件是否只用到了其中某一段数据。举个常见例子Context 里放了一个大对象{ user, theme, cartCount }某个组件只读theme但只要cartCount变了它依然跟着重新渲染。优化手段是把 Context 拆细让不同层面的数据走不同的 Provider或者把 Provider 的 value 用useMemo缓存防止父组件无关渲染导致 value 变成新引用、引发下游无意义重渲染const value useMemo(() ({ user, theme }), [user, theme]); return AppContext.Provider value{value}{children}/AppContext.Provider;这里要用useMemo而不是每个渲染都直接传对象字面量因为每次渲染都新建一个引用即使数据没变所有消费者也都会被强行刷新一遍。这个点和后面要讲的useMemo正好衔接上很多人第一次意识到 Context 的性能代价都是从这个写法开始的。4.3 自定义 Hook项目里真正的复用单元自定义 Hook 是 Hooks 体系里最体现工程能力的一层。它的本质就是把一组有内在关联的 state、effect、回调封装成一个函数函数内部可以随意使用其他 Hooks所有约束和组件内一样。我举一个封装“防抖搜索”的例子。这是业务里最常见的需求搜索框输入后延迟 300ms 请求接口function useDebouncedValue(value, delay 300) { const [debounced, setDebounced] useState(value); useEffect(() { const timer setTimeout(() setDebounced(value), delay); return () clearTimeout(timer); }, [value, delay]); return debounced; } // 组件里 const [keyword, setKeyword] useState(); const debouncedKeyword useDebouncedValue(keyword); useEffect(() { if (debouncedKeyword) search(debouncedKeyword); }, [debouncedKeyword]);这里有几个工程细节值得说清理函数clearTimeout必不可少否则上一次输入的回调会继续执行产生过期结果依赖数组里同时放value和delay保证两者变化都会重置定时器useDebouncedValue返回的是“稳定延迟后的值”组件可以把它当普通状态用下游的useEffect只管依赖它。自定义 Hook 的命名必须以use开头这不只是约定也决定了 lint 规则能否生效。我见过的项目里很多烂代码就是把一坨逻辑塞进组件内部几十行useEffect一旦遇到第二个页面需要相同逻辑就 CtrlC、CtrlV然后噩梦开始。正确的做法是只要发现“两个组件里用了差不多的状态逻辑”第一反应就应该是抽成自定义 Hook。它比 HOC 轻量比工具函数多管状态是 React 官方钦定的组合式复用方案。另外提一句抽象的程度要有度。如果一个 Hook 只有一个useState、一个useEffect使用它的地方也只有一处那它就是在制造间接层反而增加阅读负担。自定义 Hook 的第一原则是“为复用而生”不是为了显得代码高级。5. 性能 Hooks 别乱用useMemo / useCallback 的适用边界5.1 它们到底防的是什么先看useMemo和useCallback的定义useMemo缓存计算结果依赖不变则返回同一个值避免重复执行昂贵计算。useCallback缓存函数引用依赖不变则返回同一个函数对象。useCallback(fn, deps)其实等价于useMemo(() fn, deps)两者底层是同一套机制。它们的价值不是孤立的“优化了自己”而是稳定了引用从而让下游优化生效。最典型的下游是React.memo包裹的子组件。React.memo的作用是对比 props 的引用没变化就跳过子组件重新渲染。父组件每次渲染都会重新创建内联函数如果把这个函数传给React.memo子组件即使数据没变子组件也会因为 props 引用变化被迫重渲染。用useCallback稳定函数引用配合React.memo才能做到真正跳过子组件渲染。useMemo与useCallback对比如下对比项useMemouseCallback返回内容任意值计算结果函数本身主要用途避免昂贵计算重复执行稳定函数引用传给子组件典型场景大数组排序/过滤、复杂聚合计算子组件回调、Context value等价写法无useMemo(() fn, deps)5.2 什么时候真的需要它我见过的最大误用是“为了优化而优化”把整个组件里的函数和计算全部包上useMemo/useCallback最后代码看起来每个函数都带依赖数组阅读成本翻倍性能却没提升多少。原因很简单这两个 Hook 本身也有成本——每次渲染要对比依赖数组内部要维护缓存结构处理不当反而占内存。我的使用口径相当克制第一计算本身真的昂贵才用useMemo。比如在长列表上做多条件筛选、按复杂权重排序、把数组对象转换成图表坐标数据。判断标准很简单一次渲染里这个计算需要多少毫秒如果 1ms 以内别缓存超过几十毫秒考虑缓存。第二函数引用的稳定性真的影响下游才用useCallback。检查标准是这个函数会传给React.memo子组件吗会作为useEffect的依赖吗会传给useRef监听的 DOM 事件吗三个问题全是“否”那不用。第三在自定义 Hook 里导出的函数建议一律useCallback包裹。因为自定义 Hook 的使用者无法控制调用方组件的重渲染而你导出的函数引用如果不稳定使用者在useEffect里依赖它时就会产生“每次渲染都触发 effect”的连锁问题。这个建议是“接口方”的自觉不是全局建议。5.3 常见的误区和坑一个高频面试题useMemo能不能代替useEffect做数据请求答案是最好不要。useMemo在渲染过程中执行useEffect在渲染完成后执行。在渲染过程中触发请求React 会警告你“不能在渲染期间执行副作用”而且在 React 18 并发特性下渲染可能被中断、回滚useMemo的计算结果可能被丢弃再重算在里面发请求极不靠谱。请求这种副作用永远放useEffect。还有依赖数组里写对象或数组的坑。如果useMemo的依赖是一个每次渲染都新创建的对象字面量那么它的缓存永远失效等于没缓存。解决方式是把依赖拆成原语类型id、name这种或者先对那个对象本身做useMemo稳定引用再把它作为依赖。这个“依赖数组里的引用是否稳定”问题是整个 Hooks 性能优化里最隐蔽、也最容易让优化白做的一环。最后提一下不要迷信“把 useCallback 加上就一定快”。React 官方的态度一直很明确useCallback和useMemo是性能优化手段不是语义保证。正确性永远是第一位等性能问题真的出现了用 profiler 定位到具体是哪个组件在浪费渲染再回来看要不要加缓存。我之前在一个项目中就干过“全线加 useCallback”的事结果代码难读、依赖容易写漏性能提升几乎可以忽略后来花了半天把这些多余的包裹全部拆干净组件反而清晰多了。6. 踩坑实录闭包陷阱、依赖数组和 StrictMode 的意外行为6.1 闭包陷阱定时器永远拿到旧值这是 Hooks 使用中被问得最多的“怪现象”。写一个倒计时组件function Timer() { const [count, setCount] useState(0); useEffect(() { const timer setInterval(() { setCount(count 1); }, 1000); return () clearInterval(timer); }, []); return div{count}/div; }运行之后你会发现count永远停在 1。原因在于useEffect的空依赖数组只在挂载时执行了一次定时器回调闭包捕获的是首次渲染的count也就是 0。之后每次setInterval触发的都是setCount(0 1)结果一直是 1。正确解法前面已经写过用函数式更新或者useRef配合。这个例子的价值在于它逼着你理解“每次渲染都有自己的 props 和 state 快照”。函数组件每次渲染都会执行一次每次执行都生成一个全新的闭包环境useEffect里注册的回调捕获的是“触发 effect 那一次渲染”的快照。只要依赖数组没变这个快照就一直停留在那一次。想突破闭包要么让依赖数组重新变化要么借助 ref。6.2 依赖数组写不全的连锁问题eslint 的exhaustive-deps规则会警告“effect 里用了某个变量但依赖数组里没写”。很多新人图省事直接忽略警告或者更粗暴——给 eslint 关掉。这种“清爽”的代价是大部分情况下代码碰巧还能工作因为 effect 里读的是旧渲染的闭包值只有两处能发现不对劲一是调试时发现数据永远滞后一步二是并发渲染下出现极其诡异的状态错位。我的建议是除非有明确理由比如那变量确实只需要首渲染时的初始值且你清楚知道自己为什么这么做否则exhaustive-deps的警告都该当错误处理。当 lint 报“你缺了xxx”先停下来想而不是直接补一个变量进去——因为补了依赖导致 effect 频繁重跑的问题同样常见。更合理的方向往往是调整 effect 内部的写法比如把读取值的工作交给 ref 或函数式更新让依赖数组保持简洁。6.3 StrictMode 下 effect 执行两次不是 bugReact 18 在开发模式下如果组件被StrictMode包裹挂载时useEffect、useState初始化函数、useReducer的 reducer 都会被故意执行两次。不少人第一次遇到这个问题第一反应是抓狂甚至误以为是自己代码问题。这是 React 有意为之的行为。它模拟“组件被卸载再重新挂载”的过程用来暴露那些没有正确清理副作用的代码。如果你在一个useEffect里注册了全局监听但忘记清理StrictMode 的双执行就会立刻暴露“重复注册”。如果清理函数写对了双执行反而什么都看不见。所以遇到 StrictMode 双执行正确反应应该是庆幸它在开发阶段帮你排查清理逻辑而不是去把 StrictMode 摘掉。生产环境下这种行为不会出现可以放心。还有一个和 StrictMode 关联的排查方向如果刚升级到 React 18发现某些接口在开发模式下请求了两次先检查是不是 StrictMode 导致的再检查是不是 effect 依赖里写了不稳定的引用比如每次渲染都新建的对象最后检查是不是真的没有写清理函数。6.4 一次完整的排查链路setInterval 读取最新 state我举一个自己实际遇过的排查过程帮你建立“按链路走”的感觉。当时一个页面有实时轮询每隔 5 秒要基于当前筛选条件重新请求数据。代码长这样const [query, setQuery] useState(); useEffect(() { const timer setInterval(() { fetchData(query); // query 永远是旧值 }, 5000); return () clearInterval(timer); }, []);现象是筛选条件改了轮询请求里带的还是旧关键词。排查顺序是第一步先确认 effect 是否重新执行。我在 effect 里加了一行 console发现只打印了一次说明 effect 本身没有再跑。问题定位到定时器回调闭包。第二步验证闭包捕获的是哪个值。我在回调里把query和queryRef.current同时打出来前者一直是空字符串后者是当前最新值。确认就是闭包问题。第三步修复。选择在定时器回调里从 ref 读取最新 query同时保证 ref 每次渲染同步。这个方案比“把 query 加进依赖数组”更合理因为后者会导致定时器每改一次条件就销毁重建浪费资源且逻辑上“轮询器”不是稳定的。第四步回归验证。改完之后观察一组行为初始空 query 请求一次修改 query 后再等 5 秒发出新 query 的请求清理页面后定时器被清除。三步都通过修复完成。这套链路看起来简单但整个排查过程里最值钱的不是最后那行修复代码而是“先确认 effect 有没有执行再确认闭包捕获值最后选择不会被重渲染打乱位置的解法”这个思路。面试时能把排查链路完整讲出来远远比背出十个 Hook 名称有用。Hooks 这个东西说到底没有太多玄机核心就三点函数组件每次渲染都有独立的闭包快照Hooks 通过 Fiber 节点上的链表按顺序保存状态effect 和依赖数组共同管理副作用的生命周期。把这三句话吃透再用useState、useEffect、useRef这几个高频 Hook 把肌肉记忆练出来剩下的useReducer、useContext、useMemo、useCallback都只是在不同场景下的组合变体。我在带人时总说Hook 叫什么名字不重要重要的是你手上的场景需要哪一种能力以及你知不知道每一次调用背后 React 正在做什么。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑