Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
122 views
in Technique[技术] by (71.8m points)

javascript - Is it possible to make a `useIsAnimating` hook in React?

So I have a feature in my React app where it would be incredibly useful to know if an element is animating/going through a keyframe.

I spent all day yesterday trying tons of iterations through something like the below and lots of other abstractions, but can't seem to get it to work. Any thoughts or is this just not possible?

function useIsAnimating(ref) {
  const [isAnimating, setIsAnimating] = React.useState(false);

  useEffect(() => {
    ref?.current.addEventListener('animationstart', setIsAnimating(true)); 
    ref?.current.addEventListener('animationend', setIsAnimating(false)); 
    
    return () => {      
    ref?.current.removeEventListener('animationstart', setIsAnimating(true)); 
    ref?.current.removeEventListener('animationend', setIsAnimating(false)); 
    };
  }, [ref, setIsAnimating]);

  return isAnimating;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Without going too much into the reasons why you'd want to do this, the code above has a simple problem.

The second argument expected by addEventListener is a function that is supposed to be called when a given event gets triggered. Your code doesn't do that but instead it does this (after expanding it into a more readable form):

const animationStartListener = setIsAnimating(true);
ref?.current.addEventListener('animationstart', animationStartListener);
const animationEndListener = setIsAnimating(false);
ref?.current.addEventListener('animationend', animationEndListener);

For your code to work correctly you'd have to wrap the call to setIsAnimating(true) in a function, and pass that function as an argument to the addEventListener:

const animationStartListener = () => setIsAnimating(true);
ref?.current.addEventListener('animationstart', animationStartListener);
const animationEndListener = () => setIsAnimating(false);
ref?.current.addEventListener('animationend', animationEndListener);

or, using shorter syntax:

ref?.current.addEventListener('animationstart', () => setIsAnimating(true));
ref?.current.addEventListener('animationend', () => setIsAnimating(false));

Another thing to remember is that when you're removing a listener, you have to pass the same reference to the function as was passed to the addEventListener. The correct way to do this in React using React Hooks is by the use of useCallback hook:

function useIsAnimating(ref) {
  const [isAnimating, setIsAnimating] = React.useState(false);
  const handleAnimationStart = useCallback(
    () => setIsAnimating(true),
    [setIsAnimating],
  );
  const handleAnimationEnd = useCallback(
    () => setIsAnimating(false),
    [setIsAnimating],
  );

  useEffect(() => {
    ref?.current.addEventListener('animationstart', handleAnimationStart); 
    ref?.current.addEventListener('animationend', handleAnimationEnd); 
    
    return () => {      
    ref?.current.removeEventListener('animationstart', handleAnimationStart); 
    ref?.current.removeEventListener('animationend', handleAnimationEnd); 
    };
  }, [ref, handleAnimationStart, handleAnimationEnd]);

  return isAnimating;
}

Hope that helps you with your problem (and I didn't make any spelling mistakes here).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...