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

React多组件实例JSX条件渲染异常:激活状态跨实例覆盖

问题描述

我想在React的多个组件实例中同时条件渲染JSX,需求是给当前激活的标题添加下划线,每个组件实例始终要有一个激活标题。但现在所有区域总共只有一个激活标题,更新activeTenses状态时,修改一个键值对会导致另一个键被重置为初始值"present"。

父组件代码
export default function VerbPage() {
  let params = useParams();

  return (
    <>
      <Nav />
      <Header verb={params.verb} />
      <TenseGroup tenseSection="indicative" tenses={["Present", "Preterite", "Imperfect", "Conditional", "Future"]}>Present Indicative</TenseGroup>
      <TenseGroup tenseSection="subjunctive" tenses={["Present", "Imperfect", "Future"]}>Subjunctive</TenseGroup>
    </>
  );
}
子组件代码
export default function TenseGroup({ children, tenses, tenseSection }) {
  const [activeTenses, setActiveTenses] = useState({
    indicative: "present",
    subjunctive: "present"
  });

  function handleTensesClick(tense) {
    setActiveTenses((prevTense) => {
      return {
        ...prevTense,
        [tenseSection]: tense
      }
    });
  }

  return (
    <section>
      <div>{children}</div>
      <ul>
      {tenses.map((tense) => (
        <li key={tense}>
          <div onClick={() => handleTensesClick(tense.toLowerCase())}>{tense}</div>
          {activeTenses[tenseSection] === tense.toLowerCase() && (
            <motion.div className={classes["tense__underline"]} layoutId="animate-tense"></motion.div>
          )}
        </li>
      ))}
    </ul>
    </section>
  )
}
解决方案

问题根源有两个:一是每个TenseGroup组件实例没必要维护包含两个section的完整状态对象,二是相同的layoutId导致Framer Motion跨实例复用动画元素,干扰了显示效果。

修复步骤

  1. 简化子组件状态:每个TenseGroup只需要管理自身区域的激活状态,把原有的对象状态改成单个字符串:
// 替换子组件内的useState
const [activeTense, setActiveTense] = useState("present");
  1. 简化状态更新逻辑:点击时直接更新当前实例的激活时态即可:
function handleTensesClick(tense) {
  setActiveTense(tense);
}
  1. 保证动画元素唯一性:给layoutId添加tenseSection前缀,避免不同实例的动画元素冲突:
{activeTense === tense.toLowerCase() && (
  <motion.div className={classes["tense__underline"]} layoutId={`animate-tense-${tenseSection}`}></motion.div>
)}

完整修复后的子组件代码

export default function TenseGroup({ children, tenses, tenseSection }) {
  const [activeTense, setActiveTense] = useState("present");

  function handleTensesClick(tense) {
    setActiveTense(tense);
  }

  return (
    <section>
      <div>{children}</div>
      <ul>
        {tenses.map((tense) => (
          <li key={tense}>
            <div onClick={() => handleTensesClick(tense.toLowerCase())}>{tense}</div>
            {activeTense === tense.toLowerCase() && (
              <motion.div className={classes["tense__underline"]} layoutId={`animate-tense-${tenseSection}`}></motion.div>
            )}
          </li>
        ))}
      </ul>
    </section>
  )
}

修改后每个TenseGroup实例独立管理自身的激活状态,动画元素也不会互相干扰,就能实现每个区域都有独立的激活下划线效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:06:20