Mudanças entre as edições de "React.js: CRUD Rest"
(→App.js) |
(→App.js) |
||
(6 revisões intermediárias pelo mesmo usuário não estão sendo mostradas) | |||
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 6: | Linha 10: | ||
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: | 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 - | + | * [[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: | Caso você queira reimplementar o serviço em Node.js ou outra linguagem/framework, basta entender o manual de acesso ao serviço web: | ||
Linha 58: | Linha 62: | ||
E então vamos para o arquivo principal da aplicação | E então vamos para o arquivo principal da aplicação | ||
− | <syntaxhighlight lang= | + | <syntaxhighlight lang="javascript"> |
import React, { Component } from 'react'; | import React, { Component } from 'react'; | ||
import axios from 'axios'; | import axios from 'axios'; | ||
import './App.css'; | 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 { | 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) { | constructor(props) { | ||
super(props); | super(props); | ||
this.state = { | this.state = { | ||
users: [], | 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() { | render() { | ||
− | + | // Linkamos nossos estados localmente na função | |
− | const { | + | const { users, userData, isDisabled } = this.state; |
− | |||
− | |||
return ( | return ( | ||
Linha 89: | Linha 207: | ||
<form onSubmit={this.handleSubmit}> | <form onSubmit={this.handleSubmit}> | ||
<p> | <p> | ||
− | <label>ID: | + | <label> |
− | <input type= | + | ID: |
− | name= | + | <input |
− | value={ | + | type="text" |
− | onChange={this. | + | name="id" |
− | disabled={ | + | value={userData.id} |
+ | onChange={this.handleChange} | ||
+ | disabled={isDisabled} | ||
/> | /> | ||
</label> | </label> | ||
</p> | </p> | ||
<p> | <p> | ||
− | <label> | + | <label> |
− | <input type= | + | Nome: |
− | name= | + | <input |
− | value={ | + | type="text" |
− | onChange={this. | + | name="name" |
+ | value={userData.name} | ||
+ | onChange={this.handleChange} | ||
/> | /> | ||
</label> | </label> | ||
</p> | </p> | ||
<p> | <p> | ||
− | <label>E-mail: | + | <label> |
− | <input type= | + | E-mail: |
− | name= | + | <input |
− | value={ | + | type="email" |
− | onChange={this. | + | name="email" |
+ | value={userData.email} | ||
+ | onChange={this.handleChange} | ||
/> | /> | ||
</label> | </label> | ||
</p> | </p> | ||
<p> | <p> | ||
− | <button>Gravar</button> | + | <button type="submit">Gravar</button> |
− | <button type= | + | <button type="button" onClick={this.resetForm}>Limpar</button> |
</p> | </p> | ||
</form> | </form> | ||
<table> | <table> | ||
<thead> | <thead> | ||
− | <tr><th>ID</th><th>Nome</th><th>E-mail</th><th></th></tr> | + | <tr><th>ID</th><th>Nome</th><th>E-mail</th><th>Ações</th></tr> |
</thead> | </thead> | ||
<tbody> | <tbody> | ||
− | {users.map(user => ( | + | {/* Executamos nosso iteractor para ler cada linha da tabela */} |
+ | {users.map((user) => ( | ||
<tr key={user.id}> | <tr key={user.id}> | ||
<td>{user.id}</td> | <td>{user.id}</td> | ||
− | <td onClick={() => this. | + | <td onClick={() => this.loadUser(user)}>{user.name}</td> |
− | <td><a href={ | + | <td><a href={`mailto:${user.email}`}>{user.email}</a></td> |
− | <td><button type= | + | <td> |
+ | <button type="button" onClick={() => this.deleteUser(user.id)}>Excluir</button> | ||
+ | </td> | ||
</tr> | </tr> | ||
))} | ))} | ||
Linha 138: | Linha 265: | ||
</div> | </div> | ||
); | ); | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
} | } |
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;