loading...
Cover image for Creating a custom hook for dual animation state

Creating a custom hook for dual animation state

lapstjup profile image Lakshya Thakur ・3 min read

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 :-

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;
}
Enter fullscreen mode Exit fullscreen mode

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 :-

const AnimationState = {
   ONE:"1",
   ZERO:"0"
}
Enter fullscreen mode Exit fullscreen mode

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 :-

const HelloFadeClass = {
  [AnimationState.ONE]: 'HomeHero__hello-world--fade-in',
  [AnimationState.ZERO]: 'HomeHero__hello-world--fade-out',
};
Enter fullscreen mode Exit fullscreen mode

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


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

<span className={HelloFadeClass[animationState])}>{helloText}</span>
Enter fullscreen mode Exit fullscreen mode

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:-

  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]);
Enter fullscreen mode Exit fullscreen mode

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

Discussion

pic
Editor guide