Search code examples
reactjsreact-hooksmemoization

React, useCallback re-call again even if dependency did not change


I'm using useCallback to memoize a function, I wrote 2 functions, formatCounter and formatCounter2 and added useCallback to the functions with dependencies of counter and counter2.

When onClick function is called, the counter variable has changed but formatCounter2 called, but why? As I understand it needs to be called only when the dependency changes (counter2) and it does not change.

function App() {
 const [counter, setCounter] = React.useState(0);
 const [counter2, setCounter2] = React.useState(0);

 const onClick = React.useCallback(()=>{
   setCounter(prevState => ++prevState)
 },[]);
 
 const onClickSecond = React.useCallback(()=>{
   setCounter2(prevState => ++prevState)
 },[]);
 
 const formatCounter = React.useCallback((counterVal)=> {
 console.log('formatCounter Called')
  return `The counter value is ${counterVal}`;
},[counter])

 const formatCounter2 = React.useCallback((counterVal2)=> {
  console.log('formatCounterTwo Called')

  return `The counter value2 is ${counterVal2}`;
},[counter2])

const objMemo = React.useMemo(()=> {
    console.log('obj memo is')
    return {
    a:counter>2?'counter is bigger than 2': 'counter is less than 2'
  }
},[counter])
 return (
   <div className="App">
     <div>{formatCounter(counter)}</div>
     <div>{formatCounter2(counter2)}</div>
     <button onClick={onClick}>
       Increment
     </button>
      <button onClick={onClickSecond}>
       Increment2
     </button>
     <p>{objMemo.a}</p>
   </div>
 );
}

link to code


Solution

  • useCallback memoizes the function reference. But you're calling the format functions (both) in the render method. So what you're actually seeing is the inner function being called.

    Maybe out of curiosity you can check the reference to the function you're memoizing. There you will see that the reference is not changing.

    To check this I usually do:

      useEffect(() => {
        console.log('formatCounter reference changed!', formatCounter);
      }, [formatCounter]);
    

    You will see that it logs for when counter changes but not for when counter2 changes.