Next.js中react-spring服务端与客户端样式不匹配警告的最优解法
解决Next.js + react-spring BottomSheet组件的服务端/客户端样式不匹配警告
问题重现
使用Next.js结合react-spring开发BottomSheet组件时,功能正常但出现如下警告:
Warning: Prop
styledid not match. Server: "transform:translate3d(0,Infinitypx,0)" Client: "transform:translate3d(0,652px,0)"
问题根源是服务端无可用视口高度,react-spring计算finalHeight时得到Infinity,而客户端能获取视口高度算出正确数值,导致Next.js判定服务端与客户端渲染样式不一致。
最优解决方案
核心思路是给服务端提供一个安全的初始值,客户端挂载后再更新为真实计算值,确保服务端与客户端初始渲染的样式匹配,之后再由react-spring处理动画。
修改后的代码示例
import { useState, useEffect } from 'react'; import { a, config, useSpring } from '@react-spring/web'; export function BottomSheet({ propsHeight, onClose, getActions }) { // 服务端默认设置安全初始值,避免Infinity const [finalHeight, setFinalHeight] = useState(0); const [{ y }, api] = useSpring(() => ({ y: finalHeight })); const open = (dragEvent?: any) => { const canceled = dragEvent?.canceled; api.start({ y: 0, immediate: false, config: canceled ? config.wobbly : config.stiff, }); }; const close = (velocity = 0) => { api.start({ y: finalHeight, immediate: false, onResolve() { onClose?.(); }, config: { ...config.stiff, velocity }, }); }; useEffect(() => { // 客户端挂载后计算真实的finalHeight const calculatedHeight = propsHeight || window.innerHeight - 62; setFinalHeight(calculatedHeight); // 立即同步spring状态到真实值,避免动画跳变 api.start({ y: calculatedHeight, immediate: true }); // 传递操作方法给父组件 getActions?.(open, close); }, [propsHeight, getActions]); const display = y.to((py) => (py < finalHeight ? 'flex' : 'none')); return ( <a.div style={{ y, }} /> ); }
方案说明
- 服务端安全初始值:用
useState给finalHeight设置初始值0,确保服务端渲染时transform的数值是合法的0px,避免Infinity。 - 客户端动态更新:在
useEffect中(仅客户端执行)获取视口高度计算真实的finalHeight,同时通过api.start立即同步spring的状态,确保客户端hydrate后样式无缝衔接,不会出现动画跳变。 - 保留SSR优势:组件依然可以在服务端渲染基础结构,不影响首屏加载和SEO。
备选方案(适合无需SSR的场景)
如果BottomSheet不需要服务端渲染,可以用Next.js的dynamic导入禁用SSR,彻底避免服务端与客户端的渲染差异:
import dynamic from 'next/dynamic'; const BottomSheet = dynamic(() => import('./BottomSheet'), { ssr: false });
内容的提问来源于stack exchange,提问作者Velidan
相关产品推荐
相关产品推荐

