Surely you want sometimes in react to animate an entrance and/or exit of unmounting component. Instead of using a library, there is a nice way to do it by yourself, just with material ui!
For this we will make use of the emotion css prop and the keyframes helper.
As of material ui we can just take the Box component
Our goal is to create an Animated component that can receive following props:
show
: if the component is mounted or not
mountData
: describing the entrance animation
mountData.keyframes
: Standard css animation keyframes
mountData.time
: Animation duration in seconds
mountData.type
: Css animation type (e.g. linear, ease-out...)
unmountData
: describing the exit animation
unmountData.keyframes
: Standard css animation keyframes
unmountData.time
: Animation duration in seconds
unmountData.type
: Css animation type (e.g. linear, ease-out...)
unmountTimeout
(optional): to provide a possibility for auto unmount the component after a timeout
setShow
(optional): function to unmount the component, provided by the parent
If you don't provide the last two, the parent component will control the whole mount/unmount process.
And here is the solution:
import { Box } from '@mui/material';
import { useEffect, useState } from 'react';
import { css, keyframes } from '@emotion/react';
const defaultMountData = {};
const Animated = ({
children,
show,
setShow,
mountData = defaultMountData,
unmountData = defaultMountData,
unmountTimeout,
...rest
}) => {
const [animationData, setAnimationData] = useState(null);
const { time, type = 'linear' } = animationData || {};
const animationCss = animationData?.keyframes ?
css`animation: ${keyframes`${animationData.keyframes}`} ${time}s ${type}`
: '';
useEffect(() => {
let mounted = true;
let handler = null;
let unmountHandler = null;
if (show) {
setAnimationData(mountData);
if (unmountTimeout && setShow) {
unmountHandler = setTimeout(() => mounted && setShow(false), unmountTimeout);
}
} else if (animationData) {
const { time: unmountTime } = unmountData;
handler = setTimeout(() => mounted && setAnimationData(null), unmountTime * 1000);
setAnimationData(unmountData);
}
return () => {
handler && clearTimeout(handler);
unmountHandler && clearTimeout(unmountHandler);
mounted = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mountData, unmountData, show]);
if (!animationData) return null;
return (
<Box
css={animationCss}
component="div"
{...rest}
>
{children}
</Box>
);
};
export default Animated;
We can not use default props for our default mount data, because it will cause all the time re-rendering.
Now in our component we place the mountData with the settings for entrance animation, the unmountData with settings for exit animation. As soon the show param become true
, that will activate our Animated component entrance animation. After 4 seconds the exit animation will be played, and will set the shouldBeMounted variable to false
, which will unmount the component:
const [shouldBeMounted, setShouldBeMounted] = useState(false);
<Animated
show={shouldBeMounted}
mountData={{
keyframes: `
0% {opacity: 0}
100% {opacity: 1}
`,
time: 0.3,
}}
unmountData={{
keyframes: `
0% {opacity: 1}
100% {opacity: 0}
`,
time: 0.8,
}}
unmountTimeout={4000}
setShow={setShouldBeMounted}
>
Text to hide with animation
</Animated>
If we don't want automated unmounting, we can just ignore the unmountTimeout and the setShow params. If we don't want entrance or exit animation, we can also just ignore the mountData/unmountData:
const [shouldBeMounted, setShouldBeMounted] = useState(false);
<Animated
show={shouldBeMounted}
unmountData={{
keyframes: `
0% {opacity: 1}
100% {opacity: 0}
`,
time: 0.8,
}}
>
Text to hide with animation
</Animated>
Here, we control totally our Animated component from the parent, and we don't use any animation for the mount, we use animation only for unmount.
Well that's it!
This is a simple, fast and lightweight way to create mount animations just using css.
Best Regards
Anton Tonchev
JUST-SELL.online
Top comments (0)