Como usar React.memo para otimizar o desempenho
Fiz um jogo de xadrez usando a interface React. Originalmente, todos os movimentos foram implementados aparentemente instantaneamente, mas depois de adicionar recursos extras, como alterar o fundo do Cabeçalho para refletir de quem é a vez (fundo branco ou preto dependendo de se é movimento branco) e outros recursos semelhantes, meu aplicativo ficou visivelmente lento. Eu suspeito que sejam esses tipos de declarações if-
const Details = props => {
console.log(props.status);
let [backGround, setBackGround] = useState("w details")
const history = useHistory();
if (props.status.white && backGround === "bl details"){
setBackGround("w details")
} else if (!props.status.white && backGround === "w details"){
setBackGround("bl details")
}
que são responsáveis porque o console.log imprimindo os adereços imprimirá seu extrato 8 vezes por movimento (originalmente era duas vezes)
Estou usando componentes funcionais em vez de classes e minha pesquisa sobre otimização me levou às seguintes soluções-
React Hooks - Como implementar o shouldComponentUpdate?
Devo envolver todos os meus componentes com React.memo () se não estiver esperando nenhum suporte?
Como usar o shouldComponentUpdate com React Hooks?
todos apontando para casos simples de, React.memomas obviamente estou faltando alguma coisa porque quando tento implementá-la, tudo que recebo é uma infinidade de 'adereços' não são erros definidos (um para cada vez que uso acessórios, o que é muito).
Details.jsx
import React, { useState } from 'react';
import "../App.css"
import DataService from '../service/DataService';
import { useHistory } from 'react-router-dom';
let [backGround, setBackGround] = useState("w details")
const Details = React.memo(props => {if (props.status.white){setBackGround("w details")} else {setBackGround("bl details")}}, (prevProps, props) => prevProps.white === props.white); {
console.log(props.status);
const history = useHistory();
const undo = () => {
DataService.undo()
.then(res => {
console.log(res);
props.setTheBoard(res.data);
props.changeTurn();
})
.catch(err => {
console.log(err);
window.alert(err.response.data.errMessage)
})
}
const restart = () => {
DataService.restartGame()
.then(res => {
console.log(res);
props.setTheBoard(res.data);
props.changeTurn(true);
})
.catch(err => {
console.log(err);
window.alert(err.response.data.errMessage)
})
}
const newGame = () => {
history.push('/');
}
return (
<div className={backGround} >
{props.status.active ? <h2>It is {props.status.playerName}'s turn</h2> :
<div>
<h1 className="check">Game Over!</h1>
<button className="tooltip" onClick={restart}>RESTART<span className="tooltiptext">Play another game vs the same opponent</span></button>
<button className="tooltip" onClick={newGame}>NEW GAME<span className="tooltiptext">Play a game vs a different opponent</span></button>
</div>}
{props.status.active &&
<div>
{props.isMove ? <button className="detailButtons" onClick={props.specialMove}>Special Move</button> : <button className="detailButtons" onClick={() => props.endTheGame(true)}>Forfeit</button> }
{props.isMove ? <button className="detailButtons" onClick={props.unselect}>Unselect Piece</button> : <button className="detailButtons" onClick={() => props.endTheGame(false)}>Draw</button> }
{props.isMove ? <button className="detailButtons">Toggle Sidebar</button> : props.undo && <button className="detailButtons" onClick={() => undo()}>Undo</button> }
{props.status.check && <h1 className="check">You must move out of check!</h1>}
</div> }
</div>
);
}
export default Details;
Uma vez que os adereços neste componente só mudam quando a curva muda (props.status.white), pensei que este seria um bom lugar para tentar reduzir as re-renderizações desnecessárias, mas todas as soluções que vejo são muito simples. Não é possível usar React.memo quando há um uso generalizado de adereços como este?
Como posso otimizar o desempenho e ainda manter o acesso aos adereços?
Respostas
Primeiro, algo que você deve ter em mente sobre como testar o desempenho no front-end em geral.
Você nunca deixa o devtools aberto enquanto verifica o desempenho, atividades nos devtools, diminui muito o desempenho do seu aplicativo.
Em segundo lugar, ambos useMemoe useCallbacknão lhe darão nenhuma melhoria de desempenho se você não estiver renderizando exaustivamente, renderizando adereços que são possíveis são renderizados antes e o mais importante, se você não estiver renderizando outros componentes personalizados dentro do seu componente personalizado, provavelmente, você não precisa usar esses ganchos também.
Então, qual situação devemos usar useCallbacke useMemo, para melhorar o desempenho?
Antes de responder a isso, você deve saber a diferença entre os tipos de valor e os tipos de referência.
Tipos de valor são imutáveis, então você pode atribuir, modificar e usar com segurança sem se preocupar com propriedade e coletor de lixo.
Os tipos de literais primitivos em JS são tipos de valor:
let a = "Hello"
let b = a
// from here, b doesn't own a reference, because a is a value type
let c = new String("Hello")
let d = c
// now d owns c reference because c was initialized as an instance of String class
Este é um exemplo muito simples de como podemos criar a mesma string usando duas abordagens diferentes.
O primeiro foi criar uma string usando uma string literal. Em segundo lugar, usando uma instância da classe String.
Se fizermos algo assim:
a === b // true
O ===compara o valor de acom o valor em b.
No entanto, algo diferente acontece quando fazemos algo assim:
c === d // true
Isso também retorna, truemas ===aqui funciona diferente, em vez de comparar valores, ele compara a referência de c é estritamente igual à referência de d . Não podemos comparar se o valor de cé estritamente igual ao valor de dapenas usar o ===operador, uma vez que ambos são tipos de referência de String, devemos comparar os dois assim:
// toString() in this case works as well but valueOf is more semantic in this case
c.valueOf() === d.valueOf()
Pense ae bnão aponte para uma referência na memória, mas dfaça referência à mesma referência criada em c.
Portanto, agora, com isso em mente, vamos voltar à questão sobre em qual situação esses ganchos melhoram o desempenho de um aplicativo React.
Para ajudar o React a comparar valores de referência , como os tipos Function, Array e Object, por exemplo, usamos useCallbackou useMemopara encapsular essas referências em um novo tipo que o React pode comparar e decidir se o valor da referência foi alterado.
Portanto, se você estiver renderizando apenas o nível mais baixo de sua hierarquia, provavelmente esses ganchos não o ajudarão a resolver problemas de desempenho.
No entanto, se você usar isso em uma parte do seu aplicativo que lida com tipos de referência e estiver renderizando com frequência. É uma boa oportunidade de usar isso e ajuda o React a saber se essas referências foram alteradas ou não para renderizar ou não o resto ou hierarquia.
Em resumo, useMemoe useCallbacksão usados para ajudar o React a descobrir se algum tipo de referência mudou seu valor para renderizar o componente.