reactjsreact-transition-group

Run CSSTransition inside TransitionGroup when any value changes


I'm looking at the TransitionGroup documentation example, and I'm trying to update it to run the CSSTransition whenever the text value is updated for an existing item.

I have set up a codesandbox to show my current attempt.

const [items, setItems] = useState([
    { id: 1, text: 'Hello world!' },
    { id: 2, text: 'How are you?' },
]);

return (
    <Container style={{ marginTop: '2rem' }}>
        <ListGroup style={{ marginBottom: '1rem' }}>
            <TransitionGroup className="todo-list">
                {items.map(({ id, text }) => (
                    <CSSTransition
                        key={id}
                        timeout={500}
                        classNames="item">
                        <ListGroup.Item>{text}</ListGroup.Item>
                    </CSSTransition>
                ))}
            </TransitionGroup>
        </ListGroup>

        <Button
            onClick={() => {
                setItems((items) => [
                    { id: 1, text: 'Hello to you!' },
                    { id: 2, text: 'How do you do?' },
                ]);
            }}>
        Update Items
        </Button>
    </Container>

);

When I click the button, the text content updates without the CSSTransition. I know this is because of the id not changing, and this is what key is set to, but how do I have TransitionGroup take into account the text value?

How do I keep the same id but update the text value with a transition?


Solution

  • Since key={id}, the key won't change when the text is updated, and the CSSTransition won't trigger because it doesn't get rerendered.

    We can change the key to include the id and the text, so the component will always get rerendered when the text changes. We also want to add exit={false} to prevent us from seeing extra components while the new components are transitioning in. Here's what that should look like:

    <CSSTransition
      key={`${id}${text}`}
      timeout={500}
      classNames="item"
      exit={false}
    >
    

    Here's a codesandbox example of this solution.