Home  >  Q&A  >  body text

Understanding dependency caching and referencing of useCallback in React

<p>Does the useCallback hook only cache the function reference or also the value/result of the function itself? Also, does using ref in a dependency array actually have any effect, like a component's ref? If not, is there a way to ensure that changes to the ref value have the appropriate effect? </p> <p>I originally thought that only function references would be cached, but after reading an article I learned that useCallback(fn, deps) is equivalent to useMemo(() => fn, deps), and I'm not sure if this is the case This is actually the case. Also, I tried using the component's ref as a dependency (like Video.js and react-slick), but I think it doesn't have much impact compared to other dependencies. </p>
P粉044526217P粉044526217452 days ago534

reply all(1)I'll reply

  • P粉170438285

    P粉1704382852023-08-16 09:43:21

    Yes, the purpose of useCallback is to allow a function to keep its reference between renders unless the dependencies you specify have changed.

    For example, if you have a function f(x,y)=>x y, you can use an empty dependency array useCallback((x,y)=>x y,[ ]), this function will never change. It always produces consistent behavior because it only uses its arguments to parse the output. However, it may change if you have another function h and another external value z, and h is defined as h(x ,y)=>x y z, then you need to include z in the dependencies so that if z changes, the value returned from useCallback The function will have a new reference.

    So the purpose of useCallback is typically when you pass a function so it doesn't trigger a child component to re-render, or when you use a function as a function in a child component's useEffect declaration dependencies. If the operations inside the function are expensive, then useCallback is less useful and you should memorize the results separately.

    Regarding the ref thing, I think including ref in the dependency doesn't do anything, it's like the array is empty. Maybe if ref was stored in the state it might be useful, but I'm not really sure.

    Here is a link https://stackblitz.com/edit/stackblitz-starters-wwyw9f?file=src/App.tsx, which has some examples, which may be useful.

    If it might be deleted, I can also paste it over.

    import * as React from 'react';
    import './style.css';
    
    export default function App() {
      //x and y to be used as function arguments
      const [x, setX] = React.useState(0);
      const [y, setY] = React.useState(0);
    
      //z is variable also used in function calculation but not as an argument
      const [z, setZ] = React.useState(0);
    
      const ref = React.useRef<number>(0);
    
      //counter to see how many times function updates
      //will start at 2 cause strict mode but that's no issue really
      const [i, setI] = React.useState(0);
    
      //function to add x and y from args and add z from state
      const fn = React.useCallback(
        (x: number, y: number) => {
          // console.log(`${x}+${y}`);
          return x + y + z;
        },
        [z] // if you remove z and update it get wrong result
      );
    
      //update fn count on fn change
      React.useEffect(() => {
        setI((i) => i + 1);
      }, [fn]);
    
      React.useEffect(() => {
        console.log('nice');
        return () => console.log('ref cleanup');
      }, [ref]);
    
      return (
        <div>
          <pre>{JSON.stringify({ x, y, z })}</pre>
          <button onClick={() => setX((x) => x + 1)}> x ++</button>
          <button onClick={() => setY((y) => y + 1)}> y ++</button>
          <button onClick={() => setZ((z) => z + 1)}> z ++</button>
    
          <pre>x+y+z={fn(x, y)}</pre>
    
          <pre>fnCount:{i}</pre>
    
          <button
            onClick={() => {
              ref.current = ref.current++;
            }}
          >
            ref++
          </button>
        </div>
      );
    }

    hope it is of help to you

    reply
    0
  • Cancelreply