Nas primeiras duas partes desta série sobre React Native nós aprendemos o que é, para que serve e como podemos criar um app em React para dispositivos móveis e depois estudamos sobre tags e estilos JSX e componentes de informações nas telas React Native.
Hoje, nós vamos ver mais alguns conceitos básicos e ir além, avançando ainda mais!
Parametrizando um componente
Outro conceito importante de componentes em React é parametrização. Quando usamos um mesmo componente em diferentes partes da nossa aplicação, podemos configurar ele passando parâmetros ao mesmo, da mesma forma que faríamos com tags HTML na web.
Mas para que isso seja possível, devemos modificar a nossa função Header (dentro de Header.js) para que ela espere um objeto de propriedades e, a partir desse objeto, pegamos as informações que queremos e as juntamos ao JSX usando chaves {}.
1 2 3 4 5 6 7 8 9 |
function Header(props) { return ( <View style={styles.container}> <Text style={styles.headerText}>{props.title}</Text> </View> ); } |
No exemplo acima, estaremos esperando as propriedades do componente como parâmetro da função e usamos somente uma propriedade hipotética title (entre chaves, para ocorrer o evaluation da propriedade) para tornar o Text do componente configurável.
Mas como que passamos essa configuração?
Da mesma maneira que faríamos com HTML comum, diretamente onde chamamos a tag Header no módulo App.js, passando a propriedade title.
1 2 3 4 5 6 7 8 9 10 11 12 |
export default function App() { return ( <> <Header title="Header Param" /> <View style={styles.container}> <Text style={styles.title}>Hello World!</Text> </View> </> ); } |
O resultado é que o título renderizado na página agora é modificado de acordo com a propriedade title da tag Header.
Agora experimente colocar várias vezes o componente Header na sua App.js, apenas mudando o title entre eles, para ver o que acontece.
Esse é o poder de componentes parametrizados!
Estados do componente
Outro conceito muito importante de aprender antes que possamos de fato começar a construir apps em React Native é o conceito de Estados.
Estados são a forma como o React permite que os componentes mantenham informações alteradas/inputadas/manipuladas pelo usuário na interface. Por exemplo, e se quisermos alterar um contador a cada toque de um botão? Para conseguirmos fazer isso precisamos manter o estado desse contador entre um clique e outro, certo?
Para fazer isso, vamos modificar o nosso App.js para criar uma variável de contagem e imprimi-la na tela. Essa declaração e toda a lógica de interface é da mesma forma que você faria com JavaScript tradicional.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
export default function App() { let contador = 0; return ( <> <Header title="Header Param" /> <View style={styles.container}> <Text style={styles.title}>Hello World!</Text> <Text>{contador} toques!</Text> </View> </> ); } |
Se você olhar lá na aplicação verá que está marcando 0 cliques. Isso mostra como podemos imprimir uma variável local na interface. Mas e como fazemos para, a cada toque em um botão, esse contador aumente?
Para colocar um botão, mesmo que ainda sem comportamento, existem diversas formas de fazer, sendo que vou optar por uma que eu acho bem simples e, mais tarde, a gente opta por uma mais complexa. Primeiro passo, import do TouchableOpacity.
1 2 3 4 5 |
import React from 'react'; import { StyleSheet, Text, View, TouchableOpacity } from 'react-native'; import Header from './Header'; |
O próximo passo, é usar este novo componente TouchableOpacity na nossa interface, colocando-o onde você julgar mais conveniente.
1 2 3 4 5 |
<TouchableOpacity style={styles.button}> <Text>Press Here</Text> </TouchableOpacity> |
Note que declarei um estilo para ele, que ainda não existe na nossa App.js, então trate de adicionar, sendo que o código abaixo do estilo button é apenas uma sugestão.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#474A51', alignItems: 'center', justifyContent: 'center', }, title: { fontSize: 30, fontWeight: 'bold', color: 'black' }, button: { marginTop: 20, alignItems: "center", backgroundColor: "#DDDDDD", padding: 10 } }); |
Certo, com esses códigos novos, temos um botão na tela com a aparência abaixo, mas que ainda não faz nada, certo? Ou quase, porque se você clicar nele, vai notar que ele tem o comportamento de mudar levemente a opacidade para mostrar que foi clicado, o que já é alguma coisa…
Para que nosso botão dispare uma ação, primeiro devemos criar uma function JavaScript dentro da function App (sim, é uma function dentro de outra function, não se preocupe).
1 2 3 4 5 6 7 8 |
export default function App() { let contador = 0; function incrementar(){ contador++; console.log(contador); } |
E no nosso botão/TouchableOpacity, vamos referenciar essa function na propriedade onPress dentro de chaves.
1 2 3 4 5 |
<TouchableOpacity style={styles.button} onPress={incrementar}> <Text>Press Here</Text> </TouchableOpacity> |
No entanto, se você executar esse código, verá que ele imprime o contador incrementalmente a cada toque do botão no console, mas que na interface, ele não está mudando o contador impresso.
Isso porque ainda não estamos usando estados!
Para usar estados, primeiro precisamos adicionar mais um import da lib do React, o useState, como abaixo.
1 2 3 |
import React, {useState} from 'react'; |
Depois, poderemos usar essa function useState para armazenar estados da nossa aplicação ao invés de armazenar variáveis comuns. Isso vem com um pró e um contra. A vantagem é obviamente o fato de manter a nossa interface plugada na nossa lógica de front-end. A desvantagem é referente ao conceito de imutabilidade.
Todo estado é imutável em React, isso quer dizer que não podemos manipular um estado já existente, mas ao invés disso, nós temos de usar uma função para criar um novo estado sobre o antigo. Isso é feito usando a function useState que importamos agora há pouco, quando chamada (passando o valor inicial do estado) retorna um array com duas posições: na primeira, o valor atual do estado, na segunda, a função que “altera” o mesmo.
1 2 3 4 5 6 7 |
export default function App() { const [contador, setContador] = useState(0); function incrementar(){ setContador(contador+1); } |
No código acima, eu não declaro mais uma variável contador simples, mas carrego um contador e uma função de mudança de estado usando o useState e passando a ele o valor inicial do estado (0 neste caso), utilizando as duas na function de increment.
Com isso, já temos o nosso “press and increment” funcionando, independente se você testa no seu smartphone ou no simulador!
Reorganizando nosso app
Até agora estávamos fazendo tudo apenas em nosso App.js, mas isso não é nada profissional e já está na hora de começarmos a complicar um pouco mais as coisas. Vamos começar criando uma pasta Pages na raiz do nosso projeto e dentro desse pasta Pages o nosso arquivo Header e um novo arquivo Home.js.
Dentro do Home.js vamos colocar o conteúdo que antes era do nosso App.js. Isso porque vamos manter no App.js apenas elementos essenciais para inicialização da aplicação, ao invés de uma tela em específico.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import React, {useState} from 'react'; import { StyleSheet, Text, View, TouchableOpacity } from 'react-native'; import Header from './Header'; export default function Home() { const [contador, setContador] = useState(0); function incrementar(){ setContador(contador+1); } return ( <> <Header title="Header Param" /> <View style={styles.container}> <Text style={styles.title}>Hello World!</Text> <Text>{contador} toques!</Text> <TouchableOpacity style={styles.button} onPress={incrementar}> <Text>Press Here</Text> </TouchableOpacity> </View> </> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#474A51', alignItems: 'center', justifyContent: 'center', }, title: { fontSize: 30, fontWeight: 'bold', color: 'black' }, button: { marginTop: 20, alignItems: "center", backgroundColor: "#DDDDDD", padding: 10 } }); |
Então, voltando ao App.js, vamos apenas incluir nele uma chamada para a tela inicial, enquanto não aprendemos a rotear telas…por enquanto…
1 2 3 4 5 6 7 8 9 10 |
import React, {useState} from 'react'; import Home from './Pages/Home'; export default function App() { return ( <Home /> ); } |
A ideia aqui é que você coloque todas as páginas/telas da sua aplicação dentro de Pages. Opcionalmente, você pode ainda criar subpastas dentro de Pages, para ter subdivisões.
Para exercitar este conceito e antes de avançarmos para tópicos mais avançados, crie mais um arquivo dentro de pages, chamado Form.js, como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; import Header from './Header'; function Form() { return ( <> <Header title="Cadastro" /> <View style={styles.container}> <Text>Teremos um formulário aqui!</Text> </View> </> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', } }); export default Form; |
Ele não tem nada demais, mas exercita a componentização, pois usa o componente Header que criamos anteriormente e deixa espaço para que futuramente a gente coloque conteúdo nessa tela.
Mas se nosso App.js aponta diretamente para a tela Home.js, como faremos para testar essa Form.js? Por enquanto, mude manualmente no App.js para importar para Form.js e futuramente vou mostrar como você pode transicionar de uma tela para outra através do clique de um botão.
E com isso, nós encerramos esta terceira parte da nossa série sobre React Native, e se você começou a perceber, com o conteúdo dessa aula abre-se um universo de possibilidades pois já conseguimos ter um app dinâmica, então já aproveita e emenda a quarta parte!
Gostou do tutorial de hoje? Conheça meu curso online de Node.js clicando no banner abaixo.
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.