E chegamos à quarta e última parte da nossa série onde vou lhe ensinar como construir um app Android e iOS que faz um CRUD completo (cadastro, listagem, edição e exclusão de dados) usando React Native.
Na terceira parte, fizemos uma refatoração no nosso acesso à dados, fizemos funcionar a listagem e a seleção de um item para posterior edição, que de onde reiniciaremos no tutorial de hoje!
Atenção: esta é uma série intermediária. Caso nunca tenha feito um app em React Native antes, sugiro começar por esta outra série.
#10 – Atualizando um item
Agora que já temos os campos preenchidos na tela AppForm.js quando clicamos no botão editar da tela de listagem, vamos ajustar a lógica do botão Salvar para que ele identifique quando é uma edição ou novo cadastro, para fazer a operação necessária em nosso banco de dados.
Esta alteração é em dois pontos: no módulo Database.js e outra perna no AppForm.js. Primeiro vamos no módulo de acesso a dados.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
async function saveItem(listItem, id){
listItem.id = id ? id : new Date().getTime()
const savedItems = await getItems();
if(id){
const index = await savedItems.findIndex(item => item.id === id);
savedItems[index] = listItem;
}
else
savedItems.push(listItem);
return AsyncStorage.setItem(‘items’, JSON.stringify(savedItems));
}
|
Calma, vou explicar.
Primeiro, a gente continua recebendo o listItem que vai ser salvo, no entanto, verificamos se veio um id como segundo parâmetro na function, pois assim, o objeto já terá id e não precisaremos gerar um novo. Coloquei o id como segundo parâmetro porque ele é opcional e também para manter compatibilidade com o código já existente no app.
Segundo, depois de carregar todos os dados presentes no AsyncStorage com getItems, verificamos novamente se veio um id (ou seja, se é uma edição) para, neste caso, pegar o índice do elemento a ser atualizado e sobrescrevendo-o com o objeto que montamos no início da função.
Caso não tenha vindo id, a função segue fazendo o que já funcionava: adiciona o novo objeto no array.
Em ambos os casos, salva-se o array inteiro novamente no AsyncStorage.
Agora, o código abaixo vai na função do botão de Salvar da AppForm.js.
1
2
3
4
5
|
async function handleButtonPress(){
const listItem = {descricao, quantidade: parseInt(quantidade)};
Database.saveItem(listItem, id)
.then(response => navigation.navigate(“AppList”, listItem));
}
|
Note que esta function ficou bem simples, apenas com a responsabilidade de montar o objeto e chamar a função Database.saveItem passando os parâmetros (o id nós carregamos lááá no início da AppForm.js), disparando a navegação depois de concluído o salvamento.
Só obtivemos um código simples aqui pois encapsulamos toda a lógica de cadastro/atualização dentro do módulo Database. Essa segregação de responsabilidades torna o nosso código muito mais legível e fácil de dar manutenção depois.
A atualização já deve estar funcionando, pode testar sem medo. Se preferir, teste usando o emulador.
#11 – Excluindo um item
E finalmente vamos à quarta e última letra do CRUD, o D de Delete!
Para fazer isso, não vou simplesmente definir uma function que exclui o registro com determinado id no Async Storage, mas antes disso quero questionar o usuário se ele tem certeza da m**** que vai fazer.
Para exibir um popup de confirmação, adicione o seguinte código na function handleDeletePress do arquivo AppItem.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function handleDeletePress(){
Alert.alert(
“Atenção”,
“Você tem certeza que deseja excluir este item?”,
[
{
text: “Não”,
onPress: () => console.log(“Cancel Pressed”),
style: “cancel”
},
{ text: “Sim”, onPress: () => console.log(`${props.id} deleted`) }
],
{ cancelable: false }
);
}
|
Para que o código acima funcione, você vai ter de adicionar um import adicional também, para o componente Alert e tem de adicionar a função handleDeletePress no onPress do botão de exclusão.
1
2
3
|
import React from ‘react’;
import {StyleSheet, Text, View, TouchableOpacity, Alert} from ‘react-native’;
import Database from ‘./Database’;
|
Para quem já programou em outras plataformas móveis, vai sacar rapidinho o funcionamento do código anterior. Mas, mesmo que você nunca tenha programado Android, por exemplo, não é difícil de entender também.
A classe Alert possui uma function alert que serve para criar mensagens no estilo popup. O primeiro parâmetro desta função é o título do alerta, o segundo a mensagem e o terceiro é o array de botões para o usuário escolher uma opção. Cada botão tem um texto e uma função onPress própria.
Neste momento, apenas mandei imprimir no console para você ver se está funcionando até aqui. E no app, ao clicar no botão de excluir de um dos itens cadastrados, você verá o alerta abaixo.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
async function deleteItem(id){
let savedItems = await getItems();
const index = await savedItems.findIndex(item => item.id === id);
savedItems.splice(index, 1);
return AsyncStorage.setItem(‘items’, JSON.stringify(savedItems));
}
module.exports = {
saveItem,
getItems,
getItem,
deleteItem
}
|
Aqui a function é bem direta: pega todos os itens, procura o índice daquele que possui o id recebido por parâmetro, exclui esse elemento do array e salva tudo de novo.
Para usar esta função, vamos mudar levemente o código do nosso Alert na AppItem.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function handleDeletePress(){
Alert.alert(
“Atenção”,
“Você tem certeza que deseja excluir este item?”,
[
{
text: “Não”,
onPress: () => console.log(“Cancel Pressed”),
style: “cancel”
},
{ text: “Sim”, onPress: () => {
Database.deleteItem(props.id)
.then(response => props.navigation.navigate(“AppList”, {id: props.id}));
}
}
],
{ cancelable: false }
);
}
|
Com isso, ao clicar no botão de excluir de um dos itens da lista e confirmar a exclusão no alerta, irá disparar a função de exclusão da Database e depois de finalizada, executará a navegação para a tela de listagem novamente, passando o id que foi excluído na rota.
Isto finaliza o CRUD!
#12 – Adicionando ícones
Agora que finalizamos as funcionalidades principais, que tal fecharmos com um pequeno toque na interface, adicionando ícones aos botões de editar e excluir?
Para fazer isso, vamos importar um pacote que já vem no Expo por padrão com todos os ícones que podemos precisar na maioria dos apps dentro de uma lib @expo/vector-icons. Assim, volte na sua AppItem.js e adicione a importação abaixo do pacote de ícones Feather.
1
2
3
4
|
import React from ‘react’;
import {StyleSheet, Text, View, TouchableOpacity, Alert} from ‘react-native’;
import { Feather as Icon } from ‘@expo/vector-icons’;
import Database from ‘./Database’;
|
E agora, dentro dos seus botões, você poderá usar a tag Icon ao invés de Text, indicando qual o ícone da biblioteca Feather você vai querer.
1
2
3
4
5
6
7
8
9
10
|
<TouchableOpacity
style={styles.deleteButton}
onPress={handleDeletePress}>
<Icon name=“trash” color=“white” size={18} />
</TouchableOpacity>
<TouchableOpacity
style={styles.editButton}
onPress={handleEditPress}>
<Icon name=“edit” color=“white” size={18} />
</TouchableOpacity>
|
Além de indicar o ícone que você deseja no name, você pode definir a cor e o tamanho do ícone também. Para conhecer todos os ícones possíveis de todas as famílias disponíveis no Expo, visite este site.
O resultado você confere abaixo.
Opcionalmente, você pode alterar o botão de Salvar da tela AppForm.js para que, além do texto, exiba um ícone também. Antes e fazer isso, adicione o import aos ícones, como fizemos na AppItem.js. Somente depois, modifique o trecho de código do TouchableOpacity para ficar como abaixo.
1
2
3
4
5
6
|
<TouchableOpacity style={styles.button} onPress={handleButtonPress}>
<View style={styles.buttonContainer}>
<Icon name=“save” size={22} color=“white” />
<Text style={styles.buttonText}>Salvar</Text>
</View>
</TouchableOpacity>
|
Aqui temos uma View para organizar dois componentes com um alinhamento que vamos definir no estilo buttonContainer que ainda não criamos. Dentro desta view, um Icon como fizemos antes e o texto do botão.
Para que eles fiquem lado a lado e em tamanhos condizentes, temos de fazer dois ajustes nos estilos deste módulo. Um deles é um ajuste no estilo buttonText e o outro é a criação de um estilo novo chamado buttonContainer.
1
2
3
4
5
6
7
8
9
|
buttonContainer: {
flexDirection: “row”
},
buttonText: {
marginLeft: 10,
fontSize: 18,
color: ‘#fff’,
fontWeight: ‘bold’,
}
|
Esse estilo buttonContainer deve ser usado por uma View dentro do TouchableOpacity, envolvendo o Icon e o Text.
Com isso, sua AppForm.js no smartphone ou simulador deve ficar como abaixo.
E com isso finalizamos esta série de tutoriais. Espero que você tenha gostado de aprender comigo pois eu com certeza gostei de escrever estes artigos.
Um abraço e até a próxima!