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跨实例复用动画元素,干扰了显示效果。
修复步骤
- 简化子组件状态:每个
TenseGroup只需要管理自身区域的激活状态,把原有的对象状态改成单个字符串:
// 替换子组件内的useState const [activeTense, setActiveTense] = useState("present");
- 简化状态更新逻辑:点击时直接更新当前实例的激活时态即可:
function handleTensesClick(tense) { setActiveTense(tense); }
- 保证动画元素唯一性:给
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
相关产品推荐
相关产品推荐

