React Logoreact-transition-group

react-transition-group is a library that helps manage the states of components as they enter, exit, or are updated in the DOM, making it easier to apply CSS transitions and animations. It doesn't provide the animations themselves but offers a structured way to apply CSS classes at specific points in a component's lifecycle or trigger callbacks.

Key Components:

1. Transition: This is the base component. It exposes lifecycle hooks (callbacks like `onEnter`, `onEntering`, `onEntered`, `onExit`, `onExiting`, `onExited`) that you can use to apply CSS classes, manipulate styles directly, or trigger other animations. It gives you full control but requires more manual management of styles.
* `in`: A boolean prop that controls the state of the component (true for entering/entered, false for exiting/exited).
* `timeout`: Specifies the duration of the transition (number or an object for different phases).

2. CSSTransition: Built on top of `Transition`, this component simplifies CSS-based transitions by automatically applying a series of CSS class names at different stages of a component's entry or exit. This is the most commonly used component for simple CSS transitions.
* `classNames`: A string prefix (e.g., 'fade') that `CSSTransition` uses to generate specific classes like `fade-enter`, `fade-enter-active`, `fade-exit`, `fade-exit-active`, `fade-enter-done`, `fade-exit-done`.
* `unmountOnExit`: If true, the component unmounts after the exit transition completes.
* `appear`: If true, the component will apply its 'appear' transition styles when it mounts for the first time.

3. TransitionGroup: This component is designed to manage a group of `Transition` or `CSSTransition` components, particularly useful for animating lists where items are added or removed. It watches its children and triggers appropriate enter/exit transitions when children are added or removed from the DOM.
* It doesn't render any specific element itself by default; it needs a `component` prop (e.g., 'ul', 'div') if you want it to render a wrapper element.

How it works with CSS (CSSTransition example):
When `CSSTransition`'s `in` prop changes, it adds specific class names to its child element. For a `classNames='my-node'`, these classes would be:
* `my-node-enter`: Applied on the initial mount when `in` is true, and for `appear` transitions. Also added when `in` becomes true.
* `my-node-enter-active`: Applied after `my-node-enter` and during the active entering transition phase.
* `my-node-enter-done`: Applied after the `my-node-enter-active` phase, indicating the component has entered.
* `my-node-exit`: Applied when `in` becomes false.
* `my-node-exit-active`: Applied after `my-node-exit` and during the active exiting transition phase.
* `my-node-exit-done`: Applied after the `my-node-exit-active` phase, indicating the component has exited.

You then define CSS rules for these classes using `transition` properties to animate the changes.

Example Code

```javascript
import React, { useState } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import './App.css'; // Assuming you create App.css with the styles below

function App() {
  const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
  const [newItem, setNewItem] = useState('');

  const addItem = () => {
    if (newItem.trim() !== '') {
      setItems([...items, newItem.trim()]);
      setNewItem('');
    }
  };

  const removeItem = (indexToRemove) => {
    setItems(items.filter((_, index) => index !== indexToRemove));
  };

  return (
    <div className="App">
      <h1>React Transition Group Example</h1>
      <div className="input-section">
        <input
          type="text"
          value={newItem}
          onChange={(e) => setNewItem(e.target.value)}
          placeholder="Add a new item"
        />
        <button onClick={addItem}>Add Item</button>
      </div>
      
      <TransitionGroup component="ul" className="item-list">
        {items.map((item, index) => (
          <CSSTransition
            key={item + index} // A unique key is crucial for TransitionGroup
            timeout={500}
            classNames="item"
          >
            <li className="list-item">
              {item}
              <button onClick={() => removeItem(index)}>Remove</button>
            </li>
          </CSSTransition>
        ))}
      </TransitionGroup>
    </div>
  );
}

export default App;
```

App.css (to be placed in the same directory as App.js or imported):

```css
.App {
  font-family: sans-serif;
  text-align: center;
  padding: 20px;
}

.input-section {
  margin-bottom: 20px;
}

.input-section input {
  padding: 8px;
  margin-right: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.input-section button {
  padding: 8px 15px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.input-section button:hover {
  background-color: #0056b3;
}

.item-list {
  list-style: none;
  padding: 0;
  max-width: 400px;
  margin: 20px auto;
  border: 1px solid #eee;
  border-radius: 5px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.list-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 15px;
  border-bottom: 1px solid #eee;
  background-color: #f9f9f9;
}

.list-item:last-child {
  border-bottom: none;
}

.list-item button {
  background-color: #dc3545;
  color: white;
  border: none;
  padding: 5px 10px;
  border-radius: 3px;
  cursor: pointer;
}

.list-item button:hover {
  background-color: #c82333;
}

/* Transition styles for 'item' classNames */

/* Initial state for entering items */
.item-enter {
  opacity: 0;
  transform: translateY(-20px);
}

/* Active state for entering items */
.item-enter-active {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 500ms ease-in, transform 500ms ease-in;
}

/* Initial state for exiting items */
.item-exit {
  opacity: 1;
  transform: translateY(0);
}

/* Active state for exiting items */
.item-exit-active {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 500ms ease-out, transform 500ms ease-out;
}

/* Ensures exiting item collapses nicely after it disappears */
.item-exit-active.list-item {
  position: absolute;
  left: 0;
  right: 0;
  /* Adjust height for collapse effect if desired */
}

/* Optional: To make list items transition smoothly when others are removed */
.item-list > * {
  transition: all 500ms ease-in-out;
}
```