Front End

31 mar, 2017

Iniciando com React – #3 Criando componentes

Publicidade

O React introduz uma nova maneira de construir interfaces, o que podemos chamar de “Interface baseada em componentes”. Os componentes permitem que você divida sua interface em partes independentes e reutilizáveis, e que você pense em cada parte de forma isolada.

Nota: Este artigo faz parte da série “Iniciando com React”. Se você está começando com React agora, sugiro ler os outros artigos primeiro.

Componentes podem possuir diversos formatos e ter diferentes responsabilidades. Um componente pode ser desde um botão, um formulário ou até uma página completa.

O React nos permite definir e reutilizar componentes de maneira simples, conforme veremos ao longo desse artigo.

Functional components

A forma mais fácil de definir componentes é através de funções Javascript, conforme o exemplo abaixo:

import React from 'react';

function Welcome(props) {
  return (
    <h1>Hello, {props.name}</h1>
  );
}

Quando o componente é definido em forma de função, o React cria o componente executando essa função e passando as props como primeiro parâmetro. Ele espera um retorno em JSX do que deve ser renderizado, ou null no caso de não mostrar nada. Componentes React definidos da forma descrita acima, ganham o nome de Functional Components.

Props

A forma principal de comunicação entre componentes é utilizando as props. Elas são propriedades/ atributos passados para os componentes filhos. Observe o exemplo abaixo:

import React from 'react';
import ReactDOM from 'react-dom';

function Welcome(props) {
  return (
    <h1>Hello, {props.name}</h1>
  );
}

ReactDOM.render(
  <Welcome name='Stranger' />,
  document.getElementById('root')
);

No exemplo acima, na linha #4, estamos novamente definindo o componente Welcome. Na linha #11, estamos utilizando esse componente e passando para ele a prop name que possui como valor, a string “Stranger”.

Valores dinâmicos

Podemos também passar variáveis para as props, utilizando chaves {}:

import React from 'react';
import ReactDom from 'react-dom';

function Welcome({ name }) {
  return (
    <h1>Hello, {name}</h1>
  );
}

const strangerName = 'Stranger';

ReactDOM.render(
  <Welcome name={strangerName} />,
  document.getElementById('root')
);

Observe as linhas #10 e #13 do exemplo acima. Primeiro, definimos uma const strangerName e depois a passamos para a props name. No JSX, todo o conteúdo que estiver dentro de chaves será interpretado como Javascript.

O ES6 trouxe uma feature chamada Object Detructuring, e é ela que estamos utilizando na linha #4 ({ name }). Basicamente, o que ela faz é extrair os atributos de um objeto e criar variáveis com o mesmo nome. Dessa forma, podemos utilizar name aos invés de props.name.

Funções callbacks

É possível também, passar funções através das props. Normalmente, fazemos isso quando precisamos passar um callback para um componente.

Um callback é, em suma, uma função que passamos como parâmetro para outra função, que por sua vez, poderá executar o callback quando algum determinado evento ocorrer, como o final de um request, um evento de click do usuário e etc.

import React from 'react';
import ReactDom from 'react-from';
const strangerName = 'Stranger';

ReactDOM.render(
  <button onClick={() => alert(strangerName)}>
    Click
  </button>,
  document.getElementById('root')
);

No arquivo acima, não estamos definindo nenhum novo componente, apenas utilizando o elemento button, comum do HTML.

De acordo com o exemplo acima, cada vez que o usuário clicar no botão, um alerta será mostrado na tela.

O React suporta alguns eventos básicos de interação de acordo com o componente HTML que utilizarmos, como onClick, onChange, onKeyPress etc…

Children

A prop “children” é um pouco diferente das outras. Ela é o conteúdo que passamos dentro da tag:

<div>Esse conteúdo será a prop children</div>

Conforme o exemplo acima, todo o conteúdo que está dentro da div, será o valor de props.children.

Conforme o exemplo que veremos logo abaixo, é possível passar children explicitamente, porém, esse modo é pouco utilizado. A forma abaixo é equivalente à anterior:

<div children='Esse conteúdo será a prop children' />

Button

Vamos rever um exemplo similar ao que foi apresentado no primeiro artigo da série, explorando em detalhes o conteúdo:

import React from 'react';
import ReactDom from 'react-dom';

function Button({ children, onClick, type = 'button' }) {
  return (
    <button type={type} className='btn' onClick={onClick} >
      {children}  
    </button>
  );
}
  
ReactDOM.render(
  <Button onClick={() => console.log('You just clicked')} >
    Example
  </Button>,
  document.getElementById('root')
);

Como podemos ver no exemplo acima, na linha #4 estamos definindo um componente chamado Button. Mais abaixo, na linha #13, estamos utilizando-o, passando o children “Example” e uma função para onClick, que quando executada, exibirá “You just clicked” no console do navegador.

Na definição do componente na linha #4, estamos esperando as props children, onClick e type, e especificamos a string “button”, para ser o valor padrão da prop type.

Na linha #6, estamos utilizando o elemento button, passando para ele a prop type e passando a prop className, que é equivalente ao atributo classdo HTML. No className, estamos passando a classe “btn”, que seria utilizada no CSS para definirmos o estilo do nosso botão. Além disso, repassamos a prop onClick.

Na linha #7, apenas repassamos o children que recebemos.

Dica: Sempre inicie o nome de componentes com uma letra maiúscula.

Por exemplo, <button /> representa um elemento HTML padrão, mas <Button /> representa um componente e exige que a variável Button esteja no escopo.

Composição de componentes

É possível criar componentes que reutilizam outros. Dessa forma, podemos criar pequenos componentes que são úteis em várias partes de uma aplicação e, então, compor esses componentes quando precisamos criar componentes mais complexos.

import React from 'react';
import ReactDom from 'react-dom';

function Button({ children, onClick, type = 'button' }) { 
  return (
    <button type={type} className='btn' onClick={onClick} >
      {children}  
    </button>
  );
}

function ButtonGroup({ children, onClick }) {
  return (
    <div className='btn-group'>
      <Button onClick={onClick}>Click A</Button>
      <Button onClick={onClick}>Click B</Button>
      <Button onClick={onClick}>Click C</Button>
    </div>
  );
}

ReactDOM.render(
  <ButtonGroup onClick={() => console.log('You just clicked')} />,
  document.getElementById('root')
);

Conforme é possível ver no exemplo acima, possuímos dois componentes: Button e GroupButton. Primeiro, temos o componente mais simples, o Button, que pode ser reaproveitado em qualquer parte da aplicação. Depois temos o ButtonGroup, onde reutilizamos Button para criarmos um componente maior.

Dica: Componentes devem retornar um único elemento raiz. Esse é um dos motivos de termos colocado uma <div> para conter todos os componentes <Button />.

Props são read-only

De qualquer maneira que você declare componentes, seja através de classes ou funções, você nunca deve alterar o valor de props. Os componentes devem ser escritos como funções puras, de modo que o valor de suas props permaneça intacto.

Observe abaixo um exemplo de função pura:

function sum(a, b) {
  return a + b;
}

Na função acima, nós recebemos os parâmetros a e b e retornamos a sua soma. Funções como essa são consideradas funções puras, pois chamadas com os mesmos parâmetros, sempre retornarão os mesmos resultados, e elas também não alteram o valor dos seus parâmetros ou valores que estão fora de seu escopo.

Já o exemplo abaixo é considerado uma função impura, porque na linha #2, estamos reatribuindo o valor de result.a, objeto que foi criado fora do escopo atual:

1  function sum(result, b) {
2    result.a += b;
3    return result;
4  }

Conclusão

Functional components, como vimos até agora, não guardam muita lógica dentro de si. Eles normalmente só guardam uma marcação específica do componente. Quando possuem lógica, é a mínima possível.

Sendo assim, functional components são perfeitos para construir componentes simples de apresentação, como botões, labels, listas etc. Quando precisamos construir componentes que envolvem lógica e precisam guardar estado, precisamos criar componentes utilizando class, e é isso que veremos no próximo artigo.