Next.js搭配Preact使用react-spring时点击返回按钮触发TypeError报错
问题根因
这个报错是Preact与react-spring内置Context的卸载逻辑不兼容导致的。React的组件卸载顺序为「先卸载子组件,再销毁上层Context Provider」,但Preact的卸载顺序相反,Provider先被销毁后才执行子组件卸载逻辑,导致react-spring内部消费SpringContext时拿到的值为undefined,触发children解构失败的错误。
可行解决方案
按实现成本从低到高排序:
- 方案1:升级/降级react-spring版本
该问题是react-spring v9中期版本的已知兼容问题,最新版本已经针对非React运行时添加了Context值兜底判断,你可以直接升级@react-spring/web到最新稳定版,或降级到v9.4.5及以下的兼容版本即可解决。 - 方案2:手动添加SpringContext兜底
在你使用Transition组件的页面外层手动包裹SpringContext.Provider,传入空对象作为兜底值,避免Context为undefined的情况:import { SpringContext, Transition, animated } from '@react-spring/web' // 组件内部return部分修改为: return ( <SpringContext.Provider value={{}}> <div className={styles.animationContainer}> {/* 你原来的Transition代码保持不变 */} <Transition native unique items={currentQuestionFlow.id || 'completed'} onFrame={() => window.scroll(0, 0)} from={{opacity: 0, transform: `translate3d(${animation.from}, 0 ,0)`}} enter={{opacity: 1, transform: `translate3d(${animation.enter}, 0, 0)`}} leave={{opacity: 0, transform: `translate3(${animation.leave}, 0, 0)`}} > {transStyles => ( <animated.div style={{...transStyles}}> <Questions question={label} /> <UIDisplay DATA={DATA} /> </animated.div> )} </Transition> </div> </SpringContext.Provider> ) - 方案3:页面卸载时提前终止动画
在使用动画的页面添加useEffect钩子,卸载时清空所有react-spring待执行动画,避免卸载阶段触发Context消费逻辑:import { stop } from '@react-spring/web' import { useEffect } from 'react' // 组件内添加钩子 useEffect(() => { return () => { // 终止所有正在执行的动画 stop(true) } }, []) - 方案4:修改Preact别名配置
如果你不想修改业务代码,可在Next.js的webpack配置中指定react-spring依赖强制使用原生React,其他依赖依然走Preact兼容:// next.config.js const withPreact = require('next-plugin-preact'); module.exports = withPreact({ webpack: (config, { dev }) => { if (!dev) { config.resolve.alias = { ...config.resolve.alias, // react-spring相关依赖优先使用原生React 'react/jsx-runtime': require.resolve('react/jsx-runtime'), 'react-dom': require.resolve('react-dom'), } } return config } })
所有方案都可以保留Preact的包体积优化能力,不需要切换回全量React。
内容的提问来源于stack exchange,提问作者james emanon
相关产品推荐
相关产品推荐

