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

De Aulas
(Criou página com ' Afluentes')
 
 
(12 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 1: Linha 1:
  
Afluentes
+
 
 +
 
 +
 
 +
 
 +
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:
 +
 
 +
* [[Go: RESTful - exemplo com persistência usando gorm]]
 +
 
 +
Caso você queira reimplementar o serviço em Node.js ou outra linguagem/framework, basta entender o manual de acesso ao serviço web:
 +
* [[Go: RESTful - um exemplo completo com persistência#Manual de Utilização]]
 +
 
 +
= 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:
 +
 
 +
$ npx create-react-app reactcrud
 +
 
 +
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 --save axios
 +
 
 +
Depois disso, vamos alterar alguns arquivos.
 +
 
 +
== index.css ==
 +
 
 +
Para que a tabela fique bonitinha, vamos adicionar um CSS no arquivo <code>index.css</code>
 +
 
 +
<syntaxhighlight lang=css>
 +
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;
 +
}
 +
</syntaxhighlight>
 +
 
 +
== App.js ==
 +
 
 +
E então vamos para o arquivo principal da aplicação
 +
 
 +
<syntaxhighlight lang="javascript">
 +
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 quano 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 nosso GET sem parâmetro para retornar toda a lista
 +
  * de registros, e ao executar o setState, a tabela é atualizada
 +
  * com as novas informações.
 +
  */
 +
  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' ? 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;
 +
</syntaxhighlight>

Edição atual tal como às 14h59min de 1 de novembro de 2024



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. Nesse ponto, já temos instalado o npm, node e create-react-app, então vamos criar a pasta do projeto:

$ npx create-react-app reactcrud

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 --save axios

Depois disso, vamos alterar alguns arquivos.

index.css

Para que a tabela fique bonitinha, vamos adicionar um CSS no arquivo index.css

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.js

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 quano 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 nosso GET sem parâmetro para retornar toda a lista
   * de registros, e ao executar o setState, a tabela é atualizada
   * com as novas informações.
   */
  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' ? 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;