Search code examples
reactjsreact-hookssettimeoutrequestanimationframecleartimeout

How to replace a simple SetTimeout and ClearTimeout with requestAnimationFrame in React?


Here is my code block using setTimeout and clearTimeout which I want to replace with requestAnimationFrame -

const MyComponent = () => {
  const [copyText, setCopyText] = useState("Copy");
  const copyTextHandler = () => {
    setCopyText("Copied");
    const timer = setTimeout(() => {
      setCopyText("copy");
    }, 3000);
    return () => {
      clearTimeout(timer);
    };
  };
  
  return (<button onClick={copyTextHandler} disabled={copyTextHandler === "Copied"}>{copyText}</button>)
}

I tried searching for a few solutions in stack overflow and other platforms but couldn't find a fix for it.


Solution

  • The short answer is "you shouldn't, because setTimeout is the way to do this".

    However, if you absolutely have to use requestAnimationFrame then you can use it to repeatedly check if it's time to change the text.

    You could do this by passing a time at which the text should change back:

      const changeTextChecker = (time) => {
        if (Date.now() > time) {
          setCopyText("copy");
        } else {
          requestAnimationFrame(() => changeTextChecker(time));
        }
      };
    

    Sandbox

    But I prefer to set a timeout to set a flag that indicates that the time has passed, because this is more in keeping with the thinking behind this task:

      useEffect(() => {
        const changeTextChecker = () => {
          if (shouldChangeText.current) {
            setCopyText("copy");
            shouldChangeText.current = false; // reset for next time
          } else requestAnimationFrame(changeTextChecker);
        };
        requestAnimationFrame(changeTextChecker);
      }, []);
    

    Sandbox

    In both cases you are circumventing Javascript's asynchronous event queue by polling rather than just using the appropriate tool for the task. However, it does exactly answer your question.