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

MUI Slide过渡前渲染异常:如何延迟formElements渲染避免闪烁

解决Slide过渡时formElements闪烁的问题

核心问题分析

你的代码中formElements始终会被渲染到DOM中,即使Slide组件处于隐藏状态,只是通过CSS做视觉隐藏。当切换页面时,新的formElements会提前渲染,和旧内容重叠导致闪烁。要解决这个问题,需要延迟formElements的渲染,直到Slide过渡完全完成。

解决方案

利用Material-UI Slide组件的onEntered回调(过渡完成后触发)来控制内容的渲染时机,同时拆分状态管理过渡和内容可见性:

  1. 用两个状态分别控制Slide的显示(isSlideVisible)和内容的渲染(isContentReady)
  2. 页面切换时,先隐藏内容,再触发Slide的过渡动画
  3. 过渡完成后,再渲染新的表单元素

改进后的代码

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
      key={componentId}
      elementData={elementData}
      autoAdvance={shouldAutoAdvance(elementData, visiblePageComponents)}
      currentPage={currentPage}
      iFrameId={iFrameId}
      isVisible={visiblePageComponents.includes(elementData)}
      slideDirection={slideDirection}
    />
    
    然后在FormElement内部解构所需属性,代码更简洁

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:15:43