React: Evitando Anti-patterns

bind() e Funções em Componentes

Quando chamamos nos componentes de classe React, não devemos chamá-los repetidamente em nossos adereços.

Em vez disso, devemos chamar bind no construtor para que não tenhamos que chamar bind imediatamente quando passarmos em nossos métodos de componente de classe como adereços.

Por exemplo, podemos escrever o seguinte código para fazer isso:

import React from "react";export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
this.updateValue = this.updateValue.bind(this);
}
updateValue(e) {
this.setState({
name: e.target.value
});
}
render() {
return (
<>
<form>
<input onChange={this.updateValue} value={this.state.name} />
</form>
<p>{this.state.name}</p>
</>
);
}
}

No código abaixo, temos um input que chama o método , quando o valor do input muda.

Temos:

this.updateValue = this.updateValue.bind(this);

com o construtor, pois então sempre vai dar bind no componente com o valor de

Agora não precisamos criar repetidamente em todos os lugares. Também não precisaremos criar uma nova função todas as vezes que o evento for emitido porque não chamamos o bind, que retorna uma nova função.

O impacto no desempenho da criação de métodos em tempo real quando nosso aplicativo ficar grande será perceptível em algumas situações.

Ao invés de escrever:

<input onChange={this.updateValue.bind(this)} value={this.state.name} />

nós escrevemos como está acima, no primeiro exemplo.

Extra: bind em Arrow Functions x bind no construtor

Um colega de trabalho, Rodrigo Mello, Tech Lead da Zup, levantou uma questão importante, qual a melhor forma de darmos bind, via arrow function dentro da classe, ou dar bind dentro do construtor, qual seria a forma mais correta ?

Segundo uma discussão no repositório oficial do React, fazer o bind dentro do construtor é a forma como o time do Facebook, por razões de performance e porque em um exemplo com Arrow Functions, recriamos a função a cada renderização , e via construtor vinculamos ela apenas uma vez.

Exemplo Construtor:

Class Click extends react.Component {
constructor(props) {
super(props)
this.clickEvent = this.clickEvent.bind(this);
}

render = () => (
<button onClick={this.clickEvent}>Click Me</button>
)

clickEvent() {console.log(this)} // 'this' refers to the class
}

Exemplo Arrow Function:

import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props)
}

clickHandler = () => {
console.log( this )
}

render() {
return <button onClick={this.clickHandler}>Click Me</button>
}
}

Tem também o problema dos testes unitários, com o construtor, conseguimos chamar nosso bind, através do prototype da classe, o que não ocorre com a Arrow Function:

const spy = jest.spyOn(MyComponent.prototype, 'clickHandler');
// ...
expect(spy).toHaveBeenCalled();

Esquecendo a prop key ou usando indexes na prop key

A prop é importante quando renderizamos listas, mesmo que às pareça que não tem nenhuma função.

O valor único da prop permite que o React identifique os itens da lista corretamente.

os valores da prop são usados para combinar os filhos na árvore original com os filhos na árvore subsequente

Portanto, usar o index do array também é ruim porque pode levar o React a renderizar os dados errados.

É melhor usar IDs exclusivos como o valor da prop . Podemos criar um array com IDs exclusivos que podemos usar como keys da seguinte maneira:

import React from "react";
import { v4 as uuidv4 } from "uuid";
const arr = [
{
fruit: "apple",
id: uuidv4()
},
{
fruit: "orange",
id: uuidv4()
},
{
fruit: "grape",
id: uuidv4()
}
];
export default function App() {
return (
<div className="App">
{arr.map(a => (
<p key={arr.id}>{a.fruit}</p>
))}
</div>
);
}

No código acima usamos o pacote para criar um valor UUID único para a prop . Assim não existe chance de colisão entre IDS.

Dessa forma, os IDs são únicos e previsíveis, e não precisamos pensar em uma maneira de gerar o ID exclusivo para cada item.

Nomes de Componentes

Os nomes dos componentes devem começar com letra maiúscula. Por exemplo, se escrevermos o código a seguir, obteremos um erro do React:

import React from "react";const foo = () => <p>foo</p>;export default function App() {
return (
<div className="App">
<foo />
</div>
);
}

Se rodarmos o código acima, o React vai mostrar erro para ‘<foo> não é reconhecido pelo browser. Se você quer renderizar um componente React, comece seu nome com uma letra maiúscula .

Sempre precisamos começar nosso componente com uma letra maiúscula:

import React from "react";const Foo = () => <p>foo</p>;export default function App() {
return (
<div className="App">
<Foo />
</div>
);
}

Então veremos o ‘foo’ renderizado na tela como esperamos.

Fechar cada elemento

Todas as tags de componentes devem ter uma tag de fechamento ou uma barra no final para componentes de fechamento automático. Por exemplo, se tivermos:

import React from "react";const Foo = () => <p>foo</p>export default function App() {
return (
<div className="App">
<Foo>
</div>
);
}

Obteremos um erro de sintaxe porque falta uma barra ou tag de fechamento em:

<Foo>

Para fazermos nosso App rodar escrevemos:

import React from "react";const Foo = () => <p>foo</p>;export default function App() {
return (
<div className="App">
<Foo />
</div>
);
}

também funciona.

Conclusão

É fácil cometer erros ao escrever componentes React. Temos que lembrar de começar nossos componentes sempre com letra maiúscula.

Além disso, temos que fechar as tags do nosso componente, ou usar uma barra para fecha-los.

Também devemos chamar bind primeiro para que não tenhamos que chamá-lo repetidamente para vincular ao valor correto disso em componentes
baseados em classe.

Referências

Use arrow functions or bind manually in es6 classes? Any performance difference? @ React GitHub
Binding vs arrow-function (for react onClick event) @ StackOverflow

Créditos

React Antipatterns to Avoid escrito por John Au-Yeung

Software Engineer | Front-end Specialist @ Grover (https://rubenmarcus.dev)

Software Engineer | Front-end Specialist @ Grover (https://rubenmarcus.dev)