react-use 之 useBeforeUnload:在 React 中拦截页面刷新与关闭的 Side-effect Hook 实战指南
react-use 之 useBeforeUnload在 React 中拦截页面刷新与关闭的 Side-effect Hook 实战指南【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use本文围绕 react-use 中的useBeforeUnloadHook 展开介绍如何通过一行式声明在 React 组件里监听浏览器的beforeunload事件在用户尝试刷新页面或关闭标签页时弹出离开确认提示常用于表单未保存提醒、编辑状态保护等场景。读完本文你将掌握布尔值与函数两种启用方式的完整用法、参数默认值与底层源码实现原理并能在实际项目中正确规避函数引用频繁变化带来的监听器重复注册问题。useBeforeUnload是 react-use 提供的Side-effect Hooks之一见 Side-effects.md其职责非常聚焦当用户尝试重新加载或关闭页面时通过浏览器原生beforeunload事件触发确认弹窗防止未保存的数据意外丢失。它已在 入口文件 中统一导出可直接从react-use包引入使用。基本用法布尔值开关最直观的用法是传入一个布尔值作为开关。当为true时页面会被武装用户刷新或关闭页面将触发浏览器确认框为false时则完全静默。import {useBeforeUnload} from react-use; const Demo () { const [dirty, toggleDirty] useToggle(false); useBeforeUnload(dirty, You have unsaved changes, are you sure?); return ( div {dirty pTry to reload or close tab/p} button onClick{() toggleDirty()}{dirty ? Disable : Enable}/button /div ); };上述示例中useToggle(false)来自同仓库的 useToggle.ts返回[state, toggle]toggle无参调用时取反、传入布尔值时直接赋值用来维护是否有未保存修改的dirty状态点击按钮开启后页面会显示提示文案此时刷新或关闭标签页浏览器会弹出离开确认再次点击按钮关闭后事件监听随即移除页面恢复可无打扰关闭。该用法与 stories/useBeforeUnload.story.tsx 中的Demo (boolean)演示完全一致可以直接在 Storybook 中交互体验。函数式启用动态判断是否拦截某些场景下是否拦截依赖实时计算的状态此时可以传入一个返回布尔值的函数。useBeforeUnload会在事件触发时调用该函数依据返回值决定是否弹出确认。import {useBeforeUnload} from react-use; const Demo () { const [dirty, toggleDirty] useToggle(false); const dirtyFn useCallback(() { return dirty; }, [dirty]); useBeforeUnload(dirtyFn, You have unsaved changes, are you sure?); return ( div {dirty pTry to reload or close tab/p} button onClick{() toggleDirty()}{dirty ? Disable : Enable}/button /div ); };需要注意每次dirtyFn引用变化都会重新注册事件回调源码见下文因此如果测试值变化频繁建议结合useCallback或 refs 稳定函数引用避免监听器被反复销毁重建。这一约束在原文档中已明确标注也是函数式用法与布尔式用法在工程上的核心差异。参数说明与默认值从 useBeforeUnload.ts 的函数签名可以看出完整的参数契约参数类型默认值说明enabledboolean \| (() boolean)true是否启用拦截传入函数时在每次事件触发时调用以其返回值作为最终判断依据messagestring \| undefinedundefined自定义确认提示文案会写入event.returnValueenabled省略时默认true即useBeforeUnload()裸调用也会直接启用页面离开拦截message可选。现代浏览器对自定义文案支持有限多数情况下仍会显示浏览器内置的通用提示但该参数仍建议按产品文案要求传入Hook 不返回任何值纯副作用使用时无需接收返回值。源码原理从事件监听到清理useBeforeUnload的实现非常精简核心逻辑完全依赖 React 的useCallback与useEffectuseBeforeUnload.tsconst useBeforeUnload (enabled: boolean | (() boolean) true, message?: string) { const handler useCallback( (event: BeforeUnloadEvent) { const finalEnabled typeof enabled function ? enabled() : true; if (!finalEnabled) { return; } event.preventDefault(); if (message) { event.returnValue message; } return message; }, [enabled, message] ); useEffect(() { if (!enabled) { return; } on(window, beforeunload, handler); return () off(window, beforeunload, handler); }, [enabled, handler]); };拆解其工作流程回调构造handler用useCallback缓存依赖[enabled, message]。当enabled是函数时每次事件触发都会调用它得到finalEnabled若为false则直接return不拦截事件拦截finalEnabled为真时调用event.preventDefault()并将message写入event.returnValue同时作为返回值返回——这正是beforeunload事件要求必须设置returnValue才能弹出确认框的经典写法监听注册useEffect在enabled为真时通过on(window, beforeunload, handler)注册监听卸载时调用off(window, beforeunload, handler)移除实现彻底的清理不会产生内存泄漏依赖与重注册useEffect依赖[enabled, handler]当enabled变化或handler因enabled/message变化而重建时监听会先移除再重新注册。这也从源码层面解释了函数式用法必须稳定函数引用的原因——若每次渲染都传新函数监听器将随每次渲染被销毁重建造成不必要的性能开销。其中on/off是 react-use 封装的跨环境事件绑定工具misc/util.ts内部会先判断目标对象是否存在addEventListener/removeEventListener方法再做绑定在 SSR 等无window环境下也能安全兜底。与 useToggle 的组合模式文档示例中反复出现的useToggle是 react-use 中与useBeforeUnload搭配最自然的兄弟 Hook。其实现基于useReduceruseToggle.tsconst toggleReducer (state: boolean, nextValue?: any) typeof nextValue boolean ? nextValue : !state;它支持两种调用方式不传参时切换布尔值传布尔值时强制设置为该值。在草稿编辑保护场景中通常用useToggle(false)初始化未修改状态在表单onChange时toggle(true)置脏提交成功后toggle(false)复位再配合useBeforeUnload(dirty)实现完整的离开拦截闭环。典型应用场景表单与编辑器防丢失内容已修改但未保存时启用拦截防止误刷新丢失草稿多步流程守卫向导式流程中途离开时提示用户当前进度会丢失自定义兜底判断当是否拦截依赖复杂条件如上传进行中、计时器运行中时用函数式写法在事件发生时实时求值。注意事项与浏览器行为边界拦截范围有限beforeunload只能拦截刷新、关闭标签页等导航行为无法拦截浏览器崩溃、断网等异常场景文案兼容性部分浏览器会忽略自定义returnValue文案仅显示通用提示因此不要依赖message承载关键信息函数引用稳定性函数式启用时务必用useCallback或 ref 稳定引用避免监听器频繁重建源码依赖分析见上文不要过度使用在不需要拦截的页面如已提交成功的回显页务必确保enabled为false或组件卸载以免给用户造成困扰框架层限制React 18 在部分场景对beforeunload事件处理有行为差异如遇异常建议在浏览器端直接验证原生事件行为再回归到 Hook 层排查。相关资源官方文档docs/useBeforeUnload.md源码实现src/useBeforeUnload.ts配套演示stories/useBeforeUnload.story.tsx含Demo (boolean)与Demo (function)两个 Story事件工具函数src/misc/util.tsSide-effect Hooks 分类索引docs/Side-effects.mdREADME 中的完整 Hook 列表README.md安装与运行本仓库的演示环境可参照 package.json 中的脚本yarn安装依赖后执行yarn storybook启动 Storybook默认端口 6008即可在 Side effects/useBeforeUnload 分组下直观体验上述两种用法。【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考