'How to make notification appear in 4 seconds, but avoid it if state has changed

I want to show notification message after 4 seconds when some state is invalid. But if during that 4 seconds it has changed and is valid now - I want to put condition in setTimeout that would check it. But the problem is that it still uses the first state value, not the changed one. One of my assumptions to fix it was making setState in the line before synchronous, but don't know how. Maybe any other ways to fix it?

useEffect(async () => {
      try {
        const snippetIndexResponse = await getSnippetIndex(
            //some params
        );
        if (snippetIndexResponse !== -1) {
          setSnippetIndex(snippetIndexResponse);
        } else {
          setSnippetIndex(null)
          setTimeout(() => {
            console.log(snippetIndex) <-- it logs only first state, instead wanted null 
            if(!snippetIndex) {
              openNotificationWithIcon(
                  "error",
                  "Invalid snippet selection",
                  "Snippet slice shouldn't tear code blocks. Please, try again."
              );
            }
          }, 4000)
        }
      } catch (err) {
        setSnippetIndex(null);
        openNotificationWithIcon("error", err.name, err.message);
      }
  }, [beginRow, endRow]);


Solution 1:[1]

I think you could make the notification UI a component and pass the state in as a parameter. If the state changes the component will be destroyed and recreated. And you can add the 4 second timer in useEffect() as well as cancel it in the 'return' of useEffect. If the timer fires, update some visibility flag.

Live typing this - so may contain some syntax errors...

cost myAlert = (isLoading) => {
  const [isVisible, setIsVisible] = setIsVisible(false)

  useEffect(()=>{
    const timerId = setTimer, setIsVisible(true) when it goes off
    return ()=>{cancelTimer(timerId)}
  }, [isLoading, setIsVisible])

  if (!isLoading && !isVisible) return null

  if (isVisible) return (
    <> 
      // your ui
    </>
  )
}

You may want to useCallback on setTimer so it won't cause useEffect to fire if isLoading changes -- but I think this should get you close to the solution.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dave