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

React与Material UI列表切换时Fade动画无法重复触发的修复咨询

解决Material UI Fade动画仅触发一次的问题

我开发了一个列表组件,点击列表中的兄弟项时,会切换显示带不同文本的元素并触发Material UI的Fade淡入动画。当前问题是动画仅能触发一次,后续点击兄弟项时动画不再生效。以下是我的代码,请告知如何重置控制Fade的selectedElemList状态,使点击兄弟项时能重复触发动画。

import { Fade, Grid, List, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
import { useEffect, useState, forwardRef } from "react";
import { Outlet, Link } from "react-router-dom";
import {Box} from "@mui/material"
import InBoxIcon from "@mui/icons-material/Inbox";
import DraftsRounded from "@mui/icons-material/Drafts";

const MainContent = () => {

    const divs = {
        0: {
            text: "hello",
            pr: "this is pr"
        },
        1: {
            text:"second",
            pr:"this is second pr"
        }
    }

    const [selectedItemList, setselectedItemList] = useState();
    const [selectedDivList, setselectedDivList] = useState();
    const [selectedElemList, setselectedElemList] = useState(false);

    const handleSelectedList = ( e, index) => {
        if(selectedItemList === index){ setselectedItemList() }else{setselectedItemList(index)}
        setselectedDivList(divs[index]);
        setselectedElemList((e) => !e);
    }

    const Compc = forwardRef((props, ref) => {
    return(
        <Box ref={ref} {...props}>
             <Typography variant="h4" sx={{ textAlign: 'center' }}> {props.text} </Typography>
             <Typography variant="h6" sx={{ textAlign: 'center' }}> {props.pr} </Typography>
        </Box>
    )
})

  return (
    <>
        <Grid container direction="row" pt={10} pl={1} pr={1} pb={1}>
            <Grid item xs={12}>
                <Typography variant="h2" sx={{textAlign:'center'}}> TITLE </Typography>
            </Grid>
            <Grid item xs={6}>
                <List aria-label="main list element">
                    
                    <ListItemButton selected={selectedItemList === 0} onClick={ (e) => handleSelectedList(e, 0)}>
                        <ListItemIcon>
                            <InBoxIcon />
                        </ListItemIcon>
                        <ListItemText primary="Inbox"/>
                    </ListItemButton>
                    
                    <ListItemButton selected={selectedItemList === 1}  onClick={ (e) => handleSelectedList(e, 1)}>
                        <ListItemIcon>
                            <DraftsRounded />
                        </ListItemIcon>
                        <ListItemText primary="Drafts"/>
                    </ListItemButton>
                </List>
            </Grid>
            <Grid item xs={6}>
                    { 
                        selectedDivList ?
                        <Fade in={selectedElemList} timeout={600}> 
                            <Compc  {...selectedDivList} />
                        </Fade>
                        : ''
                    }
            </Grid>
        </Grid>
    </>
  )
};

export default MainContent;

解决方案

问题出在selectedElemList的切换逻辑上:单纯取反操作无法保证每次切换列表项时,Fade组件能完整执行"隐藏→显示"的动画流程。连续点击不同列表项时,状态切换太快,组件来不及响应隐藏动作,导致动画不触发。

我们需要通过先隐藏再显示的逻辑重置动画,具体修改如下:

import { Fade, Grid, List, ListItemButton, ListItemIcon, ListItemText, Typography } from "@mui/material";
import { useEffect, useState, forwardRef } from "react";
import { Outlet, Link } from "react-router-dom";
import {Box} from "@mui/material"
import InBoxIcon from "@mui/icons-material/Inbox";
import DraftsRounded from "@mui/icons-material/Drafts";

const MainContent = () => {

    const divs = {
        0: {
            text: "hello",
            pr: "this is pr"
        },
        1: {
            text:"second",
            pr:"this is second pr"
        }
    }

    const [selectedItemList, setselectedItemList] = useState();
    const [selectedDivList, setselectedDivList] = useState();
    const [selectedElemList, setselectedElemList] = useState(false);

    const handleSelectedList = ( e, index) => {
        // 点击当前选中项时,取消选中并隐藏元素
        if(selectedItemList === index){ 
            setselectedItemList(null);
            setselectedDivList(null);
            setselectedElemList(false);
            return;
        }
        
        // 先隐藏元素,等待组件状态更新后再显示,触发动画
        setselectedElemList(false);
        setTimeout(() => {
            setselectedItemList(index);
            setselectedDivList(divs[index]);
            setselectedElemList(true);
        }, 50);
    }

    const Compc = forwardRef((props, ref) => {
    return(
        <Box ref={ref} {...props}>
             <Typography variant="h4" sx={{ textAlign: 'center' }}> {props.text} </Typography>
             <Typography variant="h6" sx={{ textAlign: 'center' }}> {props.pr} </Typography>
        </Box>
    )
})

  return (
    <>
        <Grid container direction="row" pt={10} pl={1} pr={1} pb={1}>
            <Grid item xs={12}>
                <Typography variant="h2" sx={{textAlign:'center'}}> TITLE </Typography>
            </Grid>
            <Grid item xs={6}>
                <List aria-label="main list element">
                    
                    <ListItemButton selected={selectedItemList === 0} onClick={ (e) => handleSelectedList(e, 0)}>
                        <ListItemIcon>
                            <InBoxIcon />
                        </ListItemIcon>
                        <ListItemText primary="Inbox"/>
                    </ListItemButton>
                    
                    <ListItemButton selected={selectedItemList === 1}  onClick={ (e) => handleSelectedList(e, 1)}>
                        <ListItemIcon>
                            <DraftsRounded />
                        </ListItemIcon>
                        <ListItemText primary="Drafts"/>
                    </ListItemButton>
                </List>
            </Grid>
            <Grid item xs={6}>
                    { 
                        selectedDivList ?
                        <Fade in={selectedElemList} timeout={600}> 
                            <Compc  {...selectedDivList} />
                        </Fade>
                        : ''
                    }
            </Grid>
        </Grid>
    </>
  )
};

export default MainContent;

关键修改点:

  • 点击新列表项时,先将selectedElemList设为false,让Fade组件进入隐藏状态
  • 通过setTimeout延迟50ms再更新选中项、内容并将selectedElemList设为true,确保组件有足够时间响应状态变化,触发完整淡入动画
  • 优化了当前选中项的取消逻辑,状态切换更清晰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 16:22:13