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

React文本切换组件切换方向后初始滚动方向异常求助

React文本切换滚动方向初始错误修复

问题场景

实现了一个文本段落切换组件,核心需求:

  • 初始显示第1段文本
  • 正向切换(如1→2→3):旧文本向上滚出视野,新文本从底部滑入
  • 反向切换(如3→2→1):旧文本向下滚出视野,新文本从顶部滑入

当前问题:第一次切换方向时(比如从2切回1),滚动方向错误,后续操作恢复正常。

问题根源

问题出在WorkProcessText组件的状态更新逻辑:

  1. 用useState存储direction,状态更新是异步的,当activeKey变化时,useLayoutEffect里更新direction,但motion组件渲染时还没拿到最新的方向值,导致第一次动画用了旧方向
  2. 用displayedText作为motion.p的key,如果文本重复会导致动画不触发,虽然当前示例文本不重复,但这是潜在隐患

修复方案

核心修改点

  1. 用同步更新的ref替代useState存储滚动方向,避免异步状态更新的延迟
  2. 用唯一的activeKey作为motion.p的key,确保每次切换都能正确触发动画

修复后的WorkProcessText.js

import React, { useRef, useLayoutEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";

const WorkProcessText = ({ displayedText, activeKey }) => {
  const prevKeyRef = useRef(activeKey);
  const directionRef = useRef("up"); // 用ref同步存储方向

  useLayoutEffect(() => {
    const prevKey = prevKeyRef.current;

    // 计算切换方向并更新ref
    if (activeKey > prevKey) {
      directionRef.current = "up";
    } else if (activeKey < prevKey) {
      directionRef.current = "down";
    }

    prevKeyRef.current = activeKey;
  }, [activeKey]);

  return (
    <AnimatePresence mode="wait">
      <motion.p
        key={activeKey} // 使用唯一的activeKey作为动画触发标识
        className="text-s text-center w-[90%]"
        initial={{ y: directionRef.current === "up" ? 300 : -300 }}
        animate={{ y: 0 }}
        exit={{ y: directionRef.current === "up" ? -300 : 300 }}
        transition={{ duration: 0.3, ease: "easeInOut" }}
      >
        {displayedText}
      </motion.p>
    </AnimatePresence>
  );
};

export default WorkProcessText;

可选优化(简化主组件逻辑)

主组件中可以不用单独维护displayedText状态,直接通过activeKey从textOptions中获取文本,减少状态冗余:

// WorkProcess.jsx 修改部分
function WorkProcess() {
  const [activeItem, setActiveItem] = useState(textOptions[0].key);
  // 直接通过activeItem匹配文本
  const displayedText = textOptions.find(item => item.key === activeItem).text;

  const handleListItemClick = (key) => {
    setActiveItem(key);
  };

  return (
    // ... 其余结构不变
    <WorkProcessList
      activeItem={activeItem}
      handleListItemClick={handleListItemClick}
      textOptions={textOptions}
    />
  );
}

// WorkProcessList.js 修改onClick传参
<li
  key={key}
  onClick={() => handleListItemClick(key)}
  // ... 其余class不变
>
  {title}
</li>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:54:51