I love React Hooks. Code is cleaner, developers are happier, but for the longest time I wasn’t able to actually make GSAP Timelines work with them, so I defaulted to “Animated Components” as I like to call them to do the dirty work for me. Basically, I made a class based component that contained just the thing I was animating, but through the power of reading the docs and a bit of critical thinking, I’ve stumbled upon something revolutionary (as far as I’m concerned).
A brief explanation of hooks
Hooks are a way to manipulate data the way that a class based components lets you manipulate data. There are a few that you already know, like useState (which replaces setState), useEffect (which is basically all of the lifecycle methods in one) and useMemo (memoize a value that should be changed only if something changes).
Pretty simple right? Wrong. These are complex functions written by aliens that do our bidding as front end developers. Now you’re asking yourself: “Alek, what is all this nonsense and how will all this filler text make me a better programmer?”. I’m getting to that, don’t worry.
There’s a couple of things you need to know to continue. The useEffect hook basically acts like a componentDidMount if you pass an empty dependency array. If the array has values, it’s a componentDidUpdate. Or you can do both at the same time, doesn’t matter, it handles changes in the props/state. UseRef creates references to DOM elements. UseState is pretty self explanatory, it lets you use state in functional components.
GSAP and Timelines
Unless you’re living under a rock, you’ve used GSAP for animations on the web. It’s got a great and simple API and animations with it are a breeze, but using it in a functional component can be tricky. Why? Because there are no constants. Values are reset every time a function rerenders and that nullifies all of the animations and resets them back to square one.
Let’s try to code something that won’t ever work:
So why is nothing happening? In theory this should work, but it doesn’t. Constants aren’t actually constant and they get reinitialised whenever a component rerenders. This happens when it’s or it’s parent’s state is changed. That’s React and we love and hate it for that. How can we fix this? Well, we need to keep that original GSAP Timeline in memory for this to work. Whenever the component rerenders the timeline should stay the original value that it.
Luckily for us, it’s super simple. We have a hook that does exactly this. It’s the often overlooked useMemo hook. The useMemo hooks returns a computed value that changes only when something in the dependency array changes. If you pass in nothing in that dependency array it basically creates a static property of a class — it’s never going to change! So what we need to do is set the timeline to use a memoized value of that GSAP Timeline.
What’s going on here and why does it work? Because we’re using the useMemo hook and passing in an empty dependency array, the timeline value stays the same for the whole lifecycle of the component. It’s never going to change because the dependency array has nothing that will force it to change. If we didn’t pass it with the useMemo hook, it’s going to get reinitialised with an empty timeline every time the component rerenders.