javascriptcssreactjsfrontendreact-transition-group

react-transition-group does not animate


I use React and tranct-transition-group to write carousel components

But I encountered the problem that the animation does not take effect. The code is as follows

Link https://stackblitz.com/edit/react-ts-mi8mwj?file=Carousel.tsx

Carousel.tsx

import React, { FC, Fragment, ReactNode, useMemo, useState } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import CarouselItem, { ItemProps } from './CarouselItem';
import './Carousel.scss';

export interface Props {}

const Component: FC<Props> = (props) => {
  const { children } = props;
  const [curIndex, setCurIndex] = useState(1);
  const length = useMemo(() => {
    return Array.from(children as ReactNode[]).length;
  }, [children]);
  const onNext = () => {
    setCurIndex((curIndex + 1 + length) % length);
  };
  const onPrev = () => {
    setCurIndex((curIndex - 1 + length) % length);
  };
  return (
    <Fragment>
      <button onClick={onPrev}>prev</button>
      <button onClick={onNext}>next</button>
      <div className="g-carousel-wrapper">
        <div className="g-carousel-window">
          <TransitionGroup className="item">
            {React.Children.map(children, (child, index) => {
              const childElement = child as FC<ItemProps>;
              if(child.type !== CarouselItem) throw new Error('必须是Item')
              return (
                <CSSTransition classNames="item" timeout={300} key={index}>
                  {React.cloneElement(childElement, {
                    index,
                    style: { display: curIndex !== index && 'none' },
                  })}
                </CSSTransition>
              );
            })}
          </TransitionGroup>
        </div>
      </div>
    </Fragment>
  );
};

type CarouselType = {
  Item: FC<ItemProps>;
} & FC<Props>;

const Carousel: CarouselType = Component as CarouselType;

Carousel.Item = CarouselItem;

export default Carousel;

CarouselItem.tsx

import React, { CSSProperties, FC } from 'react';

export interface ItemProps {
  index?: number;
  style?: CSSProperties;
}

const carouselItem: FC<ItemProps> = (props) => {
  const { children, style } = props;
  return (
    <div className="g-carousel-item" style={style}>
      {children}
    </div>
  );
};

export default carouselItem;

I don't understand why not only there is no animation effect but also the className of CSSTransition does not exist, it seems that react-transition-group does not take effect thanks


Solution

  • I think we don't need to use the TransitionGroup component. CSSTransition itself supports a in prop, we can use this prop to control it's visibility.

    So first, Add the in condition to the CSSTransition:

    <CSSTransition
      in={curIndex === index}
      classNames="item"
      timeout={300}
      key={index}
    >
    

    And then, just remove the TransitionGroup:

    <div className="g-carousel-wrapper">
      <div className="g-carousel-window">
        {React.Children.map(children, (child, index) => {
          const childElement = child as FC<ItemProps>;
          if (child.type !== CarouselItem) throw new Error('必须是Item');
          return (
            <CSSTransition
              in={curIndex === index}
              classNames="item"
              timeout={300}
              key={index}
            >
              {React.cloneElement(childElement, {
                index,
                style: { display: curIndex !== index && 'none' },
              })}
            </CSSTransition>
          );
        })}
      </div>
    </div>
    

    It should be working now: https://stackblitz.com/edit/react-ts-kqed2n?file=Carousel.tsx