Should I memoize functions in custom hook?
Asked Answered
O

2

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

Odilia answered 21/11, 2019 at 12:35 Comment(0)
F
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.

Fabre answered 21/11, 2019 at 15:10 Comment(8)
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.Double
@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.Fabre
Explain please where argument count is taken from in your code.Odilia
@Odilia 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 hereFabre
@HMR, thanks. So count is taken from closure, isn't it?Odilia
@Odilia 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.Fabre
@HMR, am I understand rightly that my two question expressions are equivalent because they rerender every time count changes?Odilia
@Odilia Maybe in your case when count changes then increment can change too. There are many cases where a general onChange is passed to many components and you don't want all components to re render when only one of them changes.Fabre
E
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.

Edh answered 21/11, 2019 at 12:59 Comment(4)
So, should I memoize my function in the custom hook?Odilia
Could you provide the URL where the docs speak about those concepts? It will be helpful for me and other learners.Falsehood
There is this amazing post: nikgrozev.com/2019/04/07/…Edh
every function? I guess you wrong in 2023.Sack
F
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.

Fabre answered 21/11, 2019 at 15:10 Comment(8)
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.Double
@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.Fabre
Explain please where argument count is taken from in your code.Odilia
@Odilia 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 hereFabre
@HMR, thanks. So count is taken from closure, isn't it?Odilia
@Odilia 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.Fabre
@HMR, am I understand rightly that my two question expressions are equivalent because they rerender every time count changes?Odilia
@Odilia Maybe in your case when count changes then increment can change too. There are many cases where a general onChange is passed to many components and you don't want all components to re render when only one of them changes.Fabre

© 2022 - 2025 — McMap. All rights reserved.