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 14 15 |
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 6 7 |
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 16 17 |
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 4 5 |
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.
Agora que temos o alerta funcionando, vamos na Database.js criar a function de exclusão.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 20 21 |
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 5 6 |
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 11 12 |
<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 7 8 |
<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 10 11 |
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.
No final deste post você encontra um formulário onde deixa o seu email e recebe os fontes completos.
Quer aprender como ganhar dinheiro desenvolvendo apps? Leia este artigo aqui!
Um abraço e até a próxima!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
Nesta parte do tutorial, no passo 11, quando cheguei na parte de implementar o diálogo de confirmação de exclusão, simplesmente não funciona. Implantei, clico no botão de excluir e ele não manda nada pro console nem mostra a caixa de diálogo. O que pode estar acontecendo?
Pela sua descrição parece ser problema de programação no evento de click do botão.
Oi Luiz parabens pelos seus tutoriais de react native muito bons.
Se voce puder me contactar eu gostaria de falar sobre umas necessidades que tenho na plataforma, seguem meus dados de contato
Boa tarde Flavio, tudo bem? Infelizmente não presto mais serviços de desenvolvimento a empresas, atuo apenas em meus próprios projetos tem alguns anos já.
Olá!
Percebi que, logo após fazer a edição de um Item, se tentar adicionar novo item não consegue, porque o “id” fica na memória. Como posso fazer para liberar e “id” e conseguir adicionar novo item?
Faz alguns anos que fiz esse tutorial, já não lembro todos os detalhes, então vou lhe passar duas dicas. A primeira é que baixe os fontes dele e rode para ver se tem o mesmo bug na minha versão, o formulário para baixar o repositório está ao final do tutorial. A segunda dica é para ver onde o id fica armazenado, pois se ele ficar em um state, basta limpar esse state após a edição, assim o cadastro de novo item deve voltar a funcionar.