E chegamos à terceira 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 segunda parte, fizemos funcionar o cadastro de itens na nossa lista de compras e estruturamos a listagem de itens, mas ainda de maneira fixa. Nosso app de lista de compras do supermercado está começando a tomar uma forma interessante!
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.
#7 – Refatorando o acesso a dados
Agora que nossa aplicação está começando a ficar um pouco maior e o código mais complexo, vamos separar a lógica de acesso a dados do restante da aplicação.
Crie um arquivo Database.js e dentro dele vamos colocar toda a lógica de acesso a dados do nosso CRUD. A começar pela única função que lida com dados atualmente, a saveItem.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import AsyncStorage from ‘@react-native-async-storage/async-storage’;
async function saveItem(listItem){
listItem.id = new Date().getTime();
let savedItems = [];
const response = await AsyncStorage.getItem(‘items’);
if(response) savedItems = JSON.parse(response);
savedItems.push(listItem);
return AsyncStorage.setItem(‘items’, JSON.stringify(savedItems));
}
module.exports = {
saveItem
}
|
Por enquanto, teremos apenas esta function, mas mais tarde, teremos as outras operações. Nela, recebemos o objeto cru e fazemos toda a lógica necessária para salvá-lo corretamente.
Fora desse arquivo, no AppForm.js, vamos primeiro importar este módulo que acabamos de criar (última linha abaixo).
1
2
3
4
|
import React, {useState, useEffect} from ‘react’;
import { StatusBar } from ‘expo-status-bar’;
import { StyleSheet, Text, View, TextInput, TouchableOpacity } from ‘react-native’;
import Database from ‘./Database’;
|
E depois modificar a parte que usava esse código para que ela apenas lide com os estados e com a navegação, responsabilidades da interface.
1
2
3
4
5
|
async function handleButtonPress(){
const listItem = {descricao, quantidade: parseInt(quantidade)};
Database.saveItem(listItem)
.then(response => navigation.navigate(“AppList”, listItem));
}
|
Mais tarde, faremos estes mesmos passos novamente, para outras funções que lidam com o AsyncStorage.
#8 – Retornando os itens do banco
Estamos chegando em mais uma letra do CRUD, o R de retrieve. Nosso desafio agora é retornar os dados salvos pela tela de cadastro para popular a nossa lista, tanto quando abrimos o app pela primeira vez, quanto quando um dado é cadastrado, atualizado ou excluído.
Vamos no nosso módulo Database.js para adicionar mais uma function.
1
2
3
4
5
6
7
8
9
|
function getItems(){
return AsyncStorage.getItem(‘items’)
.then(response => {
if(response)
return Promise.resolve(JSON.parse(response));
else
return Promise.resolve([]);
})
}
|
Basicamente ela pega todos os itens do AsyncStorage e retorna em forma de array através de uma promise. Não esqueça de adicionar esta nova function no module.exports.
1
2
3
4
|
module.exports = {
saveItem,
getItems
}
|
Para usar função, vamos usar o useEffect e o Database, que primeiramente eu importo abaixo no arquivo AppList.
1
2
3
4
5
|
import { StatusBar } from ‘expo-status-bar’;
import React, {useState, useEffect} from ‘react’;
import { StyleSheet, View, Text, ScrollView } from ‘react-native’;
import AppItem from ‘./AppItem’;
import Database from ‘./Database’;
|
Para usar na sequência logo mais. Mas antes disso, altere a assinatura da função para que decomponha dois objetos: route e navigation.
1
|
export default function AppList({ route, navigation }) {
|
O navigation usaremos mais tarde para trocar de tela. O route, são os parâmetros de navegação que trouxeram até esta tela. Isso ficará mais claro mais tarde também.
No código abaixo, eu removi os itens que havia adicionado manualmente só para que o app tivesse dados para exibir.
1
2
3
4
5
|
const [items, setItems] = useState([]);
useEffect(() => {
Database.getItems().then(items => setItems(items));
}, [route]);
|
O useEffect irá ser disparado toda vez que a nossa variável route seja alterada, ou seja, toda vez que entrar nesta tela vindo de outra.
E, se você testar agora, verá que já está funcionando. Pode testar usando o simulador/emulador se preferir.
R do CRUD? Check!
#9 – Selecionando um item
A próxima letra do nosso CRUD é a U, de Update. Ou seja, temos de fazer com que os pequenos botões de Editar que ficam ao lado de cada item da nossa lista funcione.
Aqui temos dois comportamentos a serem programados: o de redirecionamento a partir da seleção de um item da lista e o de salvamento inteligente (atualização x novo cadastro).
Vamos começar redirecionando o usuário no press do botão de Editar, que é o primeiro comportamento necessário.
Lembra que capturamos um objeto navigation dentro dos parâmetros da function AppList?
1
|
export default function AppList({ route, navigation }) {
|
Agora vamos usar este navigation aí, no mesmo arquivo. Note que tem uma propriedade navigation ali na tag AppItem também, que é para passarmos o objeto de navegação para dentro do AppItem.
1
|
return <AppItem key={item.id} id={item.id} item={item.quantidade + ‘ de ‘ + item.descricao} navigation={navigation} />
|
Agora, voltando ao Database.js, vamos criar a function que vai retornar apenas um item (não esqueça de adicionar ela no module.exports).
1
2
3
4
|
async function getItem(id){
const savedItems = await getItems();
return savedItems.find(item => item.id === id);
}
|
E no AppItem.js, vamos importar o Database.js.
1
2
3
|
import React from ‘react’;
import {StyleSheet, Text, View, TouchableOpacity, Alert} from ‘react-native’;
import Database from ‘./Database’;
|
E ainda no AppItem.js, vamos criar uma function que vai servir de handler para o press do botão de editar, usando esta outra function que acabamos de criar no Database.js, como abaixo.
1
2
3
4
|
async function handleEditPress(){
const item = await Database.getItem(props.id);
props.navigation.navigate(“AppForm”, item);
}
|
Nesta function, nós estamos pegando o item com o id que foi passado na propriedade id do AppItem e retornando o item que queremos (buscando por id) e enviando ele para o AppForm na navegação de tela.
E vamos associar esta function ao onPress do botão de edit neste mesmo arquivo.
1
2
3
4
5
|
<TouchableOpacity
style={styles.editButton}
onPress={handleEditPress}>
<Text style={styles.buttonText}>Editar</Text>
</TouchableOpacity>
|
Com isso, acontecerá a troca de tela ao clicar no botão editar, passando por parâmetro o item da lista de compras inteiro. Para poder capturar este item na AppForm.js, teremos de alterar a assinatura da function, como abaixo, incluindo o objeto route.
1
2
3
4
|
export default function AppForm({ route, navigation }) {
const id = route.params ? route.params.id : undefined;
const [descricao, setDescricao] = useState(”);
const [quantidade, setQuantidade] = useState(”);
|
O objeto route ali possui uma propriedade params que justamente vai ter dentro dele o objeto que passamos com todos os dados do item da lista que clicamos em Editar. Um destes valores, o id, eu vou armazenar diretamente em uma variável local. Isso porque ele não é um dado que necessite ser exibido ao usuário.
Agora, logo abaixo do trecho de código acima, vamos adicionar um efeito que vai ser disparado toda vez que o objeto route for modificado.
1
2
3
4
5
|
useEffect(() => {
if(!route.params) return;
setDescricao(route.params.descricao);
setQuantidade(route.params.quantidade.toString());
}, [route])
|
Assim, a cada nova navegação realizada (ou seja, route com params diferentes), nós pegamos os parâmetros do route para setar o estado da descrição e da quantidade.
Não esqueça de revisar os seus imports, para que estejam como abaixo.
1
2
3
4
|
import React, {useState, useEffect} from ‘react’;
import { StatusBar } from ‘expo-status-bar’;
import { StyleSheet, Text, View, TextInput, TouchableOpacity } from ‘react-native’;
import Database from ‘./Database’;
|
O próximo passo então, é exibir estes dados na tela AppForm.js, em seus respectivos campos. Para fazer isso, basta adicionar a propriedade value em cada um deles referenciando a variável de estado, como abaixo.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<TextInput
style={styles.input}
onChangeText={handleDescriptionChange}
placeholder=“O que está faltando em casa?”
clearButtonMode=“always”
value={descricao} />
<TextInput
style={styles.input}
onChangeText={handleQuantityChange}
placeholder=“Digite a quantidade”
keyboardType={‘numeric’}
clearButtonMode=“always”
value={quantidade.toString()} />
|
Atenção ao fato de que todos TextInputs exigem strings na propriedade value, então tive de converter o valor retornado na quantidade.
Com esses ajustes, você já deve ter o comportamento esperado de, ao clicar no botão editar de um item da lista, abre-se a tela AppForm com os campos preenchidos.
Na próxima etapa da nossa série, vou lhe passar mais algumas dicas para aprimorar o seu app e vamos finalizar o nosso CRUD. Clique aqui para avançar comigo!
Até lá!