How does React useState and useCallback hook work when useCallback lacks dependencies
Asked Answered
F

2

9

I am studying react-hook recently. I have encountered a problem which makes me unable to understand in the process of practice.

import React, { useState, useCallback } from 'react';
const set = new Set();

function Demo() {
  const [count, setCount] = useState(0);

  const changeValue = useCallback(() => {
    setCount(count + 1);
  }, []); 

  set.add(count);
  console.log('size: ', set.size);

  return(
    <div>
      <p>Hello React Hook</p>
      <p>{count}</p>
      <button onClick={changeValue}>count++</button>
    </div>
  )
}

export default Demo;

// If you click the button multiple times, the output is:
// size: 1
// size: 2
// size: 2

I wrote a timer using react-hook. As I expected, the count value shown is always 1, because I didn't use count as a dependency of useCallback.

But what I can't understand is console.log('size: ', set.size) only printed three times, why? Every time I click the count++ button, it will cause the Demo function to re-execute. So every time I click the button, shouldn't console.log('size: ', set.size) will be executed? But in fact it only executed three times.

And why does size keep 2 unchanged? I understand that setCount will replace a new count every time, so size should not increase?

Please help me answer my doubts, thank you very much.

You can test my code here.

Frizzle answered 16/11, 2019 at 10:17 Comment(0)
A
10

The Demo component will re-render whenever its state is changed. So, it will have the size 1 as you have added count state to the set:

set.add(count); // 0 in initial render, size is 1

Now, when you click on the button, it will use the callback useCallback that is memoized by react. It means it will first check if has cached value or not. So, at the first click it has no cached value. And thus it calls its callback to set (update) the count state. Now, count is 1 and you also added the count in to the set.

set.add(count); // 1 in first click, size is 2

On following clicks useCallaback has cached the count value but you have not set the count in second parameter of useCallback hook and thus it will just return the cached value that is 1 - the count state. And it will return always from this. Again I repeat, this time the set size is 2.

You should watch for the changes when put the count state in second argument:

  const changeValue = useCallback(() => {
    setCount(count + 1);
  }, [count]); // we watch it on every click

Now, you will see the changing value each time you click on the button.


Also note: Don't confuse the changeValue is not being called on following clicks. This calls the useCallback every time but it just doesn't update the state:

  const changeValue = useCallback(() => {
    console.log('clicked') // logged every time on button click
    setCount(count + 1); // count state is not being watched, 
     // setCount won't update the count on following clicks
     // once it caches the state.
  },[]); // need to watch state here to update the state
Abelabelard answered 16/11, 2019 at 10:49 Comment(6)
useCallback is used to memoize a function. There is no sense of using a callback in this case as its dependencies will change on every function rerender. You can refer to the previous state in setCount and make useCallback without dependenciesVincenza
@EduardMukans OP is learning about hooks and trying to understand how useCallback hook works. And that's what is explained here.Abelabelard
@ Bhojendra Rauniyar I understand what you said, but I still have a problem. After I click the button for the first time, the value of count starts to be cached, so the count remains the same when I click for the second time. But why does the second click still print size:2?Frizzle
Since count state remains same. Demo function will not re-render and the set.add(count) will not be called. For your query, why second click still print size: 2 is because it returns for the first time after it's being cached. After that it will not be called.Abelabelard
@Bhojendra Rauniyar Yes, Demo function will not re-render because since count state remains same. So why does it still print size: 2 on the second click? I don't understand much that 'it returns for the first time after it's being cached'. Can trouble you to explain it?Frizzle
I mean for the first time (with cached value), the useCallback will reflect the component and after that it won't. Hope, it's clear now.Abelabelard
C
0

I'm also a React beginner but watching your question I found something.

I think you already know the internal motions of useState and useCallback,

But the reason why print size: 2 message is printed whenever you click is

the type of set variable is Set

the variable with Set type doesn't keep duplicate values inside.

(I think you might already know this but I think you missed it because you are in struggle of React)

so it is far from React I think.

And why it only printed three times is .. I think it is because an optimization of React but it is not sure because I'm also beginner

Cut answered 3/3 at 16:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.