You can view a video tutorial running through this above, and I've provided a Github repository with all code in the example
I was recently building a React application in which users could add or remove inline tags. I found it a bit jarring that clicking the add button caused a brand new tag to blast onto the screen immediately, and clicking to remove the tag caused the tag to immediately vanish in a fwoosh. As someone who spends much of his time thinking about great experiences, the jarring nature had me reaching for a softer, CSS animation solution.
Fading in elements is pretty easy using CSS 3 animations, so I implemented those first, but was immediately at a loss for how to elegantly handle the fade out for each component, which would require adding some set of “transition-out” css classes programatically as the element was removed. This would require yet another solution for the cradle to grave life of these tags to achieve the effect I wanted.
In comes React Transition Group, which seamlessly allowed me to implement transition effects for components as they entered and exited. It’s a technology I enjoyed using so much I thought I’d document my experience with it for others to build on and as a quick guide for myself.
My Test Application
I built a quick little test application to show the effect, which you can see in this Github Repository. The point was to show a very simple set of objects that transitioned in and out when you tap on them and I used three primary technologies.
- React Transition Group - the big kahuna of the tutorial, I utilized its two components [.code_snippet]<TransitionGroup>[.code_snippet] and [.code_snippet]<CSSTransition>[.code_snippet] to apply CSS classes on demand to handle my fade in and out effects I desired.
- Styled Components - Rather than having a separate style sheet, Styled Components allows me to edit my CSS right next to the JSX I use to build my component, making component styling easier to compartmentalize and follow.
- React Hooks - Well known at this point, I used the [.code_snippet]useState[.code_snippet] hook to manage the state of my application from the top of the Component tree in [.code_snippet]App.js[.code_snippet]
I’ll mainly focus on the file [.code_snippet]TileGroup.js[.code_snippet] as this holds all the React Transition Group logic, but the above libraries were used to develop the end-to-end example you see.
The App
The App has a few components that make it up, but most relevant to understanding Transition Group is the nested components of [.code_snippet]App.js[.code_snippet] > [.code_snippet]TileGroup.js[.code_snippet] > [.code_snippet]Tile.js[.code_snippet]. I wired the application so that whenever the [.code_snippet]AddButton[.code_snippet] component is pressed, it updates the state of the application, which is then passed to TileGroup, which then creates a Tile for each object stored in the state.
For this reason, outside of the Styled Components to make it look pretty and the addItem and removeItem functions to manage state, the [.code_snippet]App.js[.code_snippet] is super simple.
The TileGroup
For the sake of example, the above snippet is what the TileGroup would look like without using any Transition Group magic. It’s simply a functional component which takes in the list of items as props, and then iterates over those items to create a unique Tile component for each item in the state. But _no_ transition in and transition out animation would be included, so every time a Tile was added or removed it would simply flash off the screen.
Below is what the finished product looks like using Transition Group. Note the [.code_snippet]TileGroup[.code_snippet] component has the exact same structure with the exception of adding in [.code_snippet]<TransitionGroup[.code_snippet] and [.code_snippet]<CSSTransition>[.code_snippet] which we will unpack below. I refactored this base code to include Transition Group.
Take notice of a few nuances
- The [.code_snippet]TransitionGroup[.code_snippet] component becomes a wrapper for the whole experience. Previously I used a styled component [.code_snippet]StyledGrid[.code_snippet] as the wrapper. which applied all the grid styling I wanted for the container. If you do not specify a [.code_snippet]component[.code_snippet] prop for the [.code_snippet]TransitionGroup[.code_snippet], Transition Group will simply create a new div that holds all the functionality. If you do pass in a component, the specified component will assume all the functionality of the Transition Group. Thus, I passed it my original `StyledGrid` component as to not have to rejig my CSS to account for this extra div in the DOM.
- Each [.code_snippet]Tile[.code_snippet] component is now wrapped in a new, critical component, the `CSSTransition` component. This must be a direct child of the `TransitionGroup` component with no intermediate ancestors in between the two, or else you begin to get some weird behavior.
- The [.code_snippet]CSSTransition[.code_snippet] component carries a few argument. `key` is standard to include when mapping over elements in React. The `timeout` specifies how long it is going to apply the CSS classes necessary to facilitate the transition in milliseconds, so in this case I want my transition to last for about a third of a second. The [.code_snippet]classNames[.code_snippet] prop corresponds to what set of CSS classes the component will attach to achieve the transition effect. **This is very important to set and have correspond to your CSS**.
What does this look like? I used the [.code_snippet]classNames=transition[.code_snippet] and above in my styled component, I defined those css classes to look like the below snippet. Note each class begins with [.code_snippet]transition-[.code_snippet] because of the settings within the [.code_snippet]CSSTransition[.code_snippet] Component.
Now, because of [.code_snippet]TransitionGroup[.code_snippet] orchestrating the elements coming in and out, and [.code_snippet]CSSTransition[.code_snippet] applying classes to each individual Tile as it gets added, you will notice in your DOM via Dev Tools that [.code_snippet]transition-enter[.code_snippet] and [.code_snippet]transition-enter-active[.code_snippet] get applied over that 300 millisecond time window, and same with the [.code_snippet]-exit[.code_snippet] and [.code_snippet]-exit-active[.code_snippet] classes. _The orchestration between the component and these CSS classes is what achieves the desired effect_.
Conclusion
It takes a little getting used to, but once you understand the basics, I found it to be a delightful way to ensure my components got the animation treatment I desired. Again if you want to follow along more closely; take a look at the Github Repository or watch the video which goes a little more in depth on the solution.