Mudanças entre as edições de "React.js: CRUD Rest"
(Uma revisão intermediária pelo mesmo usuário não está sendo mostrada) | |||
Linha 1: | Linha 1: | ||
+ | |||
+ | |||
Afluentes: [[Usabilidade, Desenvolvimento Web, Mobile e Jogos]], [[Desenvolvimento Front-end II]] | Afluentes: [[Usabilidade, Desenvolvimento Web, Mobile e Jogos]], [[Desenvolvimento Front-end II]] | ||
Linha 12: | 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. | + | 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: | 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 | + | $ npm install |
+ | $ npm run dev | ||
Depois disso, vamos alterar alguns arquivos. | Depois disso, vamos alterar alguns arquivos. | ||
Linha 25: | Linha 28: | ||
== index.css == | == index.css == | ||
− | Para que a tabela fique bonitinha, vamos | + | 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 53: | Linha 56: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == App. | + | == 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 63: | Linha 66: | ||
/* | /* | ||
− | + | 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.baseURL = 'http://localhost:8080'; | ||
axios.defaults.headers.common['X-User'] = 'fulano'; | axios.defaults.headers.common['X-User'] = 'fulano'; | ||
Linha 76: | Linha 80: | ||
class App extends Component { | 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) { | constructor(props) { | ||
super(props); | super(props); | ||
Linha 92: | Linha 96: | ||
/* | /* | ||
− | + | 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() { | componentDidMount() { | ||
this.fetchUsers(); | 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 () => { | fetchUsers = async () => { | ||
try { | try { | ||
Linha 115: | Linha 119: | ||
/* | /* | ||
− | + | Esse evento é chamado sempre que alterarmos alguma informação | |
− | + | 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. | |
− | + | 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) => ({ | this.setState((prevState) => ({ | ||
userData: { | userData: { | ||
...prevState.userData, | ...prevState.userData, | ||
− | [name]: name === 'id' ? Number(value) : value, | + | [name]: name === 'id' ? (value === '' ? '' : Number(value)) : value, |
} | } | ||
})); | })); | ||
Linha 136: | Linha 140: | ||
/* | /* | ||
− | + | Quando o botão gravar é clicado, o handleSubmit é chamado para | |
− | + | tratar o evento. | |
− | + | */ | |
handleSubmit = async (event) => { | handleSubmit = async (event) => { | ||
event.preventDefault(); | event.preventDefault(); | ||
Linha 155: | Linha 159: | ||
}; | }; | ||
− | / | + | /* |
− | + | 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) => { | loadUser = (user) => { | ||
this.setState({ | this.setState({ | ||
Linha 167: | Linha 171: | ||
}; | }; | ||
− | / | + | // Exclui um registro pelo "id" e recarrega os elementos da tabela. |
− | |||
− | |||
deleteUser = async (id) => { | deleteUser = async (id) => { | ||
try { | try { | ||
Linha 180: | Linha 182: | ||
}; | }; | ||
− | / | + | // Reseta as informações do formulário ao limpar o state userData |
− | |||
− | |||
resetForm = () => { | resetForm = () => { | ||
this.setState({ | this.setState({ | ||
Linha 190: | Linha 190: | ||
}; | }; | ||
− | / | + | // 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 252: | 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;