Componentes do React Native
Boa pessoal, agora vamos avançar um pouco mais com o nosso desenvolvimento de aplicações. Aqui vamos trabalhar com mais alguns componentes e ver qual o processo necessário para utilizar e configurar cada um deles. Muito importante: Não deixem de praticar! Esse de longe é o componente principal para ganhar mais familiaridade com cada uma das etapas e utilização destes elementos!
1. Criando um novo projeto
Section titled “1. Criando um novo projeto”Esse primeiro passo é mais um recap. Vamos criar nosso projeto utilizando o template de JavaScript novamente. Para isso:
npx create-expo-app lanca-dados --template blankLegal, agora com nosso projeto criado vamos iniciar nossas edições! Vamos iniciar nosso servidor de desenvolvimento:
# Muda para o diretório da soluçãocd lanca-dados# Ativa o servidor de desenvolvimentonpx expo start2. Organizando o projeto em componentes
Section titled “2. Organizando o projeto em componentes”Um dos conceitos mais relevantes do desenvolvimento com componentes reutilizáveis é um dos pilares fundamentais e um dos grandes trunfos que impulsionam a popularidade e a eficiência do React e do React Native. Essa filosofia de desenvolvimento encoraja a criação de ‘blocos de construção’ de interface — como botões customizados, cards de informação, ou até seções inteiras de uma tela — que encapsulam tanto a sua aparência quanto o seu comportamento. Uma vez que um componente é desenvolvido e testado, ele pode ser facilmente importado e utilizado inúmeras vezes em diferentes partes da aplicação, ou mesmo em outros projetos. Isso não apenas reduz drasticamente a duplicação de código, promovendo uma base mais enxuta e organizada, mas também simplifica enormemente a manutenção: uma correção ou atualização feita em um componente se reflete automaticamente em todas as suas instâncias. O resultado direto é um ciclo de desenvolvimento mais ágil, interfaces mais consistentes e aplicações mais escaláveis e fáceis de evoluir.
Legal, depois desta afirmação sobre a importancia de utilizar componentes, vamos fazer isso! Até mesmo com a nossa tela! Primeiro vamos criar um diretório que vai guardar todas as nossas telas: o app. Ele vai ser muito importante para outro componente que vamos discultir logo mais.
Agora, vamos criar um componente que vai definir a tela inicial da nossa aplicação. Ela vai ser o ponto de entrada da nossa aplicação. Para isso, vamos criar dentro do diretório app que criamos, um arquivo chamado MainScreen.js.
import {Text,SafeAreaView,View} from 'react-native';
export function MainScreen(){ return ( <SafeAreaView> <Text>Ola Mundo Diferente!</Text> </SafeAreaView> );}Vamos verificar se nosso componente foi criado com sucesso, alterando o código do nosso arquivo App.js:
import { StatusBar } from 'expo-status-bar';import { StyleSheet, Text, View } from 'react-native';import { MainScreen } from './app/MainScreen';
export default function App() { return ( <MainScreen></MainScreen> );}
const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', },});Beleza, temos nossa tela carregada agora. Ela ainda não tem nada diferente do que utilizamos na nossa interação anterior. Vamos dar uma customizada em nossa tela. Para isso, vamos utilizar algumas mídias e mais alguns componentes. Primeiro vamos adicionar o diretório components dentro do diretório da nossa solução. Depois, vamos adicionar a pasta images dentro do diretório assets.
Com nossos diretórios criados, vamos agora criar nosso componente HeaderApp e FooterApp, dentro do diretório de componentes.
import { View, Text, Image, StyleSheet } from 'react-native';
export function HeaderApp() { return ( <View style={estilos.header}> <Text>Lançador D6</Text> </View> );}
const estilos = StyleSheet.create( { header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 8, backgroundColor: '#f8f8f8', height: 64 } });Ainda temos alguns ajustes que vamos precisar fazer dentro do nosso componente HeaderApp, mas primeiro, vamos ajustar o código de nossa aplicação. Para não termos que ficar chamando HeaderApp e FooterApp em todos os lugares que eles forem aparecer, vamos centralizar sua utilização dentro do componente principal, assim ele fica encarregado de ver a tela que será utilizada (por enquanto apenas uma) e fazer a renderização destes elementos.
Logo, nosso App.js fica assim:
import { StatusBar } from 'expo-status-bar';import { StyleSheet, Text, View, SafeAreaView} from 'react-native';import { MainScreen } from './app/MainScreen';import { HeaderApp } from './components/HeaderApp';
export default function App() { return ( <SafeAreaView style={{flex:1}}> <HeaderApp></HeaderApp> <MainScreen></MainScreen> </SafeAreaView> );}
const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', },});E vamos ajustar também o nosso MainScreen.js:
import { Text,View } from 'react-native';
export function MainScreen(){ return ( <View style={{flex:1, backgroundColor: '#fff',}}> <Text>Ola Mundo Diferente!</Text> </View> );}Beleza, temos agora nosso aplicativo funcionando com nosso Header e nossa Tela principal. Agora vamos dar uma melhorada em cada um deles. Vamos primeiro escolher qual imagem vamos utilizar de icone da nossa aplicação, que tal essa?
Vamos salvar essa imagem dentro do diretório assets/images, com o nome: reiDadoLogo.jpg. Agora vamos adicionar essa imagem dentro do nosso Header.
import { View, Text, Image, StyleSheet } from 'react-native';
export function HeaderApp() { return ( <View style={estilos.header}> <Image source={require('../assets/images/reiDadoLogo.jpg')} style={estilos.imagemLogo}/> <Text>Lançador D6</Text> </View> );}
const estilos = StyleSheet.create( { header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 8, backgroundColor: '#f8f8f8', height: 64 }, imagemLogo:{ height:48, width:48, contentFit: 'contain', padding:4, } });Boa! Agora temos nosso logo!!
Agora vamos fazer alguns ajustes no nosso Header:
import { View, Text, Image, StyleSheet } from 'react-native';
export function HeaderApp() { return ( <View style={estilos.header}> <Image source={require('../assets/images/reiDadoLogo.jpg')} style={estilos.imagemLogo}/> <Text style={estilos.headerText}>Lançador D6</Text> </View> );}
const estilos = StyleSheet.create( { header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 8, backgroundColor: '#f8f8f8', height: 64, backgroundColor: '#ef1111' }, imagemLogo:{ height:48, width:48, contentFit: 'contain', padding:4, borderRadius: 24, overflow:'hidden', }, headerText : { fontSize: 30, color: '#ececec', fontWeight: 'bold', } });Pronto, agora temos nosso Header! Vamos agora trabalhar nos componentes da nossa tela.
3. Componentes da Tela
Section titled “3. Componentes da Tela”Para os componentes de nossa tela, vamos adicionar os elementos para fazer um lançamento de dados quando um botão for acionado. Vamos adicionar as imagens para o dado.
Agora vamos ajustar nosso código:
import { Text,View,Image,StyleSheet,TouchableOpacity } from 'react-native';
export function MainScreen(){ return ( <View style={estilos.container}> <Image source={require('../assets/images/dados_00.png')} style={estilos.image}/> <TouchableOpacity style={estilos.botaoContainer}> <Text style={estilos.botaoTexto}>Rolar!</Text> </TouchableOpacity> </View> );}
const estilos = StyleSheet.create( { container:{ flex: 1, backgroundColor: '#ececec', justifyContent: 'space-evenly', alignContent: 'center', alignItems: 'center' }, image:{ height:'80%', width:'80%', resizeMode: 'contain', padding:4, borderRadius: '35%', overflow:'hidden', }, botaoContainer:{ backgroundColor: '#007bff', width: '80%', paddingVertical: 12, paddingHorizontal: 25, borderRadius: 8, elevation: 2, marginBottom: 15, }, botaoTexto:{ fontSize: 20, color: '#ececec', fontWeight: 'bold', textAlign: 'center', }, });Muitas coisas aqui:
- Criamos um
TouchableOpacitypara criar um elemento clicável. Assim, conseguimos ajustar seu formato da maneira que desejarmos.
Agora vamos ajustar a lógica para sortear nosso botão. As imagens que vamos utilizar:
E o código:
import { Text,View,Image,StyleSheet,TouchableOpacity } from 'react-native';import React, { useState } from 'react';
const imagensDados = { 0: require('../assets/images/dados_00.png'), 1: require('../assets/images/dados_01.png'), 2: require('../assets/images/dados_02.png'), 3: require('../assets/images/dados_03.png'), 4: require('../assets/images/dados_04.png'), 5: require('../assets/images/dados_05.png'), 6: require('../assets/images/dados_06.png'), };
export function MainScreen(){ // Estado para guardar o número do dado atualmente exibido. // Começa mostrando a face 1 do dado. const [faceAtualDado, setFaceAtualDado] = useState(0);
// Função para "rolar" o dado const rolarDado = () => { // Gera um número aleatório entre 1 e 6 const numeroSorteado = Math.floor(Math.random() * 6) + 1; setFaceAtualDado(numeroSorteado); }; return ( <View style={estilos.container}> <Image source={imagensDados[faceAtualDado]} style={estilos.image}/> <TouchableOpacity style={estilos.botaoContainer} onPress={rolarDado}> <Text style={estilos.botaoTexto}>Rolar!</Text> </TouchableOpacity> </View> );}
const estilos = StyleSheet.create( { container:{ flex: 1, backgroundColor: '#ececec', justifyContent: 'space-evenly', alignContent: 'center', alignItems: 'center' }, image:{ height:'80%', width:'80%', resizeMode: 'contain', padding:4, borderRadius: '35%', overflow:'hidden', }, botaoContainer:{ backgroundColor: '#007bff', width: '80%', paddingVertical: 12, paddingHorizontal: 25, borderRadius: 8, elevation: 2, marginBottom: 15, }, botaoTexto:{ fontSize: 20, color: '#ececec', fontWeight: 'bold', textAlign: 'center', }, });Pessoal, aqui acredito que faz sentindo avaliarmos um pouco mais o conceito principal que faz nossa aplicação funcionar. O que temos aqui é a função rolarDado(), que faz duas tarefas muito importantes para o nosso aplicativo:
- Sorteia um número aleatório entre 1 e 6, toda vez que ela é chamada;
- Chama o hook
useState, pela funçãosetFaceAtualDado(), que faz com que nossa aplicação seja atualizada.
O conceito da atualização aqui do ReactNative é o mesmo que do React em aplicações Web.
4. Compreendendo Hooks e outros Eventos
Section titled “4. Compreendendo Hooks e outros Eventos”“Aviso do Murilo aqui: essa nota vai ser longa.”
Pessoal a ideia desta seção é avaliarmos um pouco de onde vem esse negócio de Hooks e por que ele é tão importante no contexto de criação de uma aplicação. Primeiro vamos avaliar um pouco a nossa utilização do React (sim, Web, não o Native), para criação de soluções.
4.1 Componentes como Classes
Section titled “4.1 Componentes como Classes”Logo nas primeiras aplicações com React, os desenvolvedores utilizavam classes para representar seus componentes. Essa abordagem trazia algumas vantagens:
- Era possível armazenar estado dentro de cada objeto: cada instância de uma classe era capaz de armazenar valores dentro dela! O que permitia gerenciar valores que poderiam mudar ao longo do tempo dentro da própria classe.
- Existiam os chamados Métodos de Ciclo de Vida: os métodos de ciclo de vida são chamados de forma automática quando os eventos aconteciam com os componentes, como ele ser montado na tela (
componentDidMount) e outros.
Poxa, mas se possuíamos essas vantagens, por que não continuamos utilizando elas para criar nossas aplicações?
Porque eu falei apenas um lado desta moeda, ela com toda certeza tem mais um (esses aqui o Gemini me ajudou a sintetizar):
- Reutilização Lógica Statefult Difícil: Compartilhar lógica com estado entre componentes era complicado. Padrões como Higher-Order Components (HOCs) e Render Props ajudavam, mas podiam levar ao que era conhecido como “Wrapper Hell” (um aninhamento excessivo de componentes no React DevTools, dificultando a depuração e a compreensão da árvore de componentes). Esses padrões muitas vezes alteravam a estrutura da árvore de componentes, o que não era ideal apenas para reutilizar comportamento.
- Lógica Espalhada em Métodos de Ciclo de Vida: Código relacionado a uma única funcionalidade (como buscar dados) frequentemente ficava espalhado por diferentes métodos de ciclo de vida (componentDidMount para a busca inicial, componentDidUpdate para atualizar quando props mudam, componentWillUnmount para limpeza). Isso tornava o código mais difícil de ler e manter, pois a lógica coesa estava fragmentada.
- Complexidade do this em Classes: O uso do this em JavaScript dentro de classes pode ser confuso, exigindo bind ou arrow functions para garantir que o contexto esteja correto em callbacks de eventos. Embora seja um detalhe da linguagem, era uma fonte comum de erros para desenvolvedores React.
- Classes Dificultam Otimizações (Potencialmente): Embora o React seja eficiente, o modelo de classes adicionava uma certa sobrecarga na criação de instâncias e no gerenciamento interno que os componentes funcionais puros não tinham.
4.2 Componentes Funcionais
Section titled “4.2 Componentes Funcionais”Pessoal, a programação orientada a objetos é um paradigma de programação. Quando pensamos em um paradigma, estamos falando de um conjunto de convenções colocadas em conjunto para facilitar a forma como representamos nossas lógicas como código.
Muitos dos problemas que os programadores enfrentavam com os componentes como classes, estava no fato de lidar com o seu estado ou ainda quando precisavam compartilhar esse valor pela aplicação. Uma alternativa a esta abordagem, foi utilizar os componentes sem uma representação de estado, o que levou a criação dos componentes funcionais. No início, estes componentes eram limitados a apresentação (renderização) de alguma parte da UI, frente aos valores que recebiam.
4.3 React Hooks
Section titled “4.3 React Hooks”A partir da versão 16.8 do React, as coisas mudaram. Foi adicionado um conjunto poderoso de recursos a biblioteca, que permitia lidar com estado dentro de componentes funcionais, os hooks. Na documentação do React, podemos ver a descrição da implementação deste recurso. Sim é um link legado, mas sugiro fortemente a leitura: link.
Hooks são funções JavaScript especiais que permitem que você “engate” (hook into) recursos de estado e ciclo de vida do React a partir de componentes de função. A palavra-chave aqui é “a partir de componentes de função”. A ideia central é permitir a reutilização de lógica stateful sem alterar a hierarquia de componentes e agrupar a lógica relacionada em um só lugar (ao invés de espalhada pelos métodos de ciclo de vida). Existem algumas regras para utilização dos Hooks:
- Chamar Hooks apenas no Top Level: Nunca chame Hooks dentro de loops, condicionais ou funções aninhadas. O React confia na ordem em que os Hooks são chamados para associar o estado e os efeitos corretos a cada chamada de Hook específica em uma renderização.
- Chamar Hooks apenas de Funções React: Chame Hooks apenas de componentes de função React ou de seus próprios Hooks customizados.
Vamos estudar a utilização de mais alguns hooks e componentes, mas vou deixar mais alguns vídeos para quem desejar conhecer mais sobre o conceito.
4.4 Sumarizando para a aplicação
Section titled “4.4 Sumarizando para a aplicação”Apenas para fazer um fechamento, onde utilizamos este conceito todo dentro da nossa aplicação? Estamos utilizando o hook useState() para definir um valor inicial para o valor associado a lógica de troca de face sorteada do dado. Ele é inicializado com um tupla de dois valores:
- Variável: quem vai armazenar o valor monitorado pelo Hook;
- Método para atualização da variável: é o método que poderá ser chamado com a lógica da atualização do valor da variável. Quando ele é invocado e troca o valor da variável, os componentes de UI que utilizam ele são re-desenhados na tela.
O valor enviado na função useState(), é o valor inicial que é atribuído a variável associada ao hook.