Some animations require elements to be created and destroyed after a certain amount of time. For example - confetti particles, fireworks, or toast notifications.
These elements are perishable since they have a predefined lifespan after which they perish. Their only purpose is to be animated and then removed from the DOM.
This hook allows you to create perishable elements that will be removed after a certain amount of time, and optionally associate data to them.
#Implementation
The basic idea is to create an array of unique IDs (and, optionally, some associated data) that you can iterate over to create any element you want.
Based on a given delay
, the array will be updated and elements whose lifespan has expired will be removed.
Finally, we can use the useEffect()
hook to cleanup any pending timeouts when the component unmounts.
const usePerishable = () => {
const [items, setItems] = useState([]);
const timeouts = useRef({});
useEffect(() => {
// Cleanup pending timeouts when the component unmounts
return () => Object.values(timeouts.current).forEach(clearTimeout);
}, []);
return {items, add: (delay, data) => {
// Generate a unique ID
const id = nanoid();
// Create a new item with the given id & data
setItems(arr => [...arr, { id, data }]);
// Create a timeout to remove the item after the given delay
timeouts.current[id] = setTimeout(() => {
setItems(ids => ids.filter(item => item.id !== id));
delete timeouts.current[id];
}, delay);
}};
};
#Usage
You call add(delay, data)
to create an item, and iterate over items
to render the elements:
const Component = () => {
const { items, add } = usePerishable();
return (
<>
<button onClick={() => add(1000, 'some data')}>Create Perishable Item</button>
{items.map(({ id, data }) => (
<div key={id}>{data}</div>
))}
</>
)
}
#Examples
Here's a simple confetti button that creates a new confetti particle every time it's clicked.
Clicking on the button calls the add()
function PARTICLE_COUNT
times with some random values.
These values are then converted to CSS custom properties using objectToCustomProps()
and are used
to animated the elements.
Or how about this <ShineyButton/>
that just draws your attention to it?
You can also make cool (and useless) stuff like this smokin' cursor trail
I've also used this hook to create the like button for this site. I'm using it for creating the heart-shaped "waves" and the number bubbles. You can see the code in this CodePen: