useEffect执行时机踩坑终极解:react-custom-hooks的useEffectOnce与useUpdateEffect实战
【免费下载链接】react-custom-hooksReact Custom Hooks @ S.Leschev: useArray useAsync useClickOutside useCookie useCopyToClipboard useDarkMode useDebounce useDebugInformation useDeepCompareEffect useEffectOnce useEventListener useFetch useGeolocation useHover useLongPress useMediaQuery useOnlineStatus useOnScreen usePrevious useRenderCount useScript etc.项目地址: https://gitcode.com/gh_mirrors/re/react-custom-hooks
写 React 组件时,你是否也踩过这样的坑:useEffect在组件挂载时就会执行一次,可你只想在它“变化”之后才响应,结果得手写一堆isMounted标记;或者你只想在挂载时执行一次逻辑,却总担心依赖数组写错。开源项目react-custom-hooks提供了 20+ 个生产级自定义 Hook,其中的useEffectOnce和useUpdateEffect两个轻量 Hook,正好一次性解决 useEffect 执行时机的两大高频痛点:🎯
一、useEffect 执行时机,为什么容易踩坑?
先回顾一下useEffect的默认行为,这也是坑的来源:
| 场景 | 你的期望 | useEffect 的实际行为 |
|---|---|---|
| 只想挂载时初始化 | 只跑 1 次 | 依赖数组必须写空[],写错就反复执行 |
| 只关心数据变化 | 跳过首次 | 首次渲染也会执行,需要手写标记变量 |
而 react-custom-hooks 的思路很简单:把这些“重复的防御性写法”封装成两个开箱即用的 Hook,一行调用替代一堆样板代码。
二、三步拿到代码,跑通官方 Demo
1️⃣ 克隆仓库:
git clone https://gitcode.com/gh_mirrors/re/react-custom-hooks cd react-custom-hooks npm install && npm start2️⃣ 打开 src/App.js,取消注释对应演示组件即可切换示例;
3️⃣ 本项目使用 React 18 + CRA,无需任何额外配置,npm start后浏览器直接体验。
三、useEffectOnce:挂载时只执行一次,告别空依赖数组
useEffectOnce的完整实现只有 3 行,位于 useEffectOnce.js:
export default function useEffectOnce(cb) { useEffect(cb, []) }它的价值在于语义自解释——代码评审时看到useEffectOnce(() => initLibrary()),一眼就知道“这段逻辑只在挂载时跑一次”,而不需要再去核对某个空数组是否正确。
官方演示组件 EffectOnceComponent.js 中,组件挂载时弹出一句 "Hi",之后无论计数器怎么自增都不会再次触发,非常适合作为你的上手示例。
典型使用场景:
- 初始化第三方图表 / 地图库
- 拉取初始数据、埋点上报
- 注册全局事件监听
四、useUpdateEffect:跳过首次渲染,只响应后续变化
如果说useEffectOnce解决“只要第一次”,那useUpdateEffect就解决“只要后面每一次”。它的实现同样简洁,核心是一个 ref 标记,源码见 useUpdateEffect.js:
export default function useUpdateEffect(callback, dependencies) { const firstRenderRef = useRef(true) useEffect(() => { if (firstRenderRef.current) { firstRenderRef.current = false return } return callback() }, dependencies) }用法和useEffect完全一致,多出来的行为只有“跳过第一次”:
// 跳过首次渲染,仅在 count 后续变化时执行 useUpdateEffect(() => { console.log("count 更新为:", count) }, [count])官方示例 UpdateEffectComponent.js 演示了点击 Increment 按钮后才弹出最新值,首次渲染保持静默——这正是“监听变化”类需求的正确姿势。
典型使用场景:
- 数据变化后同步 URL 参数
- 依赖变化时重新订阅外部服务
- 避免首屏发出一次多余的“无意义”请求
五、一张表学会选型:什么时候用哪个?
| Hook | 首次挂载 | 依赖变化后 | 一句话总结 |
|---|---|---|---|
useEffect | ✅ 执行 | ✅ 执行 | 默认选择 |
useEffectOnce | ✅ 执行 | ❌ 不执行 | 只要“出生礼” |
useUpdateEffect | ❌ 跳过 | ✅ 执行 | 只要“成长史” |
六、实战避坑小贴士
- 开发模式下的“双重执行”是正常的:本项目 src/index.js 启用了
React.StrictMode,开发环境中所有 effect 会被故意挂载两次用于暴露不纯的副作用,这是 React 官方行为,生产构建不会出现,不必误判为 Bug。 - 配合防抖更丝滑:变化监听类 effect 常和 useDebounce.js 组合使用,避免高频变化触发密集副作用。
- 排查“到底渲染了几次”:用 useRenderCount.js 实时显示渲染次数,配合 useDebugInformation.js 定位不必要的重渲染。
- 深度依赖比较:当依赖是对象且浅比较失效时,可升级使用 useDeepCompareEffect.js。
写在最后
useEffect 执行时机的问题,本质上只是 React 没有内建“只挂载 / 只更新”两种模式。react-custom-hooks 用两个不到 15 行的 Hook 就把这套心智模型补齐了:记住口诀——挂载一次找useEffectOnce,跳过首次找useUpdateEffect,你的 effect 代码会立刻清爽一个量级。💪
【免费下载链接】react-custom-hooksReact Custom Hooks @ S.Leschev: useArray useAsync useClickOutside useCookie useCopyToClipboard useDarkMode useDebounce useDebugInformation useDeepCompareEffect useEffectOnce useEventListener useFetch useGeolocation useHover useLongPress useMediaQuery useOnlineStatus useOnScreen usePrevious useRenderCount useScript etc.项目地址: https://gitcode.com/gh_mirrors/re/react-custom-hooks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考