Preview and View More Animation with React Transition Group
Tags: react, animation, reacttransitiongroup, css
Published: 2021-07-24 14:29:11

![Preview and View More animation diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzadnqnqedaf6fgoyli7.png)

Using React Transition Group for a Preview & View More kind of layout animation where the Preview & View More components are different.

At a high level, we can think of several ways of CSS animation we can attempt. In this blog, we will speak about `Preview` & `View More` kind of layout where the `Preview` & `View More` components are different.

Only prerequitite is that the approximate heights of the `Expanded content` & `Collapsed content` are known beforehand as we will be animating on the `max-height` property.

Now if we are to replace the `Expanded content` with `Collapsed content` during collapse phase, it happens abruptly with a jank. To resolve this, we should adopt the following animation strategy:

#### During expand phase

1. Replace the `Collapsed content` with `Expanded content`
2. Animate height to accomodate Expanded content

#### During collapse phase

1. Animate height to `Collapsed content` height. This is because the `Expanded content` is taller than the `Collapsed content` and can accomodate the height transition without abruptly shrinking

2. Replace the `Expanded content` with `Collapsed content` once animation is over

To achieve this, we will be using `CSSTransition` module of the `react-transition-group`

#### Let's say we have the following `Collapsed content` component

```js
const CollapsedContent = (props) => {
  return (
    <div className="collapsed">
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
      <p>
        Asperiores iste magnam dolores eius ab laboriosam excepturi, consectetur
        consequuntur maiores at voluptas ipsam cum reiciendis assumenda tenetur
        natus in omnis deleniti.
      </p>
    </div>
  );
};
```

#### And the following `Expanded content` component

```js
const ExpandedContent = (props) => {
  return (
    <div className="expanded">
      <p>
        It is a long established fact that a reader will be distracted by the
        readable content of a page when looking at its layout. The point of
        using Lorem Ipsum is that it has a more-or-less normal distribution of
        letters, as opposed to using 'Content here, content here', making it
        look like readable English.
      </p>
      <p>
        There are many variations of passages of Lorem Ipsum available, but the
        majority have suffered alteration in some form, by injected humour, or
        randomised words which don't look even slightly believable.
      </p>
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Asperiores iste
        magnam dolores eius ab laboriosam excepturi, consectetur consequuntur
        maiores at voluptas ipsam cum reiciendis assumenda tenetur natus in
        omnis deleniti.
      </p>
    </div>
  );
};
```

In the main or orchestrating component, we need to add 2 booleans:

1. isCollapsed (to track which component to render)
2. isAnimating (to track when to run animation)

So when the animation is starting, we will toggle `isCollapsed` to `false` and when animation has finished, we will toggle `isCollapsed` to `true`.

This way we will be able to animate based on the strategy mentioned above.

#### Main component

```js
export const Main = () => {
  const [isCollapsed, setIsCollapsed] = useState(true);
  const [isAnimating, setIsAnimating] = useState(false);

  return (
    <div>
      <div className="card">
        <CSSTransition
          in={isAnimating}
          timeout={200}
          classNames="content-switching"
          onEntering={() => {
            setIsCollapsed(false);
          }}
          onExited={() => {
            setIsCollapsed(true);
          }}
        >
          <div className="base">
            {isCollapsed && <CollapsedContent />}
            {!isCollapsed && <ExpandedContent />}
          </div>
        </CSSTransition>
      </div>
      <button
        onClick={() => setIsAnimating((prev) => !prev)}
        style={{ display: "block" }}
      >
        {isCollapsed ? "Show" : "Hide"}
      </button>
    </div>
  );
};
```

As we see here, the user clicking on the `Show` or `Hide` button only toggles the `isAnimating` flag.

The `CSSTransition` component then handles toggling the `isCollapsed` flag based on it's life cycle methods: `onEntering` and `onExited`.

Finished result:

<iframe src="https://react-height-animation-tryout.vercel.app/"
     style="width:100%; height:460px; border:0; border-radius: 4px; overflow:hidden;"
     title="admiring-tdd-hj7by"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
></iframe>

Full source code is available here: 
<a href="https://github.com/rohanBagchi/react-height-animation-tryout" rel="noopener">
    github.com/rohanBagchi/react-height-animation-tryout
</a>

Thank you for reading this far.