The only thing both Hooks have in common is that they return something memoizado. Although this is enough to confuse the functionality of both, throughout the answer it will be very clear what are the differences.
useCallback
const callbackMemoizado = useCallback(
() => {
// callback
},
[/* array de dependências */],
);
The useCallback
returns a callback memoizado. What it means?
With each rendering of your component, all the code that is on it runs again. Therefore, functions are re-declared, and a new reference (in memory) is allocated to each function. The useCallback
its function is redefined only when necessary, thus maintaining the same reference.
The dependency array is responsible for defining whether the callback will be reset or not. If there is a change in any of the dependencies, it will be reset. See the example below:
function App() {
const [contador, setContador] = React.useState(0);
const callbackAtualizado = React.useCallback(() => {
console.log('callbackAtualizado:', contador);
}, [contador]);
const callbackNaoAtualizado = React.useCallback(() => {
console.log('callbackNaoAtualizado:', contador);
}, []);
function incrementar() {
setContador(contador + 1);
}
return (
<div>
<p>contador: {contador}</p>
<button onClick={incrementar}>Incrementar</button>
<button onClick={callbackAtualizado}>callbackAtualizado</button>
<button onClick={callbackNaoAtualizado}>callbackNaoAtualizado</button>
</div>
);
}
ReactDOM.render(<App /> , document.querySelector("#app"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="app"></div>
In this example, so that the callbackAtualizado
can display the new state value contador
, it was necessary to create a new reference whenever this state was updated, since it is in the dependencies array.
Meanwhile, the reference of the callbackNaoAtualizado
has never been modified, so it always displays the state of the first render, which is 0
.
Note about the dependency array
The above example was done for the purpose of demonstration. You should avoid using variables (or functions) external to useCallback
without putting them in the dependency array. If you are doing this, your application is probably having some logic problem.
There is a eslint plugin called eslint-plugin-react-hooks
which has a rule for validating the dependency array. This rule would point to a problem in the callbackNaoAtualizado
:
React Hook useCallback
has a Missing dependency: 'counter'. Either include it or remove the dependency array.
In other words, "O React Hook useCallback
has a dependency missing: 'counter'. Add or remove the dependency array.".
If we used an external function called funcao
without adding to the dependencies array, the message would be:
React Hook useCallback
has a Missing dependency: 'function'. Either include it or remove the dependency array. If 'funcao' changes Too often, find the Parent Component that defines it and wrap that Definition in useCallback
.
"O React Hook useCallback
has a dependency missing: 'function'. Add it or remove the dependency array. If 'function' changes too often, find the parent component that defines it and wrap this definition in a useCallback
."
useMemo
const valorMemoizado = useMemo(
() => {
return; /* retorne algo a ser armazenado em `valorMemoizado` */
},
[/* array de dependências */],
);
The useMemo
returns a value memoized. How this differs from the useCallback
?
The useMemo
is executed during rendering. It is not a function to be called, but the return of the function passed to the useMemo
.
As the useCallback
is used to maintain the passed function reference, the useMemo
should be used to expensive calculations, and not to keep the reference of an object, for example.
The useMemo
recalculate the memoized value only when one of the dependencies is modified, following the same reasoning explained for the useCallback
.
Use the useMemo
to optimize the performance of your application, not as a semantic guarantee. In the future, React may choose to "forget" some memoized value and recalculate it in the next rendering to, for example, free up memory of components that are not on screen at the moment.
Follow an example below:
function expensiveCalc(max) {
const start = Date.now();
let counter = 0;
while (Date.now()-start < max) counter++;
return counter;
}
function App() {
const [contador, setContador] = React.useState(2000);
const [horario, setHorario] = React.useState(new Date());
React.useEffect(() => {
// useEffect apenas para forçar uma re-renderização a cada segundo
const id = setInterval(() => {
setHorario(oldHorario =>
new Date(oldHorario.getTime() + 1000)
);
}, 1000);
return () => clearInterval(id);
}, []);
const calculoMemoizado = React.useMemo(() => {
console.log(`Calculo realizado para`, contador);
return expensiveCalc(contador);
}, [contador]);
const calculoNaoAtualizado = React.useMemo(() => {
console.log(`Calculo nao atualizado realizado para`, contador);
return expensiveCalc(contador);
}, []);
function incrementar() {
setContador(contador + 1);
}
return (
<div>
<p>{horario.toLocaleString('pt-BR')}</p>
<p>contador: {contador}</p>
<p>calculoMemoizado: {calculoMemoizado}</p>
<p>calculoNaoAtualizado: {calculoNaoAtualizado}</p>
<button onClick={incrementar}>Incrementar</button>
</div>
);
}
ReactDOM.render(<App /> , document.querySelector("#app"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="app"></div>
I added the current time with useEffect
in the above example only to be more visible the freezing of the interface while the "expensive calculation" is being carried out, and also to show that the useMemo
is not recalculated to any status update.
The function expensiveCalc
does nothing else, just an expensive calculation for exemplification. I got it in that article.
Important detail: The
useMemo
should not be used for the purpose of "performing the function during render" or anything like that. I describe the correct use in my answer. TheuseMemo
is more related to the function return than to the function itself.– Rafael Tavares