Mudanças entre as edições de "React.js: CRUD Rest"

De Aulas
 
(2 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
 
 
 
  
  
Linha 17: Linha 14:
 
= Criação do Projeto =
 
= Criação do Projeto =
  
Tendo definido o serviço web que iremos consumir, agora vamos para a criação do nosso projeto. Nesse ponto, já temos instalado o npm, node e create-react-app, então vamos criar a pasta do projeto:
+
Tendo definido o serviço web que iremos consumir, agora vamos para a criação do nosso projeto. Vamos criar a pasta do projeto com o vite:
  
  $ npx create-react-app reactcrud
+
  $ npm create vite@latest reactcrud -- --template react
  
 
Também vamos precisar do '''Axios''', então já vamos instalar ele. Para isso, entre dentro da pasta que do projeto que criamos e baixe o axios:
 
Também vamos precisar do '''Axios''', então já vamos instalar ele. Para isso, entre dentro da pasta que do projeto que criamos e baixe o axios:
  
 
  $ cd reactcrud
 
  $ cd reactcrud
  $ npm install --save axios
+
  $ npm install
 +
$ npm run dev
  
 
Depois disso, vamos alterar alguns arquivos.  
 
Depois disso, vamos alterar alguns arquivos.  
Linha 30: Linha 28:
 
== index.css ==
 
== index.css ==
  
Para que a tabela fique bonitinha, vamos adicionar um CSS no arquivo <code>index.css</code>
+
Para que a tabela fique bonitinha, vamos alterar o CSS no arquivo <code>App.css</code> e comentar o index.css do index.jsx.
  
 
<syntaxhighlight lang=css>
 
<syntaxhighlight lang=css>
Linha 58: Linha 56:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== App.js ==
+
== App.jsx ==
  
 
E então vamos para o arquivo principal da aplicação
 
E então vamos para o arquivo principal da aplicação
Linha 68: Linha 66:
  
 
/*
 
/*
* Abaixo temos algumas configurações globais do axios. É uma
+
  Abaixo temos algumas configurações globais do axios. É uma
* outra forma de iniciar algumas coisas que serão usadas por
+
  outra forma de iniciar algumas coisas que serão usadas por
* todos os endpoints, tais como: link do serviço principal,
+
  todos os endpoints, tais como: link do serviço principal,
* o usuário e a chave de acesso ao serviço.
+
  o usuário e a chave de acesso ao serviço.
* Nesse exemplo estamos acessando o localhost, mas tenho uma
+
  Nesse exemplo estamos acessando o localhost, mas tenho uma
* api para testes no link: https://api.arisa.com.br
+
  api para testes no link: https://api.arisa.com.br
*/
+
*/
 +
 
 
axios.defaults.baseURL = 'http://localhost:8080';
 
axios.defaults.baseURL = 'http://localhost:8080';
 
axios.defaults.headers.common['X-User'] = 'fulano';
 
axios.defaults.headers.common['X-User'] = 'fulano';
Linha 81: Linha 80:
 
class App extends Component {
 
class App extends Component {
 
   /*
 
   /*
  * No constructor inicializamos nossa herança (Component) e
+
    No constructor inicializamos nossa herança (Component) e
  * instanciamos nossos estados, sendo a lista de usuários, o
+
    instanciamos nossos estados, sendo a lista de usuários, o
  * userData com as informações id, nome e email referente aos
+
    userData com as informações id, nome e email referente aos
  * campos de entrada. E o disabled, que é para desabilitarmos
+
    campos de entrada. E o disabled, que é para desabilitarmos
  * o campo ID quano formos alterar algum registro
+
    o campo ID quando formos alterar algum registro
  */
+
  */
 
   constructor(props) {
 
   constructor(props) {
 
     super(props);
 
     super(props);
Linha 97: Linha 96:
  
 
   /*
 
   /*
  * Esse método é chamado depois que nossa aplicação tiver sido criada.
+
    Esse método é chamado depois que nossa aplicação tiver sido criada.
  * Ele apenas faz chama o método fetchUsers para carregar os dados da
+
    Ele apenas faz chama o método fetchUsers para carregar os dados da
  * base de dados via serviço web.
+
    base de dados via serviço web.
  */
+
  */
 
   componentDidMount() {
 
   componentDidMount() {
 
     this.fetchUsers();
 
     this.fetchUsers();
 
   }
 
   }
  
   /**
+
   /*
  * Aqui usamos nosso GET sem parâmetro para retornar toda a lista
+
    Aqui usamos o método GET sem passar um ID ou parâmetro específico.
  * de registros, e ao executar o setState, a tabela é atualizada
+
    Isso depende da API estar preparada para retornar todos os usuários
  * com as novas informações.
+
    nesse endpoint (no caso: GET /users).
  */
+
  */
 
   fetchUsers = async () => {
 
   fetchUsers = async () => {
 
     try {
 
     try {
Linha 120: Linha 119:
  
 
   /*
 
   /*
  * Esse evento é chamado sempre que alterarmos alguma informação
+
    Esse evento é chamado sempre que alterarmos alguma informação
  * nos campos de entrada, de forma a atualizar também os estados
+
    nos campos de entrada, de forma a atualizar também os estados
  */
+
  */
 
   handleChange = (event) => {
 
   handleChange = (event) => {
 
     const { name, value } = event.target;
 
     const { name, value } = event.target;
     /**
+
     /*
    * Se o campo for "id" gente converte para número.
+
      Se o campo for "id" gente converte para número.
    * O ...prevState.userData atualiza os campos com o estado
+
      O ...prevState.userData atualiza os campos com o estado
    * anterior.
+
      anterior.
    * Depois só modificamos no state a informação que foi
+
      Depois só modificamos no state a informação que foi
    * alterada no campo de entrada.  
+
      alterada no campo de entrada.  
    */
+
    */
 
     this.setState((prevState) => ({
 
     this.setState((prevState) => ({
 
       userData: {
 
       userData: {
 
         ...prevState.userData,
 
         ...prevState.userData,
         [name]: name === 'id' ? Number(value) : value,
+
         [name]: name === 'id' ? (value === '' ? '' : Number(value)) : value,
 
       }
 
       }
 
     }));
 
     }));
Linha 141: Linha 140:
  
 
   /*
 
   /*
  * Quando o botão gravar é clicado, o handleSubmit é chamado para
+
    Quando o botão gravar é clicado, o handleSubmit é chamado para
  * tratar o evento.
+
    tratar o evento.
  */
+
  */
 
   handleSubmit = async (event) => {
 
   handleSubmit = async (event) => {
 
     event.preventDefault();
 
     event.preventDefault();
Linha 160: Linha 159:
 
   };
 
   };
  
   /**
+
   /*
  * O load user pega as informações do elemento da lista referente
+
    O load user pega as informações do elemento da lista referente
  * à linha da tabela que clicamos e carrega as informações nos campos
+
    à linha da tabela que clicamos e carrega as informações nos campos
  * texto. Isso ocorre porque usamos o setState que faz essa sincronização.  
+
    texto. Isso ocorre porque usamos o setState que faz essa sincronização.  
  */
+
  */
 
   loadUser = (user) => {
 
   loadUser = (user) => {
 
     this.setState({
 
     this.setState({
Linha 172: Linha 171:
 
   };
 
   };
  
   /**
+
   // Exclui um registro pelo "id" e recarrega os elementos da tabela.
  * Exclui um registro pelo "id" e recarrega os elementos da tabela.
 
  */
 
 
   deleteUser = async (id) => {
 
   deleteUser = async (id) => {
 
     try {
 
     try {
Linha 185: Linha 182:
 
   };
 
   };
  
   /**
+
   // Reseta as informações do formulário ao limpar o state userData
  * Reseta as informações do formulário ao limpar o state userData
 
  */
 
 
   resetForm = () => {
 
   resetForm = () => {
 
     this.setState({
 
     this.setState({
Linha 195: Linha 190:
 
   };
 
   };
  
   /**
+
   // O render é onde criamos/renderizamos nossa página/aplicação.
  * O render é onde criamos/renderizamos nossa página/aplicação.
 
  */
 
 
   render() {
 
   render() {
 
     // Linkamos nossos estados localmente na função
 
     // Linkamos nossos estados localmente na função
Linha 257: Linha 250:
 
                 <td><a href={`mailto:${user.email}`}>{user.email}</a></td>
 
                 <td><a href={`mailto:${user.email}`}>{user.email}</a></td>
 
                 <td>
 
                 <td>
                   <button type="button" onClick={() => this.deleteUser(user.id)}>Excluir</button>
+
                   <button type="button"
 +
                    onClick={() => this.deleteUser(user.id)}>Excluir</button>
 
                 </td>
 
                 </td>
 
               </tr>
 
               </tr>

Edição atual tal como às 13h34min de 23 de abril de 2025


Afluentes: Usabilidade, Desenvolvimento Web, Mobile e Jogos, Desenvolvimento Front-end II

Serviço web

Para que nosso CRUD possa ser funcional, precisamos de um serviço web com as operações de CRUD (CREATE, READ, UPDATE e DELETE). Para o nosso exemplo aqui, usarei um serviço web desenvolvido em linguagem de programação GO:

Caso você queira reimplementar o serviço em Node.js ou outra linguagem/framework, basta entender o manual de acesso ao serviço web:

Criação do Projeto

Tendo definido o serviço web que iremos consumir, agora vamos para a criação do nosso projeto. Vamos criar a pasta do projeto com o vite:

$ npm create vite@latest reactcrud -- --template react

Também vamos precisar do Axios, então já vamos instalar ele. Para isso, entre dentro da pasta que do projeto que criamos e baixe o axios:

$ cd reactcrud
$ npm install
$ npm run dev

Depois disso, vamos alterar alguns arquivos.

index.css

Para que a tabela fique bonitinha, vamos alterar o CSS no arquivo App.css e comentar o index.css do index.jsx.

table {
  border-collapse: collapse;
  width: 100%;
}

th,
td {
  text-align: left;
  padding: 8px;
}

tr:nth-child(even) {
  background-color: #ddddff
}

tr:hover {
  background-color: #bbbbff;
}

th {
  background-color: #04AA6D;
  color: white;
}

App.jsx

E então vamos para o arquivo principal da aplicação

import React, { Component } from 'react';
import axios from 'axios';
import './App.css';

/*
  Abaixo temos algumas configurações globais do axios. É uma
  outra forma de iniciar algumas coisas que serão usadas por
  todos os endpoints, tais como: link do serviço principal,
  o usuário e a chave de acesso ao serviço.
  Nesse exemplo estamos acessando o localhost, mas tenho uma
  api para testes no link: https://api.arisa.com.br
*/

axios.defaults.baseURL = 'http://localhost:8080';
axios.defaults.headers.common['X-User'] = 'fulano';
axios.defaults.headers.common['X-API-KEY'] = '12345';

class App extends Component {
  /*
    No constructor inicializamos nossa herança (Component) e
    instanciamos nossos estados, sendo a lista de usuários, o
    userData com as informações id, nome e email referente aos
    campos de entrada. E o disabled, que é para desabilitarmos
    o campo ID quando formos alterar algum registro
  */
  constructor(props) {
    super(props);
    this.state = {
      users: [],
      userData: { id: '', name: '', email: '' },
      isDisabled: false,
    };
  }

  /*
    Esse método é chamado depois que nossa aplicação tiver sido criada.
    Ele apenas faz chama o método fetchUsers para carregar os dados da
    base de dados via serviço web.
  */
  componentDidMount() {
    this.fetchUsers();
  }

  /*
    Aqui usamos o método GET sem passar um ID ou parâmetro específico.
    Isso depende da API estar preparada para retornar todos os usuários
    nesse endpoint (no caso: GET /users).
  */
  fetchUsers = async () => {
    try {
      const response = await axios.get('/users');
      this.setState({ users: response.data });
    } catch (error) {
      console.error("Erro ao buscar usuários:", error);
    }
  };

  /*
    Esse evento é chamado sempre que alterarmos alguma informação
    nos campos de entrada, de forma a atualizar também os estados
  */
  handleChange = (event) => {
    const { name, value } = event.target;
    /*
      Se o campo for "id" gente converte para número.
      O ...prevState.userData atualiza os campos com o estado
      anterior.
      Depois só modificamos no state a informação que foi
      alterada no campo de entrada. 
    */
    this.setState((prevState) => ({
      userData: {
        ...prevState.userData,
        [name]: name === 'id' ? (value === '' ? '' : Number(value)) : value,
      }
    }));
  };

  /*
    Quando o botão gravar é clicado, o handleSubmit é chamado para
    tratar o evento.
  */
  handleSubmit = async (event) => {
    event.preventDefault();
    const { userData, isDisabled } = this.state;
    try {
      if (isDisabled) {
        await axios.put('/users', userData);
      } else {
        await axios.post('/users', userData);
      }
      this.resetForm();
      this.fetchUsers();
    } catch (error) {
      console.error("Erro ao salvar usuário:", error);
    }
  };

  /*
    O load user pega as informações do elemento da lista referente
    à linha da tabela que clicamos e carrega as informações nos campos
    texto. Isso ocorre porque usamos o setState que faz essa sincronização. 
  */
  loadUser = (user) => {
    this.setState({
      userData: { id: user.id, name: user.name, email: user.email },
      isDisabled: true,
    });
  };

  // Exclui um registro pelo "id" e recarrega os elementos da tabela.
  deleteUser = async (id) => {
    try {
      await axios.delete(`/users/${id}`);
      this.fetchUsers();
      this.resetForm();
    } catch (error) {
      console.error("Erro ao deletar usuário:", error);
    }
  };

  // Reseta as informações do formulário ao limpar o state userData
  resetForm = () => {
    this.setState({
      userData: { id: '', name: '', email: '' },
      isDisabled: false,
    });
  };

  // O render é onde criamos/renderizamos nossa página/aplicação.
  render() {
    // Linkamos nossos estados localmente na função
    const { users, userData, isDisabled } = this.state;

    return (
      <div>
        <h1>Usuários</h1>
        <form onSubmit={this.handleSubmit}>
          <p>
            <label>
              ID:
              <input
                type="text"
                name="id"
                value={userData.id}
                onChange={this.handleChange}
                disabled={isDisabled}
              />
            </label>
          </p>
          <p>
            <label>
              Nome:
              <input
                type="text"
                name="name"
                value={userData.name}
                onChange={this.handleChange}
              />
            </label>
          </p>
          <p>
            <label>
              E-mail:
              <input
                type="email"
                name="email"
                value={userData.email}
                onChange={this.handleChange}
              />
            </label>
          </p>
          <p>
            <button type="submit">Gravar</button>
            <button type="button" onClick={this.resetForm}>Limpar</button>
          </p>
        </form>
        <table>
          <thead>
            <tr><th>ID</th><th>Nome</th><th>E-mail</th><th>Ações</th></tr>
          </thead>
          <tbody>
            {/* Executamos nosso iteractor para ler cada linha da tabela */}
            {users.map((user) => (
              <tr key={user.id}>
                <td>{user.id}</td>
                <td onClick={() => this.loadUser(user)}>{user.name}</td>
                <td><a href={`mailto:${user.email}`}>{user.email}</a></td>
                <td>
                  <button type="button"
                    onClick={() => this.deleteUser(user.id)}>Excluir</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }
}

export default App;