MUI Slide过渡前渲染异常:如何延迟formElements渲染避免闪烁
解决Slide过渡时formElements闪烁的问题
核心问题分析
你的代码中formElements始终会被渲染到DOM中,即使Slide组件处于隐藏状态,只是通过CSS做视觉隐藏。当切换页面时,新的formElements会提前渲染,和旧内容重叠导致闪烁。要解决这个问题,需要延迟formElements的渲染,直到Slide过渡完全完成。
解决方案
利用Material-UI Slide组件的onEntered回调(过渡完成后触发)来控制内容的渲染时机,同时拆分状态管理过渡和内容可见性:
- 用两个状态分别控制Slide的显示(
isSlideVisible)和内容的渲染(isContentReady) - 页面切换时,先隐藏内容,再触发Slide的过渡动画
- 过渡完成后,再渲染新的表单元素
改进后的代码
import { Container, Slide } from '@material-ui/core'; import { useEffect, useState } from 'react'; import { FormElement } from '..'; import { shouldAutoAdvance } from '../Page/page.helpers'; const InputArea = ({ components, currentPage, formInputsParentRef, iFrameId, pageComponentIds, slideDirection, visiblePageComponents, }) => { const isFirstLoad = slideDirection === undefined; // 控制Slide组件的显示/隐藏 const [isSlideVisible, setIsSlideVisible] = useState(isFirstLoad); // 控制formElements是否渲染 const [isContentReady, setIsContentReady] = useState(isFirstLoad); useEffect(() => { if (isFirstLoad) return; // 页面切换时,先隐藏内容,避免新旧内容重叠 setIsContentReady(false); // 先隐藏Slide,再触发进入过渡 setIsSlideVisible(false); const timer = setTimeout(() => { setIsSlideVisible(true); }, 50); // 给浏览器足够时间处理隐藏状态,避免过渡不生效 // 清理定时器,防止内存泄漏 return () => clearTimeout(timer); }, [currentPage, isFirstLoad]); const formElements = pageComponentIds.map((componentId) => { const elementData = components[`component_${componentId}`]; const { component, input } = elementData; const { type, duration, text, mappedQuestionId } = component; return ( <FormElement key={componentId} autoAdvance={shouldAutoAdvance(elementData, visiblePageComponents)} componentId={componentId} currentPage={currentPage} duration={duration} iFrameId={iFrameId} isVisible={visiblePageComponents.includes(elementData)} mappedQuestionId={mappedQuestionId} slideDirection={slideDirection} text={text} type={type} value={input?.value} /> ); }); return ( <Slide direction={slideDirection} in={isSlideVisible} exit={false} appear={false} timeout={500} // 过渡完成后再渲染内容 onEntered={() => setIsContentReady(true)} > <Container ref={formInputsParentRef} maxWidth="md"> {isContentReady ? formElements : null} </Container> </Slide> ); }; export default InputArea;
额外改进建议
- 移除无效的短延迟:原来的
setTimeout(0.1)几乎等同于同步执行,浏览器来不及处理状态变化,改用50ms延迟更合理 - 状态拆分:将过渡控制和内容渲染的状态分开,逻辑更清晰,便于维护
- 清理副作用:在useEffect中添加定时器清理函数,避免内存泄漏
- 简化props传递:可以考虑将
elementData直接传递给FormElement,减少重复的props解构,比如:
然后在FormElement内部解构所需属性,代码更简洁<FormElement key={componentId} elementData={elementData} autoAdvance={shouldAutoAdvance(elementData, visiblePageComponents)} currentPage={currentPage} iFrameId={iFrameId} isVisible={visiblePageComponents.includes(elementData)} slideDirection={slideDirection} />
内容的提问来源于stack exchange,提问作者Jeremy
相关产品推荐
相关产品推荐

