Next.js中useState未正确更新setState致动画异常问题
解决Next.js中Framer Motion首屏动画仅首次加载的问题
核心问题分析
你碰到的问题大概率是这两个原因:
- 组件初始化时没正确从
localStorage读取状态,导致useState初始值始终处于动画可播放状态 useEffect的依赖数组设置错误,或是逻辑里同时修改状态和触发依赖,引发无限渲染
修正方案
先看修正后的代码,再逐点解释:
import { motion } from "framer-motion"; import { useState, useEffect } from "react"; export default function Hero() { // 初始化状态:默认设为true(首次加载),后续在useEffect里从localStorage修正 const [showAnimation, setShowAnimation] = useState(true); useEffect(() => { // 只在客户端执行,避免Next.js SSR时访问localStorage报错 const hasSeenAnimation = localStorage.getItem("hasSeenHeroAnimation"); if (hasSeenAnimation) { // 如果已经看过动画,更新状态为false setShowAnimation(false); } else { // 首次加载:标记已看过动画,然后更新状态防止重复播放 localStorage.setItem("hasSeenHeroAnimation", "true"); setShowAnimation(false); } }, []); // 空依赖数组,确保只在组件挂载时执行一次 return ( <motion.div initial={{ opacity: 0, y: 20 }} animate={showAnimation ? { opacity: 1, y: 0 } : { opacity: 1, y: 0 }} transition={{ duration: 0.6 }} > {/* Hero组件内容 */} <h1>欢迎访问我的网站</h1> </motion.div> ); }
关键细节说明
- SSR兼容:Next.js服务端渲染时无法访问
localStorage,所以不能直接在useState里写localStorage.getItem(...),必须放在useEffect里(useEffect仅在客户端执行) - 空依赖数组:
useEffect用空依赖确保只在组件首次挂载时执行一次,不会触发无限渲染 - 状态更新时机:首次加载时先标记
localStorage,再更新showAnimation为false,确保动画只播放一次;用户刷新页面时,localStorage已有记录,直接把状态设为false,动画不再触发 - 结合动画完成事件(可选):如果想确保动画完全播放完再标记,可以用framer-motion的
onComplete:
这种方式下,<motion.div initial={{ opacity: 0, y: 20 }} animate={showAnimation ? { opacity: 1, y: 0 } : { opacity: 1, y: 0 }} transition={{ duration: 0.6 }} onComplete={() => { if (showAnimation) { localStorage.setItem("hasSeenHeroAnimation", "true"); setShowAnimation(false); } }} > {/* 内容 */} </motion.div>useEffect只负责读取localStorage设置初始状态,动画完成后再更新存储和状态,逻辑更严谨。
常见错误排查
- 如果还是无限渲染:检查是否在
useEffect里修改了依赖数组里的变量,比如把showAnimation放进依赖,同时在里面调用setShowAnimation,就会触发循环 - 如果动画还是一直显示:检查
localStorage的键名是否一致(比如存的时候是hasSeenHeroAnimation,读的时候写成hasSeenAnimation),或是初始化状态没被正确覆盖
内容的提问来源于stack exchange,提问作者VincentAb
相关产品推荐
相关产品推荐

