Reage renderização desnecessária do elemento
Acabei de começar a aprender React e estava montando um pequeno aplicativo que faz chamadas para uma API de cotações. A API possui um ponto de extremidade que retorna uma citação aleatória. Quando o aplicativo é carregado inicialmente, ele faz uma chamada para a API e mostra uma cotação, e há um botão que pode ser clicado para obter uma nova cotação aleatória (nova chamada para a API).
Eu tenho um componente raiz chamado App. Este componente possui um componente QuoteWrap como filho. O componente QuoteWrap tem dois filhos: o botão que é usado para obter uma nova citação aleatória e um componente Citação que mostra o autor da citação e a própria citação. Este é o código dentro do componente QuoteWrap:
export default function QuoteWrap() {
const { quoteData, isLoading, fireNewCall } = useQuote();
const handleClick = () => {
fireNewCall();
};
return(
<>
<button onClick={handleClick}>Get random quote</button>
{ isLoading ?
<h2>Loading...</h2>
:
<Quote author={quoteData.author} quote={quoteData.quote} />
}
</>
);
}
useQuote () é um gancho personalizado que gerencia as chamadas para a API e retorna 3 valores: 1- os dados, 2- se uma chamada estiver em andamento e 3- uma função para fazer uma chamada para a API.
Obviamente, toda vez que o botão é clicado, todo o componente QuoteWrap é renderizado novamente (conforme as alterações quoteData e isLoading). Mas, realmente, o botão não precisa ser renderizado novamente, pois nunca muda.
Então pensei: ok, posso mover o botão até o componente App. Mas eu não tenho acesso à função fireNewCall no gancho useQuote.
Como posso evitar que o botão seja renderizado novamente? É mesmo importante neste caso ou estou ficando obcecado demais com os re-renderizadores do React?
Obrigado!
Respostas
Seu componente será renderizado novamente toda vez que a função handleClick mudar, ou seja, toda vez que QuoteWrap for renderizado.
A solução é o gancho useCallback. useCallback retornará a mesma função para handleClick, toda vez que QuoteWrap for renderizado, desde que as dependências não tenham mudado.
https://reactjs.org/docs/hooks-reference.html#usecallback
Você o usaria assim:
const handleClick = useCallback(() => {
fireNewCall();
},[fireNewCall]);
fireNewCall é a dependência, portanto, desde que useQuote retorne uma função fireNewCall estável, seu botão não será renderizado novamente, já que a propriedade handleClick não mudou.
Eu acho que você pode ficar muito obcecado com as re-renderizações do React. O botão deve ser renderizado novamente porque, handleClickdeve ser alterado quando fireNewCallalterado para algum caso. Mesmo que, handleClicknunca seja alterado. Não é necessário pensar em uma re-renderização de elemento.
Praticamente o que Benjamin e Viet disseram - em seu código original, uma nova função é atribuída para handleClick em cada renderização. Você pode usar React.useCallbackpara manter a referência da função original e apenas atualizá-la quando algo na matriz de dependência muda - neste caso, apenas fireNewCallprecisa ir para a matriz de dependência.
Mas, como diz o Viet, não fique muito obcecado com isso. O uso React.useCallbackpode até tornar seu código mais lento. Confira Kent C. Dodds When to useMemo e useCallback post para mais informações.