AnimatePresence失效:React登录注册组件循环切换动画异常
解决Framer Motion退出动画不生效的问题
问题原因
AnimatePresence生效的两个核心条件未满足:
- AnimatePresence的直接子元素必须带有唯一且稳定的
key:你当前把key放在子组件内部的motion元素上,AnimatePresence无法追踪组件的卸载状态,导致直接移除DOM。 - 子组件根元素的
min-h-screen样式和父容器的w-[40vw]冲突,布局异常会干扰动画过渡效果。
解决方案
1. 给AnimatePresence的直接子元素添加唯一key
修改UserLoginSignUp.jsx,在条件渲染的<Login>和<Signup>组件上添加key,同时给父容器补充高度属性确保布局稳定:
import { Login } from "./Login"; import { Signup } from "./Signup"; import { AnimatePresence } from "framer-motion"; function UserLoginSignUp({ isloged, setisloged }) { return ( <> <div> <div className="w-[40vw] h-screen"> <AnimatePresence> {isloged ? ( <Login key="login" setisloged={setisloged} /> ) : ( <Signup key="signup" setisloged={setisloged} /> )} </AnimatePresence> </div> <div></div> </div> </> ); } export { UserLoginSignUp };
2. 调整子组件的全屏样式,避免布局冲突
修改Login.jsx和Signup.jsx的根motion.div,将min-h-screen改为h-full,让子组件高度适配父容器:
Login.jsx 修改后
import { useState } from "react"; import { motion } from "framer-motion"; function Login({ setisloged }) { const [masterKey, setMasterKey] = useState(""); const handleLogin = () => { }; const signupHandler = () => { setisloged(false); }; return ( <motion.div initial={{ opacity: 0, y: -100 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1 }} exit={{ opacity: 0, y: 200 }} className="h-full flex items-center justify-center bg-gradient-to-b from-[#95dab6] to-[#83b2d0]" > <motion.div initial={{ opacity: 0, y: -100 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1 }} exit={{ opacity: 0, y: 200 }} className="bg-white p-8 rounded shadow-md w-96" > <h2 className="text-2xl font-semibold mb-4">Login</h2> <div className="mb-4"> <input type="text" placeholder="Enter master key" className="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-red-600 focus:border-blue-300" value={masterKey} onChange={(e) => setMasterKey(e.target.value)} /> </div> <button className="w-full bg-[#95dab6] text-white py-2 rounded hover:bg-green-400" onClick={handleLogin} > Login </button> <p className="mt-4 text-center"> Don't have an account?{" "} <span className="text-green-400 cursor-pointer" onClick={signupHandler} > Signup </span> </p> </motion.div> </motion.div> ); } export { Login };
Signup.jsx 修改后
import { useState } from "react"; import { motion } from "framer-motion"; function Signup({ setisloged }) { const [fullName, setFullName] = useState(""); const [email, setEmail] = useState(""); const [masterKey, setMasterKey] = useState(""); const handleSignup = () => { }; const loginHandler = () => { setisloged(true); }; return ( <motion.div initial={{ opacity: 0, y: -100 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1 }} exit={{ opacity: 0, y: 200 }} className="h-full flex items-center justify-center bg-gradient-to-b from-blue-600 to-purple-400" > <motion.div initial={{ opacity: 0, y: -100 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1 }} exit={{ opacity: 0, y: 200 }} className="bg-white p-8 rounded shadow-md w-96" > <h2 className="text-2xl font-semibold mb-4">Signup</h2> <div className="mb-4"> <input type="text" placeholder="Enter Full Name" className="w-full px-3 py-2 border rounded focus:outline-none focus:ring focus:border-blue-300" value={fullName} onChange={(e) => setFullName(e.target.value)} /> </div> <div className="mb-4"> <input type="email" placeholder="Enter Email" className="w-full px-3 py-2 border rounded focus:outline-none focus:ring focus:border-blue-300" value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <div className="mb-4"> <input type="text" placeholder="Enter your Masterkey" className="w-full px-3 py-2 border rounded focus:outline-none focus:ring focus:border-blue-300" value={masterKey} onChange={(e) => setMasterKey(e.target.value)} /> </div> <button className="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600 mb-2" onClick={handleSignup} > Signup </button> <button className="w-full bg-gray-300 text-gray-700 py-2 rounded hover:bg-gray-400" onClick={loginHandler} > Login </button> </motion.div> </motion.div> ); } export { Signup };
原理说明
- AnimatePresence通过
key识别元素的挂载/卸载状态,只有直接子元素的key才能被正确追踪,内部元素的key无法触发退出动画。 - 调整子组件高度为
h-full后,父容器的尺寸限制生效,避免全屏布局导致的动画过渡时的视觉异常。
内容的提问来源于stack exchange,提问作者Krishna Biswal
相关产品推荐
相关产品推荐

