Nextjs报错:Text content does not match server-rendered HTML 修复咨询
修复Next.js中react-countdown的"Text content does not match server-rendered HTML"错误
问题原因
服务端渲染时,API返回的ISO时间字符串(如2022-12-25T11:22:37.000Z)在服务端和客户端的解析结果可能存在差异(比如时区处理、时间解析工具的逻辑差异),导致react-countdown渲染的倒计时文本在服务端生成的HTML和客户端hydrate后的内容不一致,触发Next.js的内容一致性校验错误。而使用Date.now() + 1000 * 60 * 60时,因为是基于当前时间的相对值,组件渲染时的倒计时差值在服务端和客户端计算后初始展示一致,所以未触发错误。
修复方案
方案1:统一时间解析逻辑,确保服务端和客户端结果一致
使用UTC模式解析时间字符串,避免时区干扰,确保服务端和客户端生成完全相同的时间戳:
import React from 'react' import { Box } from '@mui/material' import { IFlashBanner } from '../../../interface/flashBanner' import Countdown from 'react-countdown' import moment from 'moment' const FloatingFlashSale = ({ id, title, dicount, countdownTime, image, }: IFlashBanner) => { // 使用moment.utc强制以UTC解析时间字符串,避免时区差异 const targetTime = moment.utc(countdownTime).valueOf() // 也可以用原生Date的UTC解析: // const targetTime = Date.parse(countdownTime) return ( <Box display="flex" sx={{ backgroundImage: `url(${image})`, backgroundSize: '100%', backgroundRepeat: 'no-repeat', }} > <Countdown date={targetTime} /> </Box> ) } export default FloatingFlashSale
方案2:禁用组件的服务端渲染
通过Next.js的dynamic导入,让react-countdown组件只在客户端渲染,彻底避免服务端和客户端的HTML差异:
import React from 'react' import { Box } from '@mui/material' import { IFlashBanner } from '../../../interface/flashBanner' import dynamic from 'next/dynamic' // 动态导入react-countdown,禁用SSR const Countdown = dynamic(() => import('react-countdown'), { ssr: false }) const FloatingFlashSale = ({ id, title, dicount, countdownTime, image, }: IFlashBanner) => { const targetTime = new Date(countdownTime).valueOf() return ( <Box display="flex" sx={{ backgroundImage: `url(${image})`, backgroundSize: '100%', backgroundRepeat: 'no-repeat', }} > <Countdown date={targetTime} /> </Box> ) } export default FloatingFlashSale
方案3:在客户端挂载后再初始化倒计时
使用useState和useEffect,等待客户端挂载后再设置目标时间,避免服务端渲染时的时间差异:
import React, { useState, useEffect } from 'react' import { Box } from '@mui/material' import { IFlashBanner } from '../../../interface/flashBanner' import Countdown from 'react-countdown' const FloatingFlashSale = ({ id, title, dicount, countdownTime, image, }: IFlashBanner) => { const [targetTime, setTargetTime] = useState<number | null>(null) useEffect(() => { // 客户端挂载后再解析时间 setTargetTime(new Date(countdownTime).valueOf()) }, [countdownTime]) // 未初始化时可以展示加载状态 if (!targetTime) return null return ( <Box display="flex" sx={{ backgroundImage: `url(${image})`, backgroundSize: '100%', backgroundRepeat: 'no-repeat', }} > <Countdown date={targetTime} /> </Box> ) } export default FloatingFlashSale
内容的提问来源于stack exchange,提问作者Nugraha
相关产品推荐
相关产品推荐

