React状态变更时CSS Transition失效问题求助
React手风琴关闭时无过渡效果的问题排查与修复
问题描述
实现了React手风琴组件,点击时预期内容通过过渡效果显示/隐藏,但仅展开时有过渡,关闭时无平滑过渡。
代码片段
JSX代码
import styles from './ChapterList.module.scss'; import {useEffect, useState} from "react"; import {AccordionCloseIcon, AccordionShowIcon} from "../../../components/icons/Accordion"; const dummyChapterContents = [{ id: 'c1', type: 'লেকচার ক্লাস ০১', title: 'পৌণঃপুনিক থেকে সাধারণ ভগ্নাংশে রূপান্তর থেকে', data: '1 Feb 2023 ' }, {id: 'c2', title: 'Lorem ipsum'}, {id: 'c3', title: 'Lorem ipsum'}] const ChapterList = ({chapters, onSelectChapterContent}) => { const [activeChapterId, setActiveChapterId] = useState(null); const [activeChapterContentId, setActiveChapterContentId] = useState(null); const [activeChapterContents, setActiveChapterContents] = useState([]); const handleChapterClick = (id) => { setActiveChapterId(activeChapterId === id ? null : id); }; const handleChapterContentClick = (info) => { setActiveChapterContentId(info?.id); onSelectChapterContent(info) }; // chapter contents related logic go here const renderContent = () => { // API logics [loading, fill/empty, error] to be written if (!activeChapterContents.length) { return null; } return ( <ul> {activeChapterContents.map((item) => ( <li key={`${activeChapterId}-${item?.id}`} className={`${styles.chapter_content__item} ${ activeChapterContentId === item?.id && styles.chapter_content__item__active }`} onClick={() => handleChapterContentClick(item)} > {item?.title} </li> ))} </ul> ); } useEffect(() => { if (activeChapterId) { // fetch chapter contents from API setActiveChapterContents(dummyChapterContents) } }, [activeChapterId]) return ( <div className={`col-span-full md:col-span-5 ${styles.chapter_list}`}> {chapters.map((chapter) => ( <div key={chapter.id} className={`${styles.chapter_item} ${activeChapterId === chapter.id ? styles.active : ''}`} > <div className={styles.chapter_meta_info} onClick={() => handleChapterClick(chapter.id)}> <div> <div className={styles.chapter_title}>{chapter.title}</div> </div> <div className={styles.chapter_icon}> { activeChapterId === chapter.id ? <AccordionShowIcon/> : <AccordionCloseIcon/> } </div> </div> <div className={styles.chapter_content}> {activeChapterId === chapter?.id && renderContent()} </div> </div> ))} </div> ); }; export default ChapterList;
SCSS代码
.chapter_list { width: 100%; display: flex; flex-direction: column; row-gap: 12px; @media (min-width: 768px) { row-gap: 16px; } } .chapter_item { background-color: #FFFFFF; border: 1px solid #D0DEEF; border-radius: 8px; padding: 16px; @media (min-width: 768px) { border-radius: 10px; padding: 20px 12px; } .chapter_meta_info { cursor: pointer; display: flex; justify-content: space-between; align-items: center; .chapter_title { font-family: 'Baloo Da 2'; font-style: normal; font-weight: 500; font-size: 16px; line-height: 27px; color: #343B6D; @media (min-width: 768px) { font-weight: 600; } } .chapter_icon { font-size: 1.2rem; transition: transform 0.5s ease-in-out; } } .chapter_content { max-height: 0; overflow: auto; transition: max-height 0.5s ease-in-out; padding: 0 10px; &__item { list-style: none; margin: 0; padding: 0.5rem 0; cursor: pointer; font-size: 1rem; font-weight: bold; color: #333; border-bottom: 1px solid #ddd; transition: all 0.5s ease-in-out; &:hover { background-color: #f6f6f6; } &__active { background-color: #f6f6f6; color: #0070f3; } @media (min-width: 768px) { font-size: 1.2rem; padding: 1rem 0; } } } &.active { .chapter_meta_info { .chapter_title { } .chapter_icon { transform: rotate(360deg); } } .chapter_content { max-height: 600px; } } }
问题原因
关闭手风琴时,activeChapterId !== chapter.id会触发条件渲染,直接移除.chapter_content内的内容节点。此时.chapter_content的内容为空,高度瞬间变为0,CSS的max-height过渡没有可计算的高度变化过程,导致过渡失效。
修复方案
1. 修改JSX的条件渲染逻辑
将.chapter_content内的条件渲染移除,始终保留内容节点,依赖CSS的max-height控制显示隐藏:
// 原代码 <div className={styles.chapter_content}> {activeChapterId === chapter?.id && renderContent()} </div> // 修改后 <div className={styles.chapter_content}> {renderContent()} </div>
2. 优化状态逻辑(可选)
如果需要在关闭手风琴时清空内容,可以调整useEffect,确保过渡完成后再清空:
useEffect(() => { if (activeChapterId) { setActiveChapterContents(dummyChapterContents); } else { // 延迟清空,等待过渡完成 const timer = setTimeout(() => { setActiveChapterContents([]); }, 500); // 与transition时长一致 return () => clearTimeout(timer); } }, [activeChapterId]);
3. 调整SCSS(可选)
如果关闭时滚动条闪烁影响体验,可以给.chapter_content添加过渡时的overflow控制:
.chapter_content { max-height: 0; overflow: hidden; /* 默认隐藏滚动条 */ transition: max-height 0.5s ease-in-out; padding: 0 10px; /* 展开时显示滚动条 */ .chapter_item.active & { overflow: auto; } // ... 其他样式 }
原理说明
修改后,关闭手风琴时,内容节点不会被立即移除,.chapter_content会从max-height:600px平滑过渡到max-height:0,同时overflow:auto/hidden会裁剪内容,实现关闭时的平滑过渡效果。
内容的提问来源于stack exchange,提问作者Rashed Rahat
相关产品推荐
相关产品推荐

