2

I have a function, which dynamically changes objects on page with CSS animations.

I call this function from an onclick and touchend event.

I want to prevent this function from running/calling if animation is still in the process.

The strange thing is that in the code below function changeItem is called even if isLoading from console.log(isLoading) equals false.

  const [isLoading, setIsLoading] = useState(false);

  const changeItemHandler = (mod, index) => {
    console.log(isLoading);
    if (isLoading === true) return;
    else {
      setIsLoading(true);
      changeItem(mod, index);     //at the end of animation setIsLoading(false) is called
    }
  };

I have tried Julien Ripet answer, and ran into the same problems. Now with that code:

const [animAttrs, setAnimAttrs] = useState({isLoading: false, mod: null, index: null});
  const changeItemHandler = (mod, index) => {
    if (animAttrs.isLoading === true) return;
    else setAnimAttrs({ isLoading: true, mod: mod, index: index });
  };

  useEffect(() => {
    if (animAttrs.mod == null) return;  
    //needed to not call this function only after click or swipe
    else changeItem(animAttrs.mod, animAttrs.index);  
    //onanimationend i call setAnimAttrs({isLoading:false,mod:null,index:null});
  }, [animAttrs]);

It also calls changeItem function even if animAttrs.isLoading === true. I just can't explain this behavior.

dac0n
  • 29
  • 1
  • 7
  • Potentially useful related question: [How do you detect when CSS animations start and end with JavaScript?](https://stackoverflow.com/questions/14796936/how-do-you-detect-when-css-animations-start-and-end-with-javascript) – DBS Mar 31 '21 at 09:54
  • I need to know if animation is going. If i only allow function to run at the end of animation, i wouldnt be able to run it initially. And onStart happens almost at the same time the button is clicked, so also not useful. I also don't know how can i use animationiteration event since probably if i'll just check if it exist i'll not get into the moment interation actually fires. Thanks for trying anyways! – dac0n Mar 31 '21 at 09:57
  • I think your component rerender after applying the animation. – Jakir Hossen Mar 31 '21 at 09:57
  • It does rerender after animation end. But how does that help? – dac0n Mar 31 '21 at 10:01

1 Answers1

0

The useState hook is asynchronous,so your state isn't updated when the function is called again.

You could maybe move your logic to a useEffect hook watching the isLoading state ?

See this question for more info

Julien Ripet
  • 430
  • 3
  • 12
  • I would love to do that, but to call a function changeItem i need arguments "mod" and "index" which i get when in example onClick={() => changeItemHandler(0, i)} happens. Since i can't pass these arguments to useEffect (because i can't actually call useEffect) its impossible. Probably i can make an objects which contains all: "isLoading", "mod", and "index" attributes, but since it'll be asynchronous i'll run into the same problems. – dac0n Mar 31 '21 at 10:07
  • @dac0n I think your idea of making an object containing all 3 could work. When your user clicks/touches whatever you wanted, you update one state, with all the needed values, and when the `useEffect` gets called, you should have all 3 available. I can't test to guaranty it'd work, but maybe. Another solution could be using lodash's Throttle function https://lodash.com/docs/4.17.15#throttle – Julien Ripet Mar 31 '21 at 10:26