0

I was just wondering how come useCallback cannot pick up the latest state value from the component. Isn't component's state is present in the outer scope.

const [x, updateX] = useState(2);

const handleChange = useCallback(() => {
   console.log(x);
   // To pick up latest value of x in here, I need to add x in dependency array?
   // Why is that this inner arrow function cannot pick x from its outer scope?
   
}, [])

Edit: The useRef latest value is picked up by the handleChange ..without needing the ref in the dependency array. However we need state value in dependencyArray. Why is that?

I guess there has to be some local scope in between getting created under the hood which is where the value of x is picked up from ? Not sure if I am correct.

Also, follow up question is how to write something like useCallback (function memoization)? using vanilla js ?

Avan
  • 366
  • 3
  • 17
  • Can you show the entire code? From that is hard to tell where the `updateX` is called and if it is actually changing the state, as well as the `handleChange` call, we just don't know what is happening without looking at the full implementation. That code you show on their own is fine – Ricardo Sanchez May 25 '22 at 14:05
  • Which entire code? I am simply asking why do we even need dependency array..isn't the inner arrow function is having x from its outer scope? Why is that they cannot pick updated value of x from the above scope. – Avan May 25 '22 at 14:07
  • You are asking why the useCallback hook is not logging the latest state value, from that code we can't tell why without looking at the entire code implementation. – Ricardo Sanchez May 25 '22 at 14:11
  • updateX can get called by some onClick event. I don't think that is very important ..who changes the state. – Avan May 25 '22 at 14:12
  • The [React docs](https://reactjs.org/docs/hooks-reference.html#usecallback) says that the function returned is memoized so you will have to pass a dependency, that is just the way it works – Ricardo Sanchez May 25 '22 at 14:15
  • Thats the way it works? How come ? how come latest value of useRef is available but not useState? – Avan May 25 '22 at 14:17

2 Answers2

1

Yes, you need to pass X in the dependency array. The callback only gets changed when the dependency changes. In this example you can count up and log the current state of x.

function Tester(props: TesterProps): JSX.Element {
    const [x, setX] = useState(0);

    const handleChange = useCallback(() => {
        console.log(x);
    }, [x]);
    return (
        <>
            <button onClick={() => setX(x + 1)}>Change State</button>
            <button onClick={() => handleChange()}>Handle Change</button>
        </>
    );
}

Pachari
  • 101
  • 2
  • 13
  • I want to know why we need to pass. ? isn't x available in its outer scope already? why cannot he pick it from the outer scope? – Avan May 25 '22 at 14:16
  • @Avan This goes deep into React mechanics and i'm not expert in this, but I know that the this is the only reason you would want to use `useCallback`. You gain performance by only redeclaring that function when one of those variables in dependencies array changes. So yeah, you have to tell react which variables will update your callback. Otherwise just declare the callback normally, without useCallback. – Bruno Polo May 25 '22 at 14:23
  • Then this does not really answers my question. – Avan May 25 '22 at 14:24
  • 1
    It is available. If it wasn't available it would be undefined. But the `x` is "saved" or "memoized" to have its original value when the component first mounts. When `x` gets changed, the hook still uses the orginal value. If `x` is a dependency, it updates the callback itself to contain its new value. – Pachari May 25 '22 at 14:25
  • 1
    I think @Pachari answer is perfectly correct – Bruno Polo May 25 '22 at 14:25
  • It is correct. But my question is different. ..there has to have a local scope in between somewhere. – Avan May 25 '22 at 14:27
  • If you want local scope dont use `useCallback` – Bruno Polo May 25 '22 at 14:28
0

Either do this:

const handleChange = useCallback(() => {
    console.log(x);
}, [x]);

Or this:

const handleChange = () => {
    console.log(x);
};

Both will print actual X value. The second one doesnt memoize the function, so it will be redeclared each render.

Bruno Polo
  • 657
  • 5
  • 11
  • Why is that ref ? you don't need to add to dependency array? you get latest value of ref? But not latest value of state unless you put in dependency array – Avan May 25 '22 at 14:34