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

Framer-Motion切换进入/退出状态时首次反向动画异常求助

问题描述

尝试根据用户操作的前进(state > prevState)或后退(state < prevState)状态为元素添加动画:

  • 通过自定义useStateWithPrevious Hook跟踪当前状态与历史状态
  • 在Framer-Motion的AnimatePresence组件中配置initial和exit动画参数

目前大部分场景正常,但首次切换方向时退出动画表现异常,怀疑是定义退出过渡的变量存在闭包问题。

代码示例

自定义Hook

import { useState } from "react";
export default function useStateWithPrevious(initial) {
    const [value, setValue] = useState(initial);
    const [prev, setPrev] = useState(initial);

    function setValueAndPrev(newValue) {
        if (newValue === value) return; // 可选,根据逻辑调整
        setPrev(value);
        setValue(newValue);
    }

    return [prev, value, setValueAndPrev];
}

Hook使用

const [previousRegistrationDay, registrationDay, setRegistrationDay] = useStateWithPrevious(1);

Framer-Motion组件

<AnimatePresence mode="wait">
    <motion.nav
        key={registrationDay}
        animate={{ x: "0%", opacity: 1 }}
        transition={{ duration: 0.5 }}
        initial={{ opacity: 0, x: previousRegistrationDay < registrationDay ? "100%" : "-100%" }}
        exit={{ opacity: 0, x: previousRegistrationDay < registrationDay ? "-100%" : "100%" }}
    >
        <div>
            registrationDay{registrationDay}
            <br />
            previousRegistrationDay{previousRegistrationDay}
        </div>
    </motion.nav>
</AnimatePresence>
问题原因与解决方案

问题根源

原自定义Hook使用useState存储历史状态,而React的setState是异步批量更新的。首次调用setValueAndPrev时,prev和value的更新会在同一批次完成,但即将退出的旧组件(由AnimatePresence管理)会捕获更新前的状态值——此时previousRegistrationDay与registrationDay仍相等,导致exit动画的方向判断逻辑失效。

修复方案

将自定义Hook中的历史状态改用useRef存储,因为useRef的current属性是同步更新的,能确保在状态变化时立即获取到正确的历史值,避免闭包导致的状态捕获问题:

修改后的自定义Hook

import { useState, useRef, useEffect } from "react";
export default function useStateWithPrevious(initial) {
    const [value, setValue] = useState(initial);
    const prevRef = useRef(initial);

    useEffect(() => {
        // 每次value更新后,同步更新prevRef
        prevRef.current = value;
    }, [value]);

    function setValueAndPrev(newValue) {
        if (newValue === value) return;
        setValue(newValue);
    }

    // 返回prevRef.current作为历史值,当前值,更新方法
    return [prevRef.current, value, setValueAndPrev];
}

额外说明

确保AnimatePresence的mode="wait"配置正确,它会等待退出动画完成后再渲染新组件,避免动画冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 08:25:36