Creating a custom hook for dual animation state
Lakshya Thakur
Posted on November 21, 2020
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;
}
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"
}
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 theanimationState
to 0 afteroneToZeroTransitionTime
- else we will run the callback inside timeout which sets the
animationState
to 1 afterzeroToOneTransitionTime
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',
};
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>
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]);
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
Posted on November 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.