You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中react-spring服务端与客户端样式不匹配警告的最优解法

解决Next.js + react-spring BottomSheet组件的服务端/客户端样式不匹配警告

问题重现

使用Next.js结合react-spring开发BottomSheet组件时,功能正常但出现如下警告:

Warning: Prop style did 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,
      }}
    />
  );
}

方案说明

  1. 服务端安全初始值:用useState给finalHeight设置初始值0,确保服务端渲染时transform的数值是合法的0px,避免Infinity。
  2. 客户端动态更新:在useEffect中(仅客户端执行)获取视口高度计算真实的finalHeight,同时通过api.start立即同步spring的状态,确保客户端hydrate后样式无缝衔接,不会出现动画跳变。
  3. 保留SSR优势:组件依然可以在服务端渲染基础结构,不影响首屏加载和SEO。

备选方案(适合无需SSR的场景)

如果BottomSheet不需要服务端渲染,可以用Next.js的dynamic导入禁用SSR,彻底避免服务端与客户端的渲染差异:

import dynamic from 'next/dynamic';

const BottomSheet = dynamic(() => import('./BottomSheet'), { ssr: false });

内容的提问来源于stack exchange,提问作者Velidan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:27:51