Como funcionam o hook useCallback e o React.memo?
Na última aula, você aprendeu sobre memoização e como o hook
useMemo funciona.
Nesta lição, você aprenderá como o hook useCallback e o React.memo funcionam.
Na última aula, também mencionamos que useCallback serve para memorizar referências de função.
Para React.memo, ele permite que você memorize um componente para evitar re-renderizações desnecessárias quando sua prop não mudou.
Aqui está a sintaxe básica do hook useCallback:
const handleClick = useCallback(() => {
// code goes here
}, [dependency]);
E aqui está a sintaxe básica de React.memo:
const MemoizedComponent = React.memo(({ prop }) => {
return (
<>
{/* Presentation */}
</>
)
});
Vamos ver um exemplo do hook useCallback:
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((prevCount) => prevCount + 1);
};
useEffect(() => {
console.log("useEffect runs");
}, [handleClick]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default Counter;
No componente, o efeito é executado sempre que handleClick muda porque a função handleClick está sendo recriada a cada renderização.
Para corrigir isso, você precisa dizer ao React para tratar a função handleClick como a mesma coisa entre as renderizações memorizando-a com o hook useCallback, para que ela não seja recriada:
import { useState, useEffect, useCallback } from "react";
function Counter() {
const [count, setCount] = useState(0);
// Memoize the handleClick function with useCallback
const handleClick = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
useEffect(() => {
console.log("useEffect runs");
}, [handleClick]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default Counter;
Agora a função handleClick não está sendo recriada a cada renderização.
Para mostrar como a função de ordem superior React.memo (ou memo) e o hook useCallback funcionam em conjunto, aqui está um componente Counter com uma função handleClick que precisa do useCallback mas atualmente não está usando:
import { useState, useEffect, useCallback } from "react";
import CounterChild from "./CounterChild";
function Counter() {
const [count, setCount] = useState(0);
const [timer, setTimer] = useState(new Date().toLocaleTimeString());
const handleClick = () => {
setCount(count + 1);
};
useEffect(() => {
const interval = setInterval(() => {
setTimer(new Date().toLocaleTimeString());
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div>
<h1>Time: {timer}</h1>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
<CounterChild onClick={handleClick} />
</div>
);
}
export default Counter;
Esta função também possui um timer no estado que é atualizado a cada segundo. Isso faz com que o componente seja re-renderizado toda vez que o timer mudar, fazendo com que a função handleClick seja recriada a cada renderização.
É por isso que o handleClick precisa ser memorizado com useCallback.
Aqui está o componente CounterChild:
const CounterChild = ({ onClick }) => {
console.log("CounterChild component rendered");
return <button onClick={onClick}>Increment from Child</button>;
};
export default CounterChild;
Este componente CounterChild recebe uma prop onClick, dando a você a capacidade de também incrementar o contador a partir dele.
Como o componente CounterChild é um filho do componente Counter, ele também será renderizado sempre que o Counter re-renderizar devido à alteração do timer. Então, o CounterChild também precisa ser memoizado.
Sem memoização, porque à medida que o componente é re-renderizado devido à atualização do timer a cada segundo, o componente CounterChild também é re-renderizado.
Para evitar isso, você precisa memorizar o componente CounterChild com React.memo:
import React from "react";
const CounterChild = React.memo(({ onClick }) => {
console.log("CounterChild component rendered");
return <button onClick={onClick}>Increment from Child</button>;
});
export default CounterChild;
As coisas ainda não funcionam de forma otimizada mesmo após memorizar o CounterChild com React.memo.
Isso acontece porque a função handleClick está sendo recriada a cada renderização, então ela também precisa ser memorizada com useCallback, para informar ao React que você precisa que a função permaneça a mesma entre as renderizações:
const handleClick = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, [count]);
Agora, o componente só re-renderiza quando o estado count muda.Este módulo não possui perguntas. Marque como concluído.