如何解决页面重载后history.push传入的onCancel函数丢失、存入state报错问题
问题成因
- 直接在
history.push配置根节点传入的onCancel在重载后丢失,是因为History API仅会把pathname、search、hash、state这几个标准字段持久化到浏览器的会话历史栈中,根节点额外添加的自定义属性根本不会被写入历史记录。浏览器重载时会从历史存储中重建整个location对象,非标准的自定义字段会被直接丢弃,自然没法保留。 - 把
onCancel移到state里触发报错,是因为路由的state字段会经过结构化序列化后存储到浏览器的sessionStorage中,浏览器的持久化机制本身就不支持存储函数这类可执行的引用类型,序列化过程会直接触发报错。
本质上,可执行的回调函数根本不适合通过路由跳转参数传递,不管放在哪个配置位置,只要依赖浏览器的历史持久化机制,都没法正常工作。
可落地解决方案
方案1:通过可序列化标识匹配回调(推荐,兼容重载场景)
不需要传递函数本身,只需要在路由state中存入可序列化的标识值,告诉目标页面需要触发哪个回调,目标页面拿到标识后在本地执行对应逻辑即可。
正确的跳转写法示例:
history.push({ pathname: vaccineTaken == "Yes" || (vaccineTaken == 1 ? "/vaccination" : "/no-vaccine"), state: { ...data, // 仅存入可序列化的类型标识 cancelCallbackType: 'getExistingSurvey' } })
目标页面的处理逻辑示例:
import { useLocation } from 'react-router-dom' const { state } = useLocation() const handleCancelAction = () => { // 根据存储的标识匹配本地对应的回调执行 if (state?.cancelCallbackType === 'getExistingSurvey') { getExistingSurvey() } }
这个方案完全兼容浏览器重载场景,不存在序列化报错的问题。
方案2:通过模块内存缓存暂存回调(适合同会话跳转场景)
如果跳转流程不需要兼容页面重载,可以在两个页面共同引用的模块中维护一个内存级的回调缓存,跳转前把回调暂存到缓存中,目标页面取出执行即可。
首先定义公共的缓存对象:
// utils/routeCallbackCache.js export const routeCallbackCache = { onCancel: null }
跳转前暂存回调:
import { routeCallbackCache } from '@/utils/routeCallbackCache' // 存入需要执行的回调 routeCallbackCache.onCancel = getExistingSurvey history.push({ pathname: vaccineTaken == "Yes" || (vaccineTaken == 1 ? "/vaccination" : "/no-vaccine"), state: data })
目标页面取出执行:
import { routeCallbackCache } from '@/utils/routeCallbackCache' const handleCancelAction = () => { // 从内存缓存取出暂存的回调执行 routeCallbackCache.onCancel?.() // 执行完成后清空缓存,避免后续跳转残留脏值 routeCallbackCache.onCancel = null }
注意事项
- 严禁将函数、DOM节点等无法被结构化序列化的内容直接放入路由的
state字段,react-router底层基于浏览器原生History API实现,硬塞这类内容一定会触发浏览器抛错。 - 不要在
history.push的配置根节点添加需要持久化的自定义属性,这类属性不会被写入浏览器历史栈,重载后一定会丢失。
内容的提问来源于stack exchange,提问作者Muhammad Anas Khan
相关产品推荐
相关产品推荐

