Hey you! Yea you! Do you need a quick and simple fade animation on your React component? Don't feel like installing a library?
Then let's go! 💨💨💨
Show me the code already! 👀
The hook returns [isVisible, setVisible, fadeProps]
, just like useState()
hook, but you also need to set the fadeProps
on the element you want to fade.
const MyFadingComponent = () => {
// Just like useState() hook, the fadeProps go on the fading DOM element
const [isVisible, setVisible, fadeProps] = useFade();
// You can use isVisible to mount/unmount the component!
return <>
<button onClick={() => setVisible(!isVisible)}>Toggle visibility</button>
{isVisible && <h2 {...fadeProps}>Now you see me...</h2>}
</>;
};
And the hook! 🎣
It uses onAnimationEnd
to delay setting the isVisible
state to false
, which allows the animation to finish before the component unmounts!
const useFade = (initial) => {
const [show, setShow] = useState(initial);
const [isVisible, setVisible] = useState(show);
// Update visibility when show changes
useEffect(() => {
if (show) setVisible(true);
}, [show]);
// When the animation finishes, set visibility to false
const onAnimationEnd = () => {
if (!show) setVisible(false);
};
const style = { animation: `${show ? "fadeIn" : "fadeOut"} .3s` };
// These props go on the fading DOM element
const fadeProps = {
style,
onAnimationEnd
};
return [isVisible, setShow, fadeProps];
};
Styles 💅
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes fadeOut {
0% { opacity: 1; }
100% { opacity: 0; }
}
What's the point? 🙄
If we would use the useState()
hook and then apply the state with an expression like isVisible && <Component />
, our component will unmount before the CSS animation has finished, which is not what we want! The useFade()
hook delays unmounting until the animation is finished.
What's cool about this? 😎
The syntax is just like useState()
, you can simply use an isVisible && <Component />
expression to mount/unmount the component.
Here's how you do it with React Transition Group, you need a wrapper component and wire the enter/exit animation to the state yourself, yuck! Framer Motion and React Spring are similar.
Room for improvement (please help!)
Toggling between two elements does not really work at the moment:
const [isVisible, setVisible, fromProps, toProps] = useFade();
{isVisible ? <ComponentA {...fromProps} /> : <ComponentB {...toProps} />}
I'm trying to pass the opposite fade animation to ComponentB
, but can't figure out how to get it right. If you have an idea, let me know!
Top comments (1)
Pro tip:
keep the default opacity to 0 for the component you're trying to animate. Otherwise, you'll see a flash kind of effect on fadeOut.