React: Components, State and Props

Neste tem como objetivo, fixar os conceitos mais importantes que é a base para ser um bom programador em React JS, temos que saber o que é um Component, State e Props, vamos lá ;-). Para este post eu irei criar um Projeto de exemplo e vou subir para meu Github.

Vou explicar sucintamente como criar e rodar o seu projeto no React JS, porque o foco do post e para falar do básico dele, segue os passo a passo:

  • create-react-app react-basic
  • cd react-basic
  • yarn start ou npm start

Tela inicial do Projeto

Se viu uma tela igual ou parecida com esta, é porque deu certo a instalação do seu projeto, agora vamos limpar alguns arquivos desnecessários e criar alguns components simples só para entender sobre o foco do post.

delete os arquivos:

  • public/manifest.json;
  • src/logo.svg;
  • src/serviceWorker.js;
  • src/App.test.js;
  • src/App.css

 

  • E depois vá no arquivo public/index.html e delete esta linha:
<link rel=”manifest” href=”%PUBLIC_URL%/manifest.json” />

 

  • Delete na pasta src/index.js e delete as seguintes linhas:

import * as serviceWorker from ‘./serviceWorker’;

serviceWorker.unregister();

  • Agora sua index.js vai ficar assim:
import React from “react”;
import ReactDOM from “react-dom”;
import “./index.css”;
import App from “./components/App”;
ReactDOM.render(<App />, document.getElementById(“root”));

 

  • O arquivo src/App.js tem que ficar dessa forma:
import React from “react”;
function App() {
return (
<h1>React Basic</h1>
);
}
export default App;

 

  • O arquivo src/index.css você incluir no final as seguintes linhas abaixo:
.App {
text-align: center;
background-color: #282c34;
min-height: 100vh;
}.App-header {
background-color: #d4c008;
height: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-size: calc(10px + 1vmin);
color: rgb(3, 3, 3);
box-shadow: 2px 2px #111;
}.App-header {
text-shadow: 2px 1px #fff;
}

 


O que é JSX

Antes de começar você tem que saber o que é JSX,

Em resumo, JSX é uma sintaxe semelhante ao XML, na qual você consegue escrever e compreender de uma forma melhor como será montado o seu component na UI.

Por que JSX?

O React adota o fato de que a lógica de renderização é inerentemente acoplada com outras lógicas de UI: como eventos são manipulados, como o state muda com o tempo e como os dados são preparados para exibição.

Ao invés de separar tecnologias artificialmente colocando markup e lógica em arquivos separados, o React separa conceitos com unidades pouco acopladas chamadas “componentes” que contém ambos. Voltaremos aos componentes em outra seção. Mas, se você ainda não está confortável em usar markup em JS, esta palestra pode convencer você do contrário.

O React não requer o uso do JSX. Porém, a maioria das pessoas acha prático como uma ajuda visual quando se está trabalhando com uma UI dentro do código em JavaScript. Ele permite ao React mostrar mensagens mais úteis de erro e aviso.

 


 

Agora finalmente vamos começar a falar de Components, State e Props que é o foco do post, mais antes tinha que esta introdução e falar um pouco de JSX, vamos lá Devs ;-p.

Components em uma aplicação e qualquer arquivo com extensão *.js, *.jsx ou se tiver usando TypeScript *.tsx, o que é TypeScript? Bom isso e outra linguagem de JS que proporciona uma tipagem forte para JS, mais e para outro post futuramente, ops não vamos sair do foco do post.

Depois que o create-react-app faz a sua mágica e te entrega um boilerplate lindão e pronto para uso, o arquivo index.js e um componente, App.js e um component, até então tudo bem né, vamos agora criar nossos novos componentes e saber o que é afinal Props.

 

Props

Props, que é uma abreviação de properties, ou propriedades, são informações que podem ser passadas para um componente. Pode ser uma string, um número, até mesmo uma função. Este valor pode então ser utilizado pelo componente que a recebe. Primeiro, passamos o dado que desejamos passar ao componente definindo-o como um atributo, onde o componente é utilizado.

Agora vamos criar uma pasta src/components e dentro criaremos o arquivos index.jsx e CountDisplay.jsx e será uma classe funcional, basta apenas copiar o código ok. Vamos passar a prop currentCount para o componente CountDisplay, que está sendo chamado no arquivo App.jsx. Para o valor numérico, passamos o mesmo dentro de chaves. Vejamos:

  • CountDisplay.jsx
import React from “react”;
import ReactDOM from “react-dom”;const CountDisplay = () => {
returnThe count is 0};
export default CountDisplay;
  • App.jsx
import React from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;const App = props => {

return (
<h1>React Basic</h1>
);
};
export default App;

Agora, vamos resgatar este valor no componente CountDisplay para utilizá-lo. Para isso, na definição de componente, podemos incluir as props como parâmetro da função. E aí, teremos todas as props que foram passadas para este componente. Vamos dar um console.log nas props para ver o que temos. O código no arquivo CountDisplay.jsx fica desse jeito:

  • CountDisplay.jsx

import React from “react”;
import ReactDOM from “react-dom”;

const CountDisplay = (props) => {
console.log(props);
return <p>The count is 0</p>;
};

export default CountDisplay;

Como você pode perceber ao atualizar a aba do seu navegador e abrir o console dele, o que temos é um objeto, com a prop que você acabou de passar para o componente CountDisplay. Vamos fazer um rápido teste e adicionar mais uma prop neste componente, que ficará assim:

{currentCount: 3}

 

  • App.jsx

import React from “react”;
import ReactDOM from “react-dom”;

import CountDisplay from “./CountDisplay”;

const App = props => {
return (
<CountDisplay currentCount={3}
name=”HjrDev” />
);
};

export default App;

 

Ao atualizar novamente a aba do seu navegador e checar o console novamente, temos exatamente o que esperamos. Agora, o objeto props possui valores para currentCount e name, conforme passamos.

Agora, podemos remover a linha onde fazemos o console.log e também a prop name, que utilizamos somente para um rápido teste. E então, vamos resgatar o valor de currentCount dentro do componente CountDisplay e substitui-lo no parágrafo que exibe a contagem. Ficará assim:

{currentCount: 3, name: “HjrDev”}

 

  • CountDisplay.jsx

import React from “react”;
import ReactDOM from “react-dom”;

const CountDisplay = (props) => {
return <p> The count is {props.currentCount} {props.name} </p>
};

export default CountDisplay;

 

Se você foi curioso e leu mais sobre JSX, sabe agora que podemos incluir o valor de uma variável dentro de um elemento, usando as chaves ({}) chamamos de interpolação, como foi feito acima {props.currentCount} {props.name} . Mas e se pudéssemos passar a informação para este componente sobre qual o valor atual da contagem e do name, e agora para ver o resultado passando a props, agente não precisa mais do console.log para ver, bacana né, isso que acabou de aprender foi o Props.

 


 

State

State, assim como as props, são dados utilizados pelo componente. Novamente, pode ser uma string, um objeto, um array, um número. A diferença, no caso do state, é que ao invés de receber a informação e somente utilizá-la, o state é privado e completamente controlado pelo componente. Para utilizarmos o state, precisamos primeiro conhecer a segunda forma de criar um componente, que são os componentes de classe (em contraste aos componentes funcionais, ou de função, que temos criado até o momento). Primeiro, para criar um componente de classe, devemos extender a classe React.Component. Convertendo nosso componente funcional App.js para um componente de classe, ele fica assim:

  • App.jsx
import React, { Component } from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;
class App extends Component {
constructor(props) {
super(props);
this.state = { currentCount:7, name:”HjrDev” };
}
render() {
return (
<h1>React Basic</h1>
currentCount={this.state.currentCount}
name={this.state.name}
/>
);
}
}
export default App;

Agora, para definir o state deste componente, devemos adicionar um constructor para esta classe, e dentro dela utilizar this.state para definir um objeto que terá os valores iniciais do state:

Vejam agora a mudança para uma classe Stateful, isso devido estar usando constructor, this.state e agora serve para poder utilizar as funções de lifeCycle, que veremos em outro post, para poder detalhar muito bem esta questão porque e muito importante saber.

 

Agora, vamos entender como manipular os valores no state. Primeiro, criamos dois botões, um para adicionar e outro para subtrair. Vamos usar o elemento button, fazendo com que seu arquivo App.jsx fique dessa forma:

  • App.jsx
import React, { Component } from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;
class App extends Component {
constructor(props) {
super(props);
this.state = { currentCount:7, name:”HjrDev” };
}
render() {
return (
<h1>React Basic</h1>

<button>+</button>
<button></button>


currentCount={this.state.currentCount}
/>

</div>
);
}
}
export default App;

Agora, existe um atributo no elemento button chamado onClick. Com este atributo, podemos definir o que queremos que aconteça ao clicar em um botão. Vamos usar este atributo para executar uma função que irá atualizar o nosso state, aumentando ou decrescendo o valor por 1 dependendo do botão pressionado. Primeiro, vamos criar métodos de classe para acrescer e decrescer, e então associar estes métodos com cada botão.

Para atualizar o state deste componente, usamos this.setState, sendo this referente à instância do componente. Passamos a este método um objeto, com a chave que desejamos atualizar e o novo valor do mesmo. Neste caso, usaremos o valor atual da contagem, mais ou menos 1:

  • App.jsx
import React, { Component } from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;
class App extends Component {
constructor(props) {
super(props);
this.state = { currentCount: 1 };
}
handleIncrement(event) {
this.setState({currentCount: this.state.currentCount + 1});
}handleDecrement(event) {
this.setState({currentCount: this.state.currentCount – 1});
}
render() {
return (
<h1>React Basic</h1>
<button onClick={this.handleIncrement.bind(this)}> + </button>
<button onClick={this.handleDecrement.bind(this)}> – </button>
</div>
);
}
}
export default App;

Repare que após a função, utilizamos bind(this). Isto é necessário para que, dentro da função possamos usar o this, se referindo ao componente. Caso contrário, o this será undefined dentro da função e o setState não funcionará. Existem outras formas de fazer isso. A primeira é fazer o bind no constructor como no texto abaixo:

 

  • App.jsx
import React, { Component } from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;
class App extends Component {
constructor(props) {
super(props);
this.state = { currentCount: 1 };
this.handleIncrement = this.handleIncrement.bind(this);
this.handleDecrement = this.handleDecrement.bind(this);
}
handleIncrement(event) {
this.setState({currentCount: this.state.currentCount + 1});
}handleDecrement(event) {
this.setState({currentCount: this.state.currentCount – 1});
}
render() {
return (
<h1>React Basic</h1>

<button onClick={this.handleIncrement.bind(this)}> + </button><button onClick={this.handleDecrement.bind(this)}> – </button>

</div>
);
}
}
export default App;

E ainda há outra forma, que é usar arrow functions. Neste caso, você não tem que fazer bind, porque a arrow function já faz isso automaticamente. Fica desta forma:

  • App.jsx
import React, { Component } from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;
class App extends Component {
constructor(props) {
super(props);
this.state = { currentCount: 1 };
this.handleIncrement = this.handleIncrement.bind(this);
this.handleDecrement = this.handleDecrement.bind(this);
}
handleIncrement(event) {
this.setState({currentCount: this.state.currentCount + 1});
}handleDecrement(event) {
this.setState({currentCount: this.state.currentCount – 1});
}
render() {
return (
<h1>React Basic</h1>

<button onClick={this.handleIncrement.bind(this)}> + </button>
<button onClick={this.handleDecrement.bind(this)}> – </button>

</div>
);
}
}
export default App;

 

E ainda há outra forma, que é usar arrow functions. Neste caso, você não tem que fazer bind, porque a arrow function já faz isso automaticamente. Fica desta forma:

  • App.jsx
import React, { Component } from “react”;
import ReactDOM from “react-dom”;
import CountDisplay from “./CountDisplay”;
class App extends Component {
constructor(props) {
super(props);
this.state = { currentCount: 1 };
}
handleIncrement(event) {
this.setState({currentCount: this.state.currentCount + 1});
}handleDecrement(event) {
this.setState({currentCount: this.state.currentCount – 1});
}
render() {
return (
<h1>React Basic</h1>

<button onClick={ () => this.handleIncrement() }> + </button>
<button onClick={ () => this.handleDecrement() }> – </button>

</div>
);
}
}
export default App;

Todas estas formas funcionam e atingem o mesmo resultado. Você pode usar aquela que lhe parecer mais legível, mais clara. Prefiro usar as arrow functions, porque além de escrever menos e mais atual no ECMA E6

 

Conclusão

Para resumir um pouco tudo que vimos, seguem alguns pontos:

  • Existem duas formas de criar componentes, a forma funcional que é chamada de Stateless que e sem estado e componentes de classes que é chamada de Stateful;
  • Props são dados passados a um componente para serem usados por ele
  • State são dados a serem usados pelo componente, privados e controlados pelo próprio
  • Apenas componentes de classes podem utilizar o state no React
  • Você pode passar funções através das props ao organizar sua aplicação em componentes

Bem, com este post espero ter ajudado a esclarecer um pouco os conceitos sobre componentes, state e props no React. E para lembrar aos Devs que passam por aqui, este post é apenas para reforçar o estudo e poder fixar melhor meu aprendizado no ecossistema do JS.

 

vamos agora escutar uma boa música, ao som de Destruction – Infernal Overkill

 

código no github

 

 

sources: