10

I have the counter component. I encapsulated the business logic with custom hook. Should I optimize functions by means useCallback? If there is input onchange handler, will the situation be the same?

const increment = () => {
    setCount(count + 1);
};

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

Sand

bigbabyweb
  • 328
  • 1
  • 5
  • 12

2 Answers2

5

Every function declared within a functional component’s scope should be memoized or cached with useCallback. If it references to other variables or functions from the component scope it should list them in its dependency list. Otherwise every prop/state change will be recreating the function a behavior rarely used.

But remember to measure before optimizing. - Even the oficial documentation says to go easy on that.

Ricardo Gonzalez
  • 1,827
  • 1
  • 14
  • 25
stackchain
  • 81
  • 5
5

Assuming that count and setCount came from const [count,setCount] = useState(0) then you should use callback in the following way so increment function stays the same during the component's life cycle:

const increment = useCallback(() => setCount(count => count + 1),[]);

You don't need to re create increment when count changes because you can pass a callback to the state setter function.

HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    Depending on what linting tools you're using, you may get an error unless you pass `setCount` to the `deps` array, but because `setCount`'s reference is always the same, explicitly passing it as a dependency doesn't change the behavior and this is equally correct. – Patrick Roberts Nov 21 '19 at 20:56
  • @PatrickRoberts I assume setCount is the setter from useState, maybe older linters have trouble with it but I neer have the linter complain about a useState setter being a dependency. If you were to use `setCount(count+1)` then `count` would be a dependency but passing a callback to the setter prevents that. – HMR Nov 21 '19 at 21:10
  • Explain please where argument `count` is taken from in your code. – bigbabyweb Nov 21 '19 at 21:26
  • 1
    @bigbabyweb It's not my code but your code. I assume that count and setCount came from `const [count, setCount] = useState(0)`. The setter function from useState can receive a value: `setCount(count+1)` or a callback `setCount(count=>count+1)`. It is documented [here](https://reactjs.org/docs/hooks-reference.html#functional-updates) – HMR Nov 21 '19 at 21:35
  • @HMR, thanks. So `count` is taken from closure, isn't it? – bigbabyweb Nov 22 '19 at 11:15
  • 1
    @bigbabyweb With `setCount(count+1)` count comes from closure and is a dependency to useCallback so every time count changes your increment function changes as well. With `setCount(count=>count+1)` count comes from React and you it is no longer a dependency for the useCallback so increment function will not change during component's life cycle. – HMR Nov 22 '19 at 13:31
  • @HMR, am I understand rightly that my two question expressions are equivalent because they rerender every time `count` changes? – bigbabyweb Nov 22 '19 at 15:53
  • 1
    @bigbabyweb Maybe in your case when count changes then increment can change too. There are many cases where a general [onChange](https://stackoverflow.com/a/58970646/1641941) is passed to many components and you don't want all components to re render when only one of them changes. – HMR Nov 22 '19 at 17:28