Păstrați-vă în React Native pentru dezvoltarea Android

Publicat: 2022-03-11

Acum câțiva ani, un coleg de-al meu mi-a povestit despre React Native. Eram foarte sceptic. Am susținut că este doar un alt cadru multiplatform care nu va funcționa niciodată în viața reală – nu știam cât de greșit am greșit.

Anii au trecut și abilitățile React Native au devenit foarte solicitate. De vreme ce am învățat ceva nou, m-am gândit de ce să nu încerc? Astăzi, sunt un mare avocat al React Native.

Contra:

  • Nu mai poți folosi Android Studio
  • React Native nu poate fi utilizat pentru fiecare aplicație sau caracteristică
  • React Native este un cadru nou, iar actualizările pot avea un efect negativ asupra bazei de cod curente
  • JavaScript nu este un limbaj strict scris
  • React Native necesită un motor JavaScript pentru a rula, ceea ce îl poate face mai puțin performant

Pro:

  • Usor de invatat
  • O bază de cod partajată între aplicațiile Android și iOS, cu doar modificări minore necesare pentru a se potrivi cu experiențele platformei
  • Reîncărcare live și la cald, ceea ce înseamnă că nu mai există timpi infiniti de construcție
  • Componente native pentru ambele platforme
  • În continuă îmbunătățire
  • Comunitate în creștere activă
  • Număr mare de biblioteci
  • Expo elimină necesitatea de a deține un Mac pentru a dezvolta pentru iOS
  • Reducerea resurselor de muncă — deși s-ar putea să mai aveți nevoie de o dezvoltare nativă Android/iOS, aceasta va fi rar.

Pot continua și mai departe, dar hai să ne oprim aici și să trecem la subiectul acestei postări pe blog. În această postare, voi crea patru aplicații Android bazate pe React Native:

  1. Un contor de bază cu butoane pentru a crește și a reduce contorul
  2. O aplicație pentru a căuta subreddit-ul r/pics
  3. O pagină de autentificare generică
  4. O aplicație pentru a răsfoi subreddit-ul r/pics

IDE

După cum am menționat mai sus, nu putem folosi Android Studio pentru dezvoltarea React Native. Avem nevoie de un înlocuitor. React Native poate fi dezvoltat probabil în orice editor de text modern disponibil (Atom, VS Code, Sublime Text, Brackets, etc.), dar din moment ce venim cu experiența Android Studio preferatul meu este WebStorm, care este construit de aceeași companie. Deși WebStorm este o aplicație plătită (129 USD pe an), puteți instala versiunea Early Access a acesteia. EAP build a WebStorm este gratuită și destul de stabilă. Dacă preferați un editor care este complet gratuit, alegeți VS Code. Microsoft a dezvoltat chiar un plugin uimitor React Native pentru el și funcționează foarte bine.

Crearea unui nou proiect

Cerințe preliminare: Android SDK, Node și React Native instalate pe computer.

Există două moduri de a crea un nou proiect React Native.

  1. Modul convențional. Fie cu WebStorm GUI, fie cu comanda terminalului: react-native init AwesomeToptalProject
  2. Mod mai simplu „Creați aplicația React Native”. create-react-native-app AwesomeToptalProject

Dacă utilizați create-react-native-app , proiectul creat va fi bootstrap cu expo. Nu voi intra în detalii, dar practic înseamnă că nu trebuie să aveți Xcode instalat pentru a rula aplicația pe iOS. Este, de asemenea, mai ușor să ai clientul mereu la zi prin funcționalitatea expo.io și alte caracteristici. Dar nu puteți adăuga cod nativ. Astfel, dacă dezvoltați o funcție specifică, poate fi necesar să scoateți o aplicație din expoziție și să utilizați în schimb un proiect obișnuit React Native.

Voi folosi prima metodă.

Să rulăm proiectul. Mai întâi, deschideți un emulator sau conectați dispozitivul. Dacă ați creat proiectul cu WebStorm GUI, tot ce trebuie să faceți este să alegeți o configurație. În colțul din dreapta sus al WebStorm, faceți clic pe meniul drop-down din stânga butonului Run, alegeți Android și faceți clic pe Run sau Debug. Dacă ați creat proiectul cu Terminal, puteți fie să adăugați o nouă configurație React Native, fie să o rulați folosind Terminal:

 cd AwesomeToptalProject react-native run-android

Dacă totul a mers bine, veți fi întâmpinat cu următorul ecran:

Aspect generat

Structură și setare de bază

Elementele notabile din cadrul proiectului sunt:

  • android - proiect Android Studio preconfigurat cu suport React Native.
  • ios - proiect Xcode preconfigurat cu suport React Native.
  • node_modules - Un folder care conține framework-ul React Native și alte biblioteci Javascript.
  • index.js - Un punct de intrare pentru aplicația noastră.
  • App.js - Componenta inițială încărcată.

Să creăm un folder „src” în rădăcina proiectului și să mutăm App.js acolo. Va trebui să actualizați importurile index.js pentru a se potrivi cu noua locație App.js.

 import App from './src/App';

Ștergeți totul din App.js și inserați acest cod:

 import React from 'react'; import {Text} from 'react-native'; export default class App extends React.Component { render() { return ( <Text>Hello TopTal</Text> ); } }

Codul pe care l-am lipit este destul de simplu. Am creat o clasă App (copilul React.Component ) care suprascrie metoda render() și returnează componenta Text . React.Component este clasa de bază pentru construirea UI folosind JSX. Modificatorul export default face clasa public .

Acum suntem gata să începem proiectarea aspectului nostru.

Aspect cu Flexbox

Flexbox este similar cu LinearLayout , dar Flexbox depășește cu mult abilitățile lui LinearLayout .

Acest fragment de JSX:

 <View style={{ flex: 1, flexDirection: 'row' }}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD' }}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2' }}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7' }}/> </View>

Redă acest aspect:

Aspect generat


În timp ce acest XML:

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Redă asta:

Aspect generat

Codul JSX pare familiar, nu?! Să creăm un „dicționar” (sau un cheatsheet) pentru machete care să arate similar în JSX și Android XML.

Vă rugăm să rețineți că funcționalitățile nu sunt neapărat egale. Încerc să îi ajut pe începătorii React Native să înțeleagă ideea sistemului de aspect în React Native. Vă rugăm să consultați ghidul oficial pentru informații detaliate.

Luați în considerare această proprietate JSX:

 flex: 1

Este echivalent cu asta:

 android:layout_width="match_parent" android:layout_height="match_parent"

Acest fragment de JSX:

 <View style={{flex: 1, flexDirection: 'row'}}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7'}}/> </View>

Și acest XML:

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Ambele generează această ieșire:

Aspect generat


În mod similar, acest JSX:

 <View style={{flex: 1, flexDirection: 'column'}}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7'}}/> </View>

Și acest XML:

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Generați asta:

Aspect generat


Pentru a obține poziția corectă în interiorul containerului, cel mai frecvent vom folosi o combinație de flexDirection , alignItems și justifyContent .

Acest JSX:

 <View style={{flex: 1, flexDirection: 'column', alignItems: 'center'}}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7'}}/> </View>

Și acest XML:

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Va produce acest aspect:

Aspect generat


Acest JSX:

 <View style={{flex: 1, flexDirection: 'column', justifyContent: 'center'}}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7'}}/> </View>

Și acest XML

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="vertical"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Va produce acest aspect:

Aspect generat


Acest JSX:

 <View style={{flex: 1, flexDirection: 'row', justifyContent: 'center'}}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7'}}/> </View>

Și acest XML:

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="horizontal"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Va produce acest aspect:

Aspect generat


Acest JSX:

 <View style={{flex: 1, flexDirection: 'row', justifyContent: 'center', alignItems: 'center'}}> <View style={{ width: 100, height: 100, backgroundColor: '#9575CD'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#7E57C2'}}/> <View style={{ width: 100, height: 100, backgroundColor: '#673AB7'}}/> </View>

și acest XML:

 <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#9575CD" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#7E57C2" /> <View android:layout_width="100dp" android:layout_height="100dp" android:background="#673AB7" /> </LinearLayout>

Va produce acest aspect:

Aspect generat


Lecție de învățat: dacă avem flexDirection: row', alignItems works on Y axis and justifyContent works on X axis. Everything is mirrored for works on X axis. Everything is mirrored for flexDirection: column' - justifyContent afectează axa Y și alignItems afectează axa Y.

justifyContent: „flex-start” gravity="start|left"
alignItems: 'flex-start' gravity="start|left"
justifyContent: „flex-end” gravitate="end|dreapta"
alignItems: 'flex-end' gravitate="end|dreapta"

Incearca-l tu insuti. Setați valoarea justifyContent la space-around , space-between și space-evenly .

Managementul Statului

Pentru a actualiza starea aplicației, veți folosi variabila de state a lui React. Ori de câte ori state este actualizată, render() este invocat.

Copiați codul de mai jos în aplicația dvs.:

 import React from 'react'; import {Button, Text, View} from 'react-native'; export default class App extends React.Component { /* Initialize state object with variable 'number' set to 0 and variable name with value of empty string */ state = {number: 0}; render() { return ( <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', flex: 1, padding: 20 }}> <Button title='Decrement' color='#e57373' onPress={() => this.decrement()}/> <Text> {/* Text will be automatically updated whenever state.number has changed value */} Value = {this.state.number} </Text> <Button title='Increment' color='#64B5F6' {/* Set listener for click */} onPress={() => this.increment()}/> </View> ); } //Declaration of decrement function decrement() { //To update the state we need invoke this.setState //with new value for variable 'number' this.setState({number: this.state.number - 1}); } increment() { this.setState({number: this.state.number + 1}); } } 
Aplicația finală de decrement

Dacă faceți clic pe butoanele DECREȘARE și INCREMENTARE, veți vedea că textul este actualizat automat pentru dvs. Nu este nevoie să utilizați în mod explicit textView.setText("Value " + number) .

Funcționalitatea de stat este utilă din mai multe motive:

  • Ușurință în obținerea valorii — știți întotdeauna unde și cum să obțineți valoarea pentru o anumită variabilă.
  • Datele nu sunt legate de anumite widget-uri.
  • Având mai multe widget-uri în funcție de modificarea valorii comune.

Crearea unei aplicații de căutare pentru /r/pics

Acum că ne înțelegem elementele fundamentale, să creăm ceva mai complex: o aplicație de căutare pentru /r/pics. Reddit oferă un punct final JSON API simplu, așa că nu va trebui să facem misiuni secundare pentru a obține autentificare pentru ca acesta să funcționeze corect.

React Native oferă un API Fetch încorporat. Deoarece majoritatea dintre noi sunt probabil obișnuiți cu Retrofit și ușurința sa de utilizare, vom folosi axios . Puteți instala axios printr-o comandă de terminal

folosind yarn (metoda mea preferată):

yarn add axios

sau folosind npm :

npm install axios

Importuri:

 import React from 'react'; import { TextInput, View, Text, Image, ActivityIndicator, Platform, StyleSheet } from 'react-native'; import axios from 'axios'; TextInput = EditText, ActivityIndicator = ProgressBar Platform - Platform detecting module StyleSheet - Module for creating stylesheets and moving them away from JSX

Creați clasa:

 export default class App extends React.Component { }

Pentru a inițializa starea. Vom avea nevoie de:

  • încărcare - Pentru afișarea unei bare de progres.
  • eroare - Pentru a arăta dacă a apărut o eroare la efectuarea unei solicitări API REST.
  • imgUrl - Pentru a previzualiza imaginea căutată.
  • text - interogare de căutare.
 state = {text: '', loading: false, error: null, imgUrl: null};

Adăugați codul JSX. Avem un aspect vertical cu componente TextInput și Image .

 render() { return ( //Predefined style. See below <View style={styles.containerStyle}> {/* returnKeyType ~ imeOptions onSubmitEditing ~ et.OnEditorActionListener */} <TextInput style={styles.textInputStyle} placeholder="Enter text to search an image" returnKeyType='search' autoFocus={true} onChangeText={(text) => this.setState({text})} onSubmitEditing={() => this.searchPicture()}/> {/* Render error Image component if this.state.imgUrl is not equal to null */} { this.state.imgUrl && <Image source={{uri: this.state.imgUrl}} style={{flex: 1}}/> } </View> ); }

Lucruri noi:

 onChangeText={(text) => this.setState({text})} onSubmitEditing={() => this.searchPicture()} { this.state.imgUrl && <Image source={{uri: this.state.imgUrl}} style={{flex: 1}}/> }

Prima metodă face o activitate similară cu EditText cu componenta TextWatcher . Să fim sinceri, este mult mai frumos în React Native.

A doua metodă este invocată atunci când tasta return este apăsată pe tastatură ( et.OnEditorActionListener ) după ce declanșează searchPicture() .

Imaginea este redată atunci când imgUrl nu este nulă sau nedefinită, deoarece operatorul „&&” nu verifică un al doilea argument dacă primul este deja fals.

S-ar putea să vă întrebați de ce this.state.imgUrl este fals. Ei bine, atunci când utilizați operatori logici în JavaScript, orice cu excepția „” (un șir gol), 0 , false , null sau undefined este adevărat. Nu este nevoie de un control specific.

 searchPicture() { //Default state this.setState({loading: true, error: null, imgUrl: null}); axios.get('https://www.reddit.com/r/pics/search.json', { params: { //the get param map restrict_sr: 'on', //search only /r/pics limit: 1, //limit to one search item sort: 'new', //sort by creation date q: this.state.text //our search query } }).then(response => { //promise is resolved and 'then' block is triggered //set state with new values this.setState({ imgUrl: response.data.data.children[0] .data.preview.images[0].source.url, error: null, loading: false }) }).catch(error => {//Some error occurred //set error this.setState({error: error.message, loading: false, imgUrl: null}) }) }

Începem. Aplicația ar trebui să funcționeze conform așteptărilor acum. Introduceți un șir de căutare și apăsați Retur.

Aplicația finală de căutare reddit

Deoarece aplicația noastră este, de asemenea, pregătită pentru a reda ActivityIndicator și erori, trebuie să mai adăugăm ceva cod după componenta Image :

 { //Separate method this.renderProgress() } {/* Render error Text component if this.state.error is not equal to null */} { this.state.error && <Text style={{margin: 16, color: 'red'}}> {this.state.error} </Text> }

De asemenea, puteți muta componentele de randare în afara metodei principale de render() :

 renderProgress() { //If this.state.loading is true //return View containing a progressbar //View takes style array if (this.state.loading === true) { return ( <View style={ [styles.containerStyle, {justifyContent: 'center'}]}> <ActivityIndicator color='#e57373'/> </View> ); } }

Tot ce a mai rămas sunt stiluri. Pune-le în afara clasei App .

 const styles = StyleSheet.create({ containerStyle: { flexDirection: 'column', flex: 1, //Since React Native is cross platform //let's handle both platforms. //Add top margin to fix status bar overlap marginTop: Platform.OS === 'ios' ? 20 : 0, }, textInputStyle: { marginLeft: 16, marginRight: 16, height: Platform.OS === 'ios' ? 30 : undefined } });

Acum putem adăuga câteva modificări, cum ar fi deschiderea automată a tastaturii soft atunci când aplicația este lansată.

Vă rugăm să rețineți că există o modalitate mai ușoară de a face TextInput să se concentreze automat ( autoFocus={true} prop ), dar de dragul acestui exemplu, nu îl vom folosi.

Adăugați referință la TextInput cu prop:

ref={ref => this.searchInput = ref}

Și suprascrieți metoda ciclului de viață componentDidMount() astfel:

 componentDidMount(){ this.searchInput.focus(); }

Reîncărcați aplicația și tastatura se deschide automat pentru noi.

Metode ale ciclului de viață al componentelor

Am creat deja o componentă, dar să trecem prin durata de viață a unei componente.

Iată fluxul ciclului de viață al lui React:

  • constructor() - Constructorul este întotdeauna apelat când aplicația este pornită
  • static _getDerivedStateFromProps_(props, state) - Apelat înainte de randare și după actualizare. Returnează obiectul pentru actualizarea stării. Returnează null pentru a nu actualiza nimic.
  • render() - Redarea este necesară pentru fiecare clasă React Component. Este folosit pentru a reda View.
  • componentDidMount() - Este invocat după ce componenta este randată și montată în arborele de vizualizare.
  • shouldComponentUpdate(nextProps, nextState) - Apelat după schimbarea stării sau a elementelor de recuzită. Revenirea este implicită la true după fiecare actualizare de stare. Invocă render() dacă returnează adevărat.
  • getSnapshotBeforeUpdate(prevProps, prevState) - Apelat chiar înainte ca rezultatul redat să fie comis.
  • componentDidUpdate(prevProps, prevState, snapshot) - Apelat după ce a redat o nouă actualizare. Nu este apelat după primul render() .
  • componentWillUnmount() - Apelat chiar înainte ca componenta să fie demontată și distrusă.

Diagrama ciclului de viață al componentelor

Componente reutilizabile

De multe ori trebuie să creăm componente reutilizabile atunci când lucrăm la proiect. Există două moduri de a crea o componentă:

  1. Crearea unei clase care extinde React.Component . Această metodă ar trebui folosită dacă avem nevoie de metode de ciclu de viață.
  2. Prin scrierea unei funcții care returnează View pentru o sintaxă mai simplă.

Deoarece am creat deja clase de componente, să creăm o funcție pentru această instanță.

Să presupunem că avem nevoie de un analog pentru <CardView> . Creați un folder „comun” în directorul ./src .

Creați CardView.js .

 import React from "react"; import {View} from "react-native"; export default CardView = (props) => { return ( //Style will be merged from default containerStyle //and props.style. props.style attributes will override //values if parameters are same. <View style={{...styles.containerStyle, ...props.style}}> {/* props.children contain subviews add this line if the component is container */} {props.children} </View> ); }; const styles = { containerStyle: { borderRadius: 4, margin: 5, padding: 5, elevation: 5, shadowColor: 'black', shadowRadius: 5, shadowOpacity: 0.5, shadowOffset: {width: 0, height: 3}, backgroundColor: 'white' } };

LoginForm folosind noul nostru aspect CardView :

 import React from "react"; import {TextInput, Platform, Button, StyleSheet} from "react-native"; import CardView from "../common/components/CardView"; export default class LoginForm extends React._Component _{ render() { return ( //Override default style <CardView style={{ borderRadius: 4, backgroundColor: '#fff' }}> <TextInput placeholder="Email" style={styles.textInputStyle}/> <TextInput placeholder="Password" style={styles.textInputStyle} secureTextEntry={true}/> <Button color="#841584" title="Login" onPress={() => console.log("onLoginPress")} buttonStyle={styles.buttonStyle}/> </CardView> ); } } const styles = StyleSheet.create({ buttonStyle: { elevation: 5, height: 40 }, textInputStyle: { padding: 10, //Additional params to make //iOS inputs prettier ...Platform.select({ ios: { borderRadius: 2, marginTop: 5, backgroundColor: '#eeeeee' } }) } });

Importați clasa LoginForm în clasa App și împachetați-o cu View

 <View style={{flex: 1, justifyContent: 'center'}}> <LoginForm/> </View>

Dacă modificați parametrii în stiluri, puteți obține ceva care arată mult mai frumos.

Aplicația finală pentru formularul de conectare generat

Navigare

Navigarea către diferite scene este o parte esențială pentru majoritatea aplicațiilor. Vom crea o aplicație de browser Reddit /r/pics.

Crearea navigației în React Native este destul de ușoară.

Cerințe preliminare

  • Instalați react-navigation cu yarn sau npm
  • Instalați axios cu yarn sau npm

Să începem prin a crea două componente diferite.

Notă: majoritatea codului de mai jos ar trebui să vă fie deja familiar. Voi lipi toată clasa.

PictureList.js:

 import React from 'react'; import { ActivityIndicator, FlatList, Image, Text, TouchableHighlight, View } from "react-native"; import axios from "axios"; import CardView from "../common/CardView"; export default class PictureList extends React.Component { state = {loading: true, error: null, posts: null}; componentDidMount() { axios.get('https://www.reddit.com/r/pics.json') .then(response => { this.setState({ posts: response.data.data.children, loading: false }) }).catch(error => { this.setState({ error: error.message, loading: false }) }) } render() { return ( <View style={{flex: 1, justifyContent: 'center'}}> // FlatList ~ ListView // data - DataSource for the List // renderItem - function returns View item // keyExtractor - Unique id for items {this.state.posts && <FlatList data={this.state.posts} renderItem={this.renderItem.bind(this)} keyExtractor={(item) => (item.data.id + '')}/>} {this.state.loading && <ActivityIndicator size="large" color="#f4511e"/>} </View> ); } navigateToPicture(title, url) { this.props.navigation.navigate('PicturePreview', { 'title': title, 'url': url }) } renderItem(item) { //Destructuring values from item //Read more 'ES6 destructuring' const {data} = item.item; const {title} = data; const {url} = data.preview.images[0].source; return ( //Clickable view <TouchableHighlight onPress={() => this.navigateToPicture(title, url)}> {/Reusing our CardView/} <CardView> <Image style={{height: 150}} source={{uri: url}}/> <Text style={{padding: 5}}>{title}</Text> </CardView> </TouchableHighlight> ) } }

PicturePreview.js :

 import React from 'react'; import {Image} from "react-native"; export default class PicturePreview extends React.Component { //Destructure navigation //Set title to header static _navigationOptions = ({navigation}) => ({ title: navigation.state.params.title }); render() { const {url} = this.props.navigation.state.params; return (<Image style={{flex: 1}} source={{uri: url}}/>) } }

Opțiunile de navigationOptions vor fi invocate automat de React-Navigation.

Acum să trecem la App.js

Notă: Există multe tipuri de navigare în React-Navigation. Astăzi, ne vom concentra pe StackNavigation . Vă rugăm să consultați site-ul oficial pentru informații detaliate.

 import React from 'react'; import {createStackNavigator} from "react-navigation"; import PictureList from "./components/PictureList"; import PicturePreview from "./components/PicturePreview"; export default class App extends React.Component { render() { return ( <Router/> ); } } //Customize the header_ const NavigationOptions = { headerTintColor: '#fff', headerStyle: { backgroundColor: '#f4511e', } }; //Create the router. const Router = createStackNavigator({ //Name the screen 'PictureList': { //Link the Component screen: PictureList, //Additional navigation options navigationOptions: { title: '/r/pics Browser', ...NavigationOptions } }, 'PicturePreview': { screen: PicturePreview, navigationOptions: NavigationOptions } }, { //Root initialRouterName: 'PictureList' } );

După cum puteți vedea, tot ce trebuie să facem este să creăm un router de navigare și să facem ca aplicația să-l redea. Dacă totul a mers bine, vom avea o aplicație funcțională de browser Reddit /r/pics.

Android:

Aplicația finală de navigare în Android

iOS:

Aplicația finală de navigare în iOS

React Native este uimitor!

De când am început să programez, am avut experiențe de dezvoltare pur mobilă. Dar acum pot codifica pentru aproape orice cu React: mobil, desktop și web.

Dacă decideți să începeți să vă dezvoltați următoarea aplicație uimitoare folosind React Native, veți descoperi că are ciudateniile sale și unele erori pe alocuri, dar React Native este foarte funcțional și ideal pentru majoritatea proiectelor.

Înrudit: Construiți un scaner QR: Un tutorial pentru camera React Native