Como você decide, como escolhe entre esses três com base no propósito / tamanho / adereços / comportamento de nossos componentes?
Estender de React.PureComponent
ou para React.Component
um shouldComponentUpdate
método personalizado tem implicações no desempenho. O uso de componentes funcionais sem estado é uma opção "arquitetural" e ainda não possui benefícios de desempenho prontos para uso.
Para componentes simples, apenas para apresentação, que precisam ser reutilizados com facilidade, prefira componentes funcionais sem estado. Dessa forma, você tem certeza de que eles são dissociados da lógica real do aplicativo, que são fáceis de testar e que não têm efeitos colaterais inesperados. A exceção é se, por algum motivo, você tiver muitos deles ou se realmente precisar otimizar o método de renderização (como não é possível definir shouldComponentUpdate
um componente funcional sem estado).
Estenda PureComponent
se você souber que sua saída depende de props / state simples ("simples" significa que não há estruturas de dados aninhadas, pois o PureComponent realiza uma comparação superficial) E você precisa / pode obter algumas melhorias de desempenho.
Estenda Component
e implemente você mesmo shouldComponentUpdate
se precisar de alguns ganhos de desempenho, executando uma lógica de comparação personalizada entre adereços próximos / atuais e estado. Por exemplo, você pode executar rapidamente uma comparação profunda usando lodash # isEqual:
class MyComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
}
Além disso, implementar otimizações próprias shouldComponentUpdate
ou ampliadas PureComponent
são otimizações e, como de costume, você deve começar a investigar isso apenas se tiver problemas de desempenho ( evite otimizações prematuras ). Como regra geral, eu sempre tento fazer essas otimizações depois que o aplicativo está em um estado funcional, com a maioria dos recursos já implementados. É muito mais fácil focar nos problemas de desempenho quando eles realmente atrapalham.
Mais detalhes
Componentes apátridas funcionais:
Estes são definidos apenas usando uma função. Como não há estado interno para um componente sem estado, a saída (que é renderizada) depende apenas dos objetos fornecidos como entrada para esta função.
Prós:
A maneira mais simples possível de definir um componente no React. Se você não precisa gerenciar nenhum estado, por que se preocupar com classes e herança? Uma das principais diferenças entre uma função e uma classe é que, com a função, você tem certeza de que a saída depende apenas da entrada (não do histórico das execuções anteriores).
Idealmente, no seu aplicativo, você deve ter o maior número possível de componentes sem estado, porque isso normalmente significa que você moveu sua lógica para fora da camada de visualização e a moveu para algo como redux, o que significa que você pode testar sua lógica real sem precisar renderizar nada (muito mais fácil de testar, mais reutilizável etc.).
Contras:
Nenhum método de ciclo de vida. Você não tem como definir componentDidMount
e outros amigos. Normalmente, você faz isso em um componente pai mais alto na hierarquia para poder transformar todos os filhos em sem estado.
Não há como controlar manualmente quando uma nova renderização é necessária, pois você não pode definir shouldComponentUpdate
. Uma nova renderização acontece toda vez que o componente recebe novos adereços (nenhuma maneira de comparação superficial etc.). No futuro, o React poderá otimizar automaticamente os componentes sem estado, por enquanto existem algumas bibliotecas que você pode usar. Como componentes sem estado são apenas funções, basicamente é o problema clássico da "memorização de funções".
Não há suporte para referências: https://github.com/facebook/react/issues/4936
Um componente que estende a classe PureComponent VS Um componente normal que estende a classe Component:
Reagir costumava ter um que PureRenderMixin
você poderia anexar a uma classe definida usando React.createClass
sintaxe. O mixin simplesmente definiria uma shouldComponentUpdate
comparação superficial entre os próximos objetos e o próximo estado para verificar se alguma coisa mudou. Se nada mudar, não será necessário executar uma nova renderização.
Se você deseja usar a sintaxe ES6, não pode usar mixins. Portanto, por conveniência, o React introduziu uma PureComponent
classe da qual você pode herdar em vez de usar Component
. PureComponent
apenas implementa shouldComponentUpdate
da mesma maneira que o PureRendererMixin
. É principalmente uma questão de conveniência, para que você não precise implementá-lo por conta própria, pois uma comparação superficial entre o estado atual / próximo e os adereços é provavelmente o cenário mais comum que pode oferecer ganhos rápidos de desempenho.
Exemplo:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
Como você pode ver, a saída depende de props.imageUrl
e props.username
. Se em um componente pai você renderizar <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
com os mesmos adereços, o React chamariarender
toda vez, mesmo que a saída seja exatamente a mesma. Lembre-se, no entanto, que o React implementa o dom diffing, para que o DOM não seja realmente atualizado. Ainda assim, executar o dom diffing pode ser caro, portanto, nesse cenário, seria um desperdício.
Se o UserAvatar
componente se estender PureComponent
, é realizada uma comparação superficial. E como props e nextProps são os mesmos, render
não serão chamados.
Notas sobre a definição de "puro" no React:
Em geral, uma "função pura" é uma função que avalia sempre o mesmo resultado, com a mesma entrada. A saída (para React, é o que é retornado pelo render
método) não depende de nenhum histórico / estado e não possui efeitos colaterais (operações que alteram o "mundo" fora da função).
No React, os componentes sem estado não são necessariamente componentes puros de acordo com a definição acima, se você chamar "sem estado" um componente que nunca chama this.setState
e que não usa this.state
.
De fato, em a PureComponent
, você ainda pode executar efeitos colaterais durante os métodos do ciclo de vida. Por exemplo, você pode enviar uma solicitação ajax para dentro componentDidMount
ou executar algum cálculo do DOM para ajustar dinamicamente a altura de uma div dentro render
.
A definição de "componentes mudos" tem um significado mais "prático" (pelo menos no meu entendimento): um componente idiota "é informado" do que fazer por um componente pai por meio de adereços e não sabe como fazer as coisas, mas usa adereços retornos de chamada.
Exemplo de um "inteligente" AvatarComponent
:
class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}
render () {
<div onClick={this.expandAvatar}>
<img src={this.props.username} />
</div>
}
}
Exemplo de um "burro" AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
No final, eu diria que "burro", "sem estado" e "puro" são conceitos bem diferentes que às vezes podem se sobrepor, mas não necessariamente, dependendo principalmente do seu caso de uso.