Otimizando componentes do React com PureComponent
Neste artigo, Matheus Castiglioni fala sobre performance e mostra como otimizar componentes do React com PureComponent e tornar um projeto mais performático.

Performance e desempenho geralmente são grandes questões para levarmos em consideração durante a usabilidade de nossos webapps ou sites.
Construir um webapp que seja performático e garantir uma boa usabilidade para os usuários do mesmo, nem sempre é uma tarefa simples de conquistar.
Muitas vezes vai exigir um bom tempo de dedicação para encontrar momentos de gargalos e um pouco mais ainda para pensar/implementar uma solução. Para exemplificar no artigo, abordaremos a biblioteca React.
Como podemos tornar nossos componentes mais performáticos?
Criando um componente normal
A classe mais comum para ser utilizada durante a criação de nossos componentes é a velha conhecida Component. Através dela podemos criar um component comum.
Para entender o problema, vamos criar um simples aplicativo, onde temos um array de números, e através de um botão será possível adicionar novos números para esse array.
Criando o nosso container:
// App.js
import React, { Component, Fragment } from 'react'
import NComponent from '../components/NComponent'
class App extends Component {
state = {
numbers: []
}
render() {
const { numbers } = this.state
return (
<Fragment>
<h1>App Container</h1>
<button onClick={this.handleBtnClick} type="button">
Add new number
</button>
<NComponent numbers={numbers} />
</Fragment>
)
}
handleBtnClick = () =>
this.setState(({ numbers }) => {
const MAX = 100
const MIN = 1
numbers.push(parseInt(Math.random() * (MAX - MIN) + MIN))
return {
numbers
}
})
}
export default AppAgora vamos implementar o NComponent que será um componente normal do React utilizando a classe Component:
// NComponent.js
import React, { Component, Fragment } from 'react'
class NComponent extends Component {
render() {
const { numbers } = this.props
console.log("NComponent => I'm rendering...")
return (
<Fragment>
<h1>Normal component</h1>
<ul>
{numbers.map((number, index) => (
<li key={index}>{number}</li>
))}
</ul>
</Fragment>
)
}
}
export default NComponentPor fim, para que tudo funcione, devemos ter o nosso famoso index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './containers/App'
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)Que apenas irá renderizar nosso container App.
Se tudo ocorreu conforme o planejado, devemos ter o seguinte aplicativo:

Se você reparou, dentro da função render adicionamos um log para informar que aquele componente foi renderizado:
render() {
// ... códigos omitidos
console.log("NComponent => I'm rendering...");
// ... códigos omitidos
}Ao carregar a página pela primeira vez, olhando em nosso console, temos a seguinte saída:

Tudo funcionando como esperado, agora, se tentarmos adicionar um novo número no array:

Tudo funcionou como o esperado:
- Conseguimos adicionar um novo número para o
array - O novo número foi renderizado pelo nosso
NComponent - O log foi mostrado pois a função
renderdoNComponentfoi chamada
Até então não vimos nada diferente, mas onde podemos começar a otimizar nosso aplicativo?
Entenda a classe Component
Por padrão, um componente do tipo Component sempre irá se atualizar quando uma propriedade ou um estado sofrer modificações. Isso pode ser melhorado implementando o ciclo de vida shouldComponentUpdate. Essa função deve devolver um valor booleano para casos onde true significa que deve ser atualizado, e false onde não devemos atualizá-lo.
shouldComponentUpdate() {
return false
}Neste exemplo, o componente não será atualizado em momento algum (apenas para exemplo). A função shouldComponentUpdate recebe dois parâmetros: próximo valor de props e próximo valor de state. Dessa forma é possível realizar alguma atualização condicional.
shouldComponentUpdate(nextProps, nextState) {
// sua condição aqui...
}Mas será que devemos ficar nos preocupando com esse tipo de coisa? E se fosse possível utilizar algo inteligente o suficiente para saber quando deve ser atualizado? Pois é. isso é possível.
Criando um componente puro
Pensando justamente nesse problema, a galera do React, a partir da versão 15.3, disponibilizou um novo tipo de componente: o PureComponent. A diferença dele para o Component está justamente na inteligência. Vamos fazer um teste?
// PComponent.js
import React, { Fragment, PureComponent } from 'react'
class PComponent extends PureComponent {
render() {
const { numbers } = this.props
console.log("PComponent => I'm rendering...")
return (
<Fragment>
<h1>Pure component</h1>
<ul>
{numbers.map((number, index) => (
<li key={index}>{number}</li>
))}
</ul>
</Fragment>
)
}
}
export default PComponentA implementação do PComponent é semelhante do NComponent, com a única diferença em seu tipo. Agora estamos usando o PureComponent ao invés de Component. Com o componente novo criado, já podemos utilizá-lo dentro de nosso App.js:
// App.js
import React, { Component, Fragment } from 'react'
import NComponent from '../components/NComponent'
import PComponent from '../components/PComponent' // importe-o
class App extends Component {
state = {
numbers: []
}
render() {
const { numbers } = this.state
return (
<Fragment>
<h1>App Container</h1>
<button onClick={this.handleBtnClick} type="button">
Add new number
</button>
<NComponent numbers={numbers} />
<PComponent numbers={numbers} /> // renderize o novo componente
</Fragment>
)
}
handleBtnClick = () =>
this.setState(({ numbers }) => {
const MAX = 100
const MIN = 1
numbers.push(parseInt(Math.random() * (MAX - MIN) + MIN))
return {
numbers
}
})
}
export default AppNovamente, olhando o resultado, temos:

E novamente em nosso console, temos a seguinte saída:

Nada de novo, certo? Então, vamos tentar adicionar um novo número para nosso array:

Ué, ele atualizou apenas o NComponent. Por que isso aconteceu?
Entenda a classe PureComponent
Como já foi mencionado anteriormente, um componente do tipo PureComponent possuí uma inteligência. Ele só atualizará quando de fato as referências de suas props ou state forem atualizadas, e isso é feito através da implementação da função shouldComponentUpdate (também já mencionado).
Em outras palavras, o PureComponent já vem com uma implementação para a shouldComponentUpdate.
Mas atualizamos nosso array, então ele deveria ser atualizado também. Por que isso não ocorreu? Vamos olhar a função responsável por receber o click do botão e adicionar um novo número para o array:
handleBtnClick = () =>
this.setState(({ numbers }) => {
const MAX = 100
const MIN = 1
numbers.push(parseInt(Math.random() * (MAX - MIN) + MIN))
return {
numbers
}
})Basicamente estamos pegando o state de number já existente e adicionando um novo item para ele. Em momento algum estamos criando um novo array e assim modificando sua referência. Vamos modificar a função para sempre devolver um novo array:
handleBtnClick = () =>
this.setState(({ numbers }) => {
const MAX = 100
const MIN = 1
const newNumber = parseInt(Math.random() * (MAX - MIN) + MIN)
return {
numbers: [].concat(numbers, newNumber)
}
})Agora estamos sempre criando um novo array e atualizando nosso estado com o mesmo, voltando para o aplicativo e testando novamente:

E voilà, temos o resultado esperado:
- Um novo número é adicionado para o array
- O novo número foi renderizado pelo NComponent e PComponent
- O log de NComponent e PComponent foram logados
Conclusão
Neste artigo, expliquei um pouco sobre o PureComponent, quando devemos utilizá-lo e como ele funciona. Dessa maneira, conseguimos trazer um ganho de performance para nossos aplicativos, pois com a inteligência dos componentes, muitas chamadas para a função render serão evitadas e teremos um ganho de processamento (o mesmo será menos exigido).
Caso queira ver o exemplo em funcionamento:
Se preferir, pode visualizá-lo diretamente no sandbox.
E aí, você já conhecia o PureComponent? Não deixe de comentar.
Se gostou do artigo, fique à vontade para se inscrever na newsletter e receber novidades por e-mail.
Até a próxima!








