Como começar rapidamente com VUE3.0: Primeiros passos
Neste tutorial, aprenderemos como implementar Memoization no React. A memorização melhora o desempenho armazenando em cache os resultados das chamadas de função e retornando esses resultados armazenados em cache quando forem necessários novamente.
Abordaremos o seguinte:
Este artigo pressupõe que você tenha um conhecimento básico dos componentes de classe e função no React.
Se quiser conferir esses tópicos, você pode conferir a documentação oficial dos componentes e adereços do React
https://reactjs.org/docs/components-and-props.html
Antes de discutir os detalhes da memorização no React, vamos primeiro dar uma olhada em como o React usa o DOM virtual para renderizar a UI.
Um DOM regular contém basicamente um conjunto de nós mantidos na forma de uma árvore. Cada nó no DOM representa um elemento da UI. Sempre que ocorre uma mudança de estado no aplicativo, os nós correspondentes para esse elemento da UI e todos os seus elementos filhos são atualizados na árvore DOM, o que aciona um redesenho da UI.
Com a ajuda de algoritmos de árvore DOM eficientes, a atualização dos nós é mais rápida, mas o redesenho é lento e pode afetar o desempenho quando o DOM tem um grande número de elementos de UI. Portanto, o DOM virtual foi introduzido no React.
Esta é uma representação virtual do DOM real. Agora, sempre que houver alguma mudança no estado da aplicação, o React não atualiza o DOM real diretamente, mas cria um novo DOM virtual. O React irá então comparar este novo DOM virtual com o DOM virtual criado anteriormente, encontrar as diferenças (Nota do tradutor: isto é, encontrar os nós que precisam ser atualizados) e então redesenhar.
Com base nessas diferenças, o DOM virtual pode atualizar o DOM real com mais eficiência. Isso melhora o desempenho porque o DOM virtual não atualiza simplesmente o elemento da UI e todos os seus elementos filhos, mas atualiza efetivamente apenas as alterações necessárias e mínimas no DOM real.
Na seção anterior, vimos como o React usa um DOM virtual para executar com eficiência operações de atualização do DOM para melhorar o desempenho. Nesta seção, apresentaremos um exemplo que explica a necessidade de usar Memoization para melhorar ainda mais o desempenho.
Criaremos uma classe pai que contém um botão que incrementa uma variável chamada count
. O componente pai também chama o componente filho e passa parâmetros para ele. Também adicionamos console.log()
no método render
:
//Parent.js classe Pai estende React.Component { construtor(adereços) { super(adereços); this.state = {contagem: 0}; } handleClick = () => { this.setState((prevState) => { return { contagem: prevState.count + 1 }; }); }; renderizar() { console.log("Renderização pai"); retornar ( <div className="Aplicativo"> <button onClick={this.handleClick}>Incremento</button> <h2>{this.state.count}</h2> <Nome da criança={"joe"} /> </div> ); } } export default Parent;
O código completo deste exemplo pode ser visualizado no CodeSandbox.
Criaremos uma classe Child
que aceita parâmetros passados pelo componente pai e os exibe na UI:
//Child.js classe Filho estende React.Component { renderizar() { console.log("Renderização filho"); retornar ( <div> <h2>{this.props.name}</h2> </div> ); } } export default Child;
count
mudará toda vez que clicarmos no botão no componente pai. Como o estado mudou, o método render
do componente pai é executado.
Os parâmetros passados para o componente filho não mudam toda vez que o componente pai é renderizado novamente, portanto, o componente filho não deve ser renderizado novamente. No entanto, quando executamos o código acima e continuamos a incrementar count
, obtemos a seguinte saída:
Parent render Renderização secundária Renderização pai Renderização secundária Renderização pai Renderização filho
Você pode experimentar o exemplo acima nesta sandbox e visualizar a saída do console.
A partir da saída, podemos ver que quando o componente pai é renderizado novamente, o componente filho é renderizado novamente, mesmo que os parâmetros passados para o componente filho permaneçam inalterados. Isso fará com que o DOM virtual do componente filho execute uma verificação de comparação com o DOM virtual anterior. Como não há alterações em nossos componentes filhos e todos os adereços permanecem inalterados na nova renderização, o DOM real não será atualizado.
É definitivamente um benefício de desempenho que o DOM real não seja atualizado desnecessariamente, mas podemos ver que um novo DOM virtual é criado e uma verificação de comparação é executada mesmo quando não há alterações reais nos componentes filhos. Para componentes pequenos do React, esse custo de desempenho é insignificante, mas para componentes grandes, o impacto no desempenho pode ser significativo. Para evitar essa nova renderização e verificação diferencial do DOM virtual, usamos Memoization.
No contexto de aplicativos React, Memoização é um meio pelo qual sempre que o componente pai é renderizado novamente, o componente filho só é renderizado novamente quando os adereços dos quais ele depende mudam. Se não houver alterações nos adereços dos quais o componente filho depende, ele não executará o método render e retornará os resultados armazenados em cache. Como o método render não é executado, não haverá criação de DOM virtual e verificação diferencial, resultando em melhor desempenho.
Agora, vamos ver como implementar Memoization em componentes de classe e função para evitar essa nova renderização desnecessária.
Para implementar memorização em um componente de classe, usaremos React.PureComponent. React.PureComponent
implementa shouldComponentUpdate(), que faz uma comparação superficial state
e props
e apenas renderiza novamente o componente React se adereços ou estado mudarem.
Altere o componente filho para um código como este:
//Child.js class Child estende React.PureComponent { // Aqui mudamos React.Component para React.PureComponent renderizar() { console.log("Renderização filho"); retornar ( <div> <h2>{this.props.name}</h2> </div> ); } } export default Child;
O código completo deste exemplo é mostrado nesta sandbox.
O componente pai permanece inalterado. Agora, quando incrementamos count
no componente pai, a saída no console fica assim:
Parent render Renderização secundária Renderização pai Renderização pai
Para a primeira renderização, ele chama o método render
do componente pai e do componente filho.
Para cada nova renderização após o aumento count
, apenas a função render
do componente pai é chamada. Os componentes filhos não serão renderizados novamente.
Para implementar Memoização em Componentes de Função, usaremos React.memo(). React.memo()
é um componente de ordem superior (HOC) que executa um trabalho semelhante ao PureComponent
para evitar nova renderização desnecessária.
Aqui está o código para o componente de função:
//Child.js função de exportação Filho(adereços) { console.log("Renderização filho"); retornar ( <div> <h2>{props.name}</h2> </div> ); } export default React.memo(Child); // Aqui adicionamos HOC ao componente filho para implementar Memoization
e também convertemos o componente pai em um componente de função, conforme mostrado abaixo:
//Parent.js função padrão de exportação Parent() { const [contagem, setCount] = useState(0); const handleClick = () => { setContagem(contagem + 1); }; console.log("Renderização pai"); retornar ( <div> <button onClick={handleClick}>Incremento</button> <h2>{contagem}</h2> <Nome da criança={"joe"} /> </div> ); }
O código completo deste exemplo pode ser visto nesta sandbox.
Agora, quando incrementamos count
no componente pai, o seguinte será enviado para o console:
Parent render Renderização secundária Renderização pai Renderização pai
renderização paiReact.memo()
No exemplo acima, vemos que quando usamos React.memo()
HOC em um componente filho, o componente filho não é renderizado novamente, mesmo que o componente pai seja renderizado novamente.
No entanto, uma pequena coisa a notar é que se passarmos uma função como parâmetro para o componente filho, o componente filho será renderizado novamente mesmo após usar React.memo()
. Vejamos um exemplo disso.
Alteraremos o componente pai conforme mostrado abaixo. Aqui adicionamos uma função manipuladora e a passamos como parâmetro para o componente filho:
//Parent.js função padrão de exportação Parent() { const [contagem, setCount] = useState(0); const handleClick = () => { setContagem(contagem + 1); }; manipulador const = () => { console.log("handler"); // A função manipuladora aqui será passada para o componente filho}; console.log("Renderização pai"); retornar ( <div className="Aplicativo"> <button onClick={handleClick}>Incremento</button> <h2>{contagem}</h2> <Nome da criança={"joe"} childFunc={handler} /> </div> ); }
O código do componente filho permanecerá como está. Não usaremos funções passadas de componentes pais em componentes filhos:
//Child.js função de exportação Filho(adereços) { console.log("Renderização filho"); retornar ( <div> <h2>{props.name}</h2> </div> ); } export default React.memo(Child);
Agora, quando incrementamos count
no componente pai, ele renderiza e renderiza novamente o componente filho ao mesmo tempo, mesmo que não haja alteração nos parâmetros passados.
Então, o que faz com que o subcomponente seja renderizado novamente? A resposta é que toda vez que o componente pai é renderizado novamente, uma nova função handler
é criada e passada para o componente filho. Agora, como a função handle
é recriada a cada nova renderização, o componente filho descobrirá que handler
mudou ao fazer uma comparação superficial dos adereços e renderizará novamente o componente filho.
A seguir, abordaremos como corrigir esse problema.
useCallback()
para evitar mais re-renderizaçõesO principal problema com a re-renderização do componente filho é que a função handler
é recriada, o que altera a referência passada para o componente filho. Portanto, precisamos de uma forma de evitar esta duplicação. Se a função handler
não for recriada, a referência à função handler
não será alterada, portanto, o componente filho não será renderizado novamente.
Para evitar ter que recriar a função toda vez que o componente pai for renderizado, usaremos um React Hook chamado useCallback(). Ganchos foram introduzidos no React 16. Para saber mais sobre Hooks, você pode verificar a documentação oficial de hooks do React ou verificar `React Hooks: Como começar e construir o seu próprio ".
O gancho useCallback()
leva dois parâmetros: uma função de retorno de chamada e uma lista de dependências .
A seguir está um exemplo de useCallback()
:
const handleClick = useCallback(() => {. //Faça alguma coisa }, [x,y]);
Aqui, useCallback()
é adicionado à função handleClick()
. O segundo parâmetro [x, y]
pode ser um array vazio, uma única dependência ou uma lista de dependências. A função handleClick()
só é recriada sempre que alguma das dependências mencionadas no segundo parâmetro for alterada.
Se as dependências mencionadas em useCallback()
não foram alteradas, então a versão Memoized da função de retorno de chamada mencionada como o primeiro argumento será retornada. Alteraremos o componente pai para usar useCallback()
nos manipuladores passados para o componente filho:
//Parent.js função padrão de exportação Parent() { const [contagem, setCount] = useState(0); const handleClick = () => { setContagem(contagem + 1); }; const handler = useCallback(() => { // Use useCallback() para a função manipuladora console.log("manipulador"); }, []); console.log("Renderização pai"); retornar ( <div className="Aplicativo"> <button onClick={handleClick}>Incremento</button> <h2>{contagem}</h2> <Nome da criança={"joe"} childFunc={handler} /> </div> ); }
O código do componente filho permanecerá como está.
O código completo deste exemplo está nesta sandbox.
Quando incrementamos count
no componente pai do código acima, podemos ver a seguinte saída:
Parent render Renderização secundária Renderização pai Renderização pai Renderização pai
Como usamos useCallback()
para handler
no componente pai, a função handler
não será recriada toda vez que o componente pai for renderizado novamente e a versão Memoization handler
será passada para o componente filho. O componente filho fará uma comparação superficial e perceberá que a referência à função handler
não mudou, portanto não chamará o método render
.
a memorização é um bom meio de evitar a nova renderização desnecessária de componentes quando seu estado ou adereços não foram alterados, melhorando assim o desempenho dos aplicativos React. Você pode considerar adicionar Memoization a todos os seus componentes, mas essa não é necessariamente a maneira de construir componentes React de alto desempenho. A memorização só deve ser usada se o componente:
Neste tutorial entendemos:
React.memo()
para componentes de função e React.PureComponent
para componentes de classeReact.memo()
useCallback()
evitar problemas de nova renderização quando as funções são passadas como acessórios para componentes filhos.Espero que esta introdução ao React Memoization seja útil para você!
Endereço original: https://www.sitepoint.com/implement-memoization-in-react-to-improve-performance/Autor
original: Nida Khan