skip to Main Content

I’m trying to call an animation but simultaneously call an async function. Once that resolves it should stop the animation

  console.log(context, "context value outside") //updating
  const animate = useCallback(
    async (index = 0) => {
      console.log(context, "context value inside") //not updating
      return setTimeout(() => {
        // do animate
        if (index === 3) {
          animate(0)
        } else {
          animate(index + 1)
        }
      }, 800)
   },
   [context]
  )

but when I call my trigger function that calls animate() and getAsyncFunction() it never updates the context value but I know the context value is updating because logging it in the main component body above renders a different value. why would it not update here? I even wrapped in useCallback to trigger it

2

Answers


    • Issue seemed to be with the flow and logic of the code on how you start and stop the animation.
    • If you’re just running it on button click it is being handled by the useEffect you need to cleanup the timeout also.
    function App() {
      const [context, setContext] = useState(0); // Simulates the context value
      const contextRef = useRef(context);
      const animationRef = useRef(null); // Tracks the current animation timeout
    
      // Update the ref whenever context changes
      useEffect(() => {
        contextRef.current = context;
      }, [context]);
    
      // Simulates an async function
      const getAsyncFunction = async () => {
        console.log("Async task started...");
        return new Promise((resolve) => setTimeout(() => {
          console.log("Async task completed!");
          resolve();
        }, 3000)); // 3 seconds delay
      };
    
      // Animation function
      const animate = useCallback((index = 0) => {
        console.log(contextRef.current, "context value inside animation"); // Always shows the latest context value
    
        animationRef.current = setTimeout(() => {
          if (index === 3) {
            animate(0); // Loop back to start
          } else {
            animate(index + 1); // Increment index
          }
        }, 800); // Delay for animation
      }, []);
    
      // Trigger function
      const trigger = useCallback(async () => {
        console.log("Trigger called. Starting animation and async task...");
        animate(); // Start animation
        await getAsyncFunction(); // Wait for async task
        if (animationRef.current) {
          clearTimeout(animationRef.current); // Stop the animation
          animationRef.current = null; // Clean up reference
        }
        console.log("Animation stopped after async task.");
      }, [animate]);
    
      return (
        <div style={{ padding: "20px" }}>
          <h1>React Animation with Async Example</h1>
          <p>Current Context Value: {context}</p>
          <button onClick={() => setContext((prev) => prev + 1)}>Update Context</button>
          <button onClick={trigger}>Start Animation & Async Task</button>
        </div>
      );
    }
    
    export default App;
    Login or Signup to reply.
  1. Please note that while a context object is specified as dependency for useCallback, it must specify the value of the context, not the context object as such. Please see below two sample codes, and see the difference.

    a. When the context value is in the dependency array, then useCallback has returned the latest function definition on each change of the value.

    b. When the context as such is in the dependency array, then useCallback does not return the latest definition.

    Case a

    App.js

    import { createContext, useCallback, useContext, useEffect } from 'react';
    import { useState } from 'react';
    import { useRef } from 'react';
    
    const someContext = createContext(null);
    
    export default function App() {
      const [someState, setsomeState] = useState(null);
    
      useEffect(() => {
        setTimeout(() => setsomeState(Math.random()), 1000);
      }, [someState]);
    
      return (
        <>
          <someContext.Provider value={someState}>
            <Component />
          </someContext.Provider>
          <br />
          <br />A new render by the someState changed to : {someState}
        </>
      );
    }
    
    function Component() {
      let ref = useRef(null);
      const contextValue = useContext(someContext);
      const cachedFunction = useCallback(
        () => console.log(`I am memoized with ${contextValue}`),
        [contextValue]
      );
    
      useEffect(() => {
          cachedFunction();
        if (ref.current === cachedFunction) {
          console.log('Memoized function retained');
        } else {
          console.log('Memoized function refreshed');
          ref.current = cachedFunction; // setting the ref to the new function
        }
      });
    
      return 'Check console';
    }
    

    Test run

    Browser console logs as below :

    // I am memoized with 0.3287557684453728
    // Memoized function refreshed
    // I am memoized with 0.08560524542093395
    // Memoized function refreshed
    // I am memoized with 0.664627542672463
    // Memoized function refreshed
    // I am memoized with 0.731629612163414
    // Memoized function refreshed
    // I am memoized with 0.7098038964262374
    // Memoized function refreshed
    

    Case b

    App.js

    ...
      const cachedFunction = useCallback(
        () => console.log(`I am memoized with ${contextValue}`),
        [someContext]
      );
    ...
    

    Test run

    Browser console logs as below:

    // I am memoized with null
    // Memoized function refreshed
    // I am memoized with null
    // Memoized function retained
    // I am memoized with null
    // Memoized function retained
    // I am memoized with null
    // Memoized function retained
    // I am memoized with null
    // Memoized function retained
    // I am memoized with null
    // Memoized function retained
    // I am memoized with null
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search