Creating a custom hook for dual animation state 
Tags: react, javascript, typescript, webdev
Published: 2020-11-21 21:12:15

So recently I created a custom hook called `useAnimationState` to return me either of the two states - `AnimationState.ZERO` or `AnimationState.ONE` based on the transition times for both. Let's go through the thought process behind it. 

So consider a scenario where you have an animation like **fade in** and **fade out**.

Now you would like to perform **fade in** after `x` time and **fade out** after `y` time and they should repeat in these cycles.

This was our specific use case and the earlier code worked well for accommodating it. But I saw an opportunity to make this behavior reusable. 

So the fancy code looks like this :-

``` react 
function useAnimationState (defaultState: string, 
 zeroToOneTransitionTime: number, 
 oneToZeroTransitionTime: number) {

  const [animationState, setAnimationState] = useState(defaultState);

  useEffect(() => {

    let stateOneTimer: ReturnType<typeof setTimeout>;
    let stateZeroTimer: ReturnType<typeof setTimeout>;

    if (animationState === AnimationState.ONE) {
      stateZeroTimer = setTimeout(() => {
        setAnimationState(AnimationState.ZERO);
      }, oneToZeroTransitionTime);
    }

    else {
      stateOneTimer = setTimeout(() => {
        setAnimationState(AnimationState.ONE);
      }, zeroToOneTransitionTime);
    }

    return () => {
      if (stateOneTimer) clearTimeout(stateOneTimer);
      if (stateZeroTimer) clearTimeout(stateZeroTimer);
    };

  }, [animationState, oneToZeroTransitionTime, zeroToOneTransitionTime]);
  return animationState;
}
```
One may ask, that's all cool but the heck is happening here ?

Before that, let's get one thing clear i.e. what is `AnimationState` ? 

Well that's just my way of making things more verbose. 

Let's create an object called `AnimationState` like so :-

``` js
const AnimationState = {
   ONE:"1",
   ZERO:"0"
}
```

Note - Going forward I will mostly be talking in terms of 0 and 1 since that is not so verbose.

Now back to what's happening inside the hook :-

1) `useAnimationState` takes 3 parameters - `defaultState` (either of `AnimationState.ZERO` or `AnimationState.ONE`) , `zeroToOneTransitionTime` and `oneToZeroTransitionTime` (time taken to go from 0 to 1 and vice-versa). 

2) We have a `animationState` with initial value of `defaultState` with it's respective setter. This is the state which our custom hook will return. 

3) We have an useEffect inside which we are maintaining two timeouts. Simply put,
- if the `animationState` is 1, we will run the callback inside timeout which sets the `animationState` to 0 after `oneToZeroTransitionTime` 
- else we will run the callback inside timeout which sets the `animationState` to 1 after `zeroToOneTransitionTime` 

4) Then the obvious cleanup function being returned from useEffect to prevent memory leak by clearing out the set timers.

**Notice that `animationState` is a dependency in `useEffect's` dependency array and is the reason why we are able to execute everything beautifully.**

So once you get the `animationState`, how do you plan to use it ?

Well here is our partial use case :-

``` js
const HelloFadeClass = {
  [AnimationState.ONE]: 'HomeHero__hello-world--fade-in',
  [AnimationState.ZERO]: 'HomeHero__hello-world--fade-out',
};
```

And inside any component that has makes use of this fade animation, you can have following for an element, say - `span` :-

``` jsx

const animationState = useAnimationState(AnimationState.ZERO,1000,4000);

<span className={HelloFadeClass[animationState])}>{helloText}</span>
```

Now you may think, why bother creating all this when you can have everything achieved using **CSS animations** hacks ?

To a certain extent yes but stuff like transition time should be programmable and an `animationState` as a result of that transition can cover multiple use cases. 

Our full use case was to shuffle a word at fixed intervals and display on the Home page with **fade in, fade out** animations. Shuffling seems easy but when to do that ? 

That right there is where `animationState` comes in !!

We only shuffle when the `animationState` transitions from 1 to 0 so that when it is 1 again, the shuffled text is visible via a **fade in**.

Related code:-

``` react
  const animationState = useAnimationState(AnimationState.ONE, 1000, 4000);
  const shouldShuffle = useRef(false);
  const helloText = useShuffle(defaultHelloWorld, HelloWorld, HelloWorldKeys, shouldShuffle.current);

  useEffect(() => {
    shouldShuffle.current = animationState === AnimationState.ZERO;
  }, [animationState]);
```

Do give this a <3 if you found this useful !

Checkout the full usage of hook here :-
https://github.com/thenewboston-developers/Website/blob/development/src/containers/Home/HomeHero/index.tsx