最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React context not updating value - Stack Overflow

programmeradmin0浏览0评论

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

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

Share Improve this question asked Nov 19, 2024 at 12:06 Red BaronRed Baron 7,70112 gold badges47 silver badges112 bronze badges 1
  • An async function is quite useless if it doesn't use await on a promise-returning function. – trincot Commented Nov 19, 2024 at 13:48
Add a comment  | 

2 Answers 2

Reset to default 0
  • 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;

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
发布评论

评论列表(0)

  1. 暂无评论