import { useState, useEffect } from 'react'; /** * Hook to animate a button through css when the user clicks on it. the button should have a class * whenes the boolean returned by the hook is true, and remove that class when it is false. * @param {function} onClick - Function to be executed when the button is clicked, at the same time * as it animates * @param {number} duration - Time (in milliseconds) that the click will stay active. * Defaults to 500 milliseconds. * @returns {Array} - array containing boolean to determine when the button was * just clicked and function to trigger the click action. * * @category Hooks * */ const useButtonFeedbackAnimation = ( onClick?: (e: React.MouseEvent) => void, duration = 500, ): [boolean, (e: React.MouseEvent) => void] => { const [click, setClick] = useState(false); const [timeOut, setTimer] = useState(null); const [initialTimeout, setInitialTimer] = useState(null); /** * When we mount the component we clear the timer and set a new timeout to remove the class * after the duration specified in `duration`. * When the component unmounts we clear both timeouts stored in state, so we don't get an error * trying to perform a state update on an unmounted component. */ useEffect(() => { clearTimeout(timeOut); const effTimeout = window.setTimeout(() => { setClick(false); }, duration); setTimer(effTimeout); return (): void => { clearTimeout(effTimeout); clearTimeout(timeOut); clearTimeout(initialTimeout); }; }, [click]); /** * When we click the button we set a timer on 100 milliseconds to set the click variable to true. * This way, if the user clicks a lot of times in a row, only the last click triggers the animation. */ const doClickAction = ( e: React.MouseEvent, ): void => { clearTimeout(initialTimeout); if (onClick) onClick(e); setInitialTimer( window.setTimeout(() => { setClick(true); }, 100), ); }; return [click, doClickAction]; }; export default useButtonFeedbackAnimation;