Material UI折叠面板动画失效求助:子组件引入无平滑过渡
Material UI Accordion封装子组件后动画失效的解决方案
问题原因
最常见的触发原因是子组件被定义在父组件的渲染函数内部,导致父组件每次重新渲染时都会创建一个全新的AccordionItem组件类型。React会将其识别为不同的组件,从而卸载旧组件并重新挂载新组件——这会丢失Accordion内部的状态和动画依赖的DOM稳定性,最终导致折叠/展开时无平滑过渡,直接突变。
另外,如果父组件控制Accordion的expanded状态时,状态管理不稳定或未给Accordion设置唯一key,也可能引发类似问题。
解决方案
1. 将子组件移到父组件外部定义
不要在函数组件的内部(即App函数里)定义子组件,而是把AccordionItem放在父组件外部或单独的文件中:
// 正确:子组件定义在父组件外部 import { Accordion, AccordionDetails, AccordionSummary, Typography } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; const AccordionItem = () => { return ( <Accordion> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography>Accordion 1</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. </Typography> </AccordionDetails> </Accordion> ); }; export default function App() { return ( <div className="App"> <AccordionItem /> </div> ); }
2. 若需依赖父组件props,用useMemo缓存组件
如果子组件必须依赖父组件的props才能定义,可以用React.useMemo缓存组件,避免每次渲染都重新创建:
import React from "react"; import { Accordion, AccordionDetails, AccordionSummary, Typography } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; export default function App({ panelTitle }) { // 用useMemo缓存组件,仅当依赖项变化时才重新创建 const AccordionItem = React.useMemo(() => { return () => ( <Accordion> <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography>{panelTitle}</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. </Typography> </AccordionDetails> </Accordion> ); }, [panelTitle]); return <AccordionItem />; }
3. 受控模式下确保状态稳定并设置唯一key
如果使用父组件控制Accordion的展开状态,要保证状态逻辑稳定,同时给Accordion设置唯一的key属性,避免不必要的组件重新挂载:
import React, { useState } from "react"; import { Accordion, AccordionDetails, AccordionSummary, Typography } from "@mui/material"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; const AccordionItem = ({ expanded, onChange, title }) => { return ( <Accordion expanded={expanded} onChange={onChange} key={title} // 设置唯一key > <AccordionSummary expandIcon={<ExpandMoreIcon />}> <Typography>{title}</Typography> </AccordionSummary> <AccordionDetails> <Typography> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, sit amet blandit leo lobortis eget. </Typography> </AccordionDetails> </Accordion> ); }; export default function App() { const [expandedPanel, setExpandedPanel] = useState<string | false>(false); const handlePanelChange = (panelId: string) => (_, isExpanded) => { setExpandedPanel(isExpanded ? panelId : false); }; return ( <div className="App"> <AccordionItem title="Accordion 1" expanded={expandedPanel === "panel1"} onChange={handlePanelChange("panel1")} /> </div> ); }
内容的提问来源于stack exchange,提问作者branperr
相关产品推荐
相关产品推荐

