Zanurz się w React Native dla Androida
Opublikowany: 2022-03-11Kilka lat temu mój współpracownik powiedział mi o React Native. Byłem bardzo sceptyczny. Twierdziłem, że to tylko kolejny cross-platformowy framework, który nigdy nie zadziała w prawdziwym życiu – niewiele wiedziałem, jak bardzo się myliłem.
Minęły lata, a umiejętności React Native stały się bardzo poszukiwane. Ponieważ minęło trochę czasu, gdy nauczyłem się czegoś nowego, pomyślałem, dlaczego nie spróbować? Dziś jestem wielkim zwolennikiem React Native.
Cons:
- Nie możesz już korzystać z Android Studio
- React Native nie może być używany dla każdej aplikacji lub funkcji
- React Native to nowatorski framework, a aktualizacje mogą mieć negatywny wpływ na aktualną bazę kodu
- JavaScript nie jest językiem ściśle pisanym
- React Native wymaga do działania silnika JavaScript, co może zmniejszyć jego wydajność
Plusy:
- Łatwe do nauki
- Wspólna baza kodu między aplikacjami na Androida i iOS, z niewielkimi poprawkami wymaganymi w celu dopasowania doświadczeń platformy
- Przeładowanie na żywo i na gorąco, co oznacza koniec nieskończonych czasów budowy
- Natywne komponenty dla obu platform
- Ciągle się poprawiam
- Aktywnie rozwijająca się społeczność
- Ogromna liczba bibliotek
- Expo eliminuje potrzebę posiadania komputera Mac do programowania na iOS
- Zmniejszenie zasobów pracy — chociaż nadal możesz potrzebować natywnego programowania na Androida/iOS, będzie to rzadkie.
Mogę gadać i gadać, ale zatrzymajmy się tutaj i przejdźmy do tematu tego wpisu na blogu. W tym poście zamierzam stworzyć cztery aplikacje na Androida oparte na React Native:
- Podstawowy licznik z przyciskami do zwiększania i zmniejszania licznika
- Aplikacja do wyszukiwania subreddit r/pics
- Ogólna strona logowania
- Aplikacja do przeglądania subreddit r/pics
IDE
Jak wspomniałem powyżej, nie ma mowy, abyśmy mogli wykorzystać Android Studio do programowania React Native. Potrzebujemy substytutu. React Native może być rozwijany prawdopodobnie w dowolnym nowoczesnym edytorze tekstowym dostępnym na rynku (Atom, VS Code, Sublime Text, Brackets itp.), ale ponieważ przychodzimy z doświadczeniem Android Studio, moim ulubionym jest WebStorm, który jest zbudowany przez tę samą firmę. Chociaż WebStorm jest płatną aplikacją (129 $ rocznie), możesz zainstalować jej wersję Early Access. Kompilacja EAP WebStorma jest bezpłatna i dość stabilna. Jeśli wolisz edytor, który jest całkowicie darmowy, wybierz VS Code. Microsoft opracował nawet dla niego niesamowitą wtyczkę React Native i działa bardzo dobrze.
Tworzenie nowego projektu
Wymagania wstępne: Android SDK, Node i React Native zainstalowane na Twoim komputerze.
Istnieją dwa sposoby tworzenia nowego projektu React Native.
- Konwencjonalny sposób. Albo za pomocą GUI WebStorm, albo za pomocą polecenia terminala:
react-native init AwesomeToptalProject
- Łatwiejszy sposób „Utwórz aplikację React Native”.
create-react-native-app AwesomeToptalProject
Jeśli użyjesz create-react-native-app
, utworzony projekt zostanie załadowany za pomocą expo. Nie będę wdawał się w szczegóły, ale w zasadzie oznacza to, że nie musisz mieć zainstalowanego Xcode, aby uruchomić aplikację na iOS. Łatwiej jest również mieć klienta zawsze na bieżąco dzięki funkcjonalności expo.io i kilku innym funkcjom. Ale nie możesz dodać kodu natywnego. Tak więc, jeśli tworzysz konkretną funkcję, być może będziesz musiał usunąć aplikację z expo i zamiast tego użyć zwykłego projektu React Native.
Użyję pierwszej metody.
Uruchommy projekt. Najpierw otwórz emulator lub podłącz urządzenie. Jeśli stworzyłeś projekt za pomocą WebStorm GUI, wszystko, co musisz zrobić, to wybrać konfigurację. W prawym górnym rogu WebStorm kliknij menu rozwijane po lewej stronie przycisku Uruchom, wybierz Android i kliknij Uruchom lub Debuguj. Jeśli utworzyłeś projekt za pomocą Terminala, możesz dodać nową konfigurację React Native lub uruchomić ją za pomocą Terminala:
cd AwesomeToptalProject react-native run-android
Jeśli wszystko poszło dobrze, zostaniesz powitany następującym ekranem:

Struktura i podstawowa konfiguracja
Godne uwagi elementy wewnątrz projektu to:
- android - Projekt Android Studio wstępnie skonfigurowany z obsługą React Native.
- ios - projekt Xcode wstępnie skonfigurowany z obsługą React Native.
- node_modules — folder zawierający framework React Native i inne biblioteki Javascript.
- index.js — punkt wejścia do naszej aplikacji.
- App.js — załadowano początkowy komponent.
Utwórzmy folder „src” w katalogu głównym projektu i przenieśmy tam App.js. Musisz zaktualizować importy index.js, aby pasowały do nowej lokalizacji App.js.
import App from './src/App';
Usuń wszystko w App.js i wklej ten kod:
import React from 'react'; import {Text} from 'react-native'; export default class App extends React.Component { render() { return ( <Text>Hello TopTal</Text> ); } }
Kod, który wkleiliśmy, jest dość prosty. Stworzyliśmy klasę App
(dziecko React.Component
), która nadpisuje metodę render()
i zwraca komponent Text
. React.Component
jest klasą bazową do budowania UI przy użyciu JSX. export default
sprawia, że klasa jest public
.
Jesteśmy teraz gotowi do rozpoczęcia projektowania naszego układu.
Układ z Flexbox
Flexbox
jest podobny do LinearLayout
, ale Flexbox
wykracza daleko poza możliwości LinearLayout
.
Ten fragment kodu 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>
Renderuje ten układ:
Podczas gdy ten kod 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>
Renderuje to:
Kod JSX wygląda znajomo, co?! Stwórzmy „słownik” (lub ściągawkę) dla układów, które wyglądają podobnie w JSX i Android XML.
Pamiętaj, że funkcje niekoniecznie są równe. Staram się pomóc nowicjuszom React Native w zrozumieniu idei systemu layoutu w React Native. Szczegółowe informacje można znaleźć w oficjalnym przewodniku.
Rozważ tę właściwość JSX:
flex: 1
Jest to równoznaczne z tym:
android:layout_width="match_parent" android:layout_height="match_parent"
Ten fragment kodu 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>
A ten 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>
Oba generują ten wynik:
Podobnie ten 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>
A ten 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>
Wygeneruj to:
Aby uzyskać właściwą pozycję wewnątrz kontenera, najczęściej używamy kombinacji właściwości flexDirection
, alignItems
i justifyContent
.
Ten 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>
A ten 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>
Wyprodukuje ten układ:
Ten 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 ten 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>
Wyprodukuje ten układ:
Ten 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>
A ten 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>
Wyprodukuje ten układ:
Ten 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 ten 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>
Wyprodukuje ten układ:
Lekcja do nauczenia: jeśli mamy 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
wpływa na oś Y, a alignItems
na oś Y.
justifyContent: 'flex-start' | grawitacja="start|w lewo" |
alignItems: 'flex-start' | grawitacja="start|w lewo" |
justifyContent: 'flex-end' | grawitacja="koniec|prawo" |
alignItems: 'flex-end' | grawitacja="koniec|prawo" |
Spróbuj sam. Ustaw wartość justifyContent
na space-around
, space-between
i space-evenly
.
Zarządzanie państwowe
Do aktualizacji stanu aplikacji użyjesz zmiennej state
Reacta. Za każdym razem, gdy state
jest aktualizowany, wywoływana jest funkcja render()
.
Skopiuj poniższy kod do swojej aplikacji:
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}); } }

Jeśli klikniesz przyciski ZMNIEJSZ i ZMNIEJSZ, zobaczysz, że tekst zostanie automatycznie zaktualizowany. Nie ma potrzeby jawnego używania textView.setText("Value " + number)
.

Funkcjonalność stanu jest przydatna z wielu powodów:
- Łatwość uzyskania wartości – zawsze wiesz, gdzie i jak uzyskać wartość dla określonej zmiennej.
- Dane nie są powiązane z konkretnymi widżetami.
- Posiadanie wielu widżetów zależnych od wspólnej zmiany wartości.
Tworzenie aplikacji do wyszukiwania dla /r/pics
Teraz, gdy znamy już podstawy, stwórzmy coś bardziej złożonego: wyszukiwarkę /r/pics. Reddit zapewnia prosty punkt końcowy interfejsu API JSON, więc nie będziemy musieli wykonywać zadań pobocznych, aby uzyskać uwierzytelnianie, aby działało poprawnie.
React Native zapewnia wbudowany interfejs Fetch API. Ponieważ większość z nas jest prawdopodobnie przyzwyczajona do modernizacji i łatwości jej użycia, będziemy używać axios . Możesz zainstalować axios za pomocą polecenia terminala
używając yarn
(moja preferowana metoda):
yarn add axios
lub za pomocą npm
:
npm install axios
Import:
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
Utwórz klasę:
export default class App extends React.Component { }
Aby zainicjować stan. Będziemy potrzebować:
- ładowanie — wyświetlanie paska postępu.
- error — aby pokazać, czy wystąpił jakiś błąd podczas wysyłania żądania REST API.
- imgUrl — do podglądu wyszukiwanego obrazu.
- tekst - zapytanie wyszukiwania.
state = {text: '', loading: false, error: null, imgUrl: null};
Dodaj kod JSX. Mamy układ pionowy z komponentami 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> ); }
Nowe rzeczy:
onChangeText={(text) => this.setState({text})} onSubmitEditing={() => this.searchPicture()} { this.state.imgUrl && <Image source={{uri: this.state.imgUrl}} style={{flex: 1}}/> }
Pierwsza metoda działa podobnie do EditText
ze składnikiem TextWatcher
. Bądźmy szczerzy, w React Native jest o wiele ładniej.
Druga metoda jest wywoływana po naciśnięciu klawisza Return na klawiaturze ( et.OnEditorActionListener
) po wyzwoleniu searchPicture()
.
Obraz jest renderowany, gdy imgUrl
nie ma wartości null lub jest niezdefiniowany, ponieważ operator „&&” nie sprawdza drugiego argumentu, jeśli pierwszy jest już fałszywy.
Być może zastanawiasz się, dlaczego this.state.imgUrl
jest fałszywe. Cóż, gdy używasz operatorów logicznych w JavaScript, wszystko oprócz '' (pusty ciąg), 0
, false
, null
lub undefined
jest prawdziwe. Nie ma potrzeby specjalnego sprawdzania.
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}) }) }
No to ruszamy. Aplikacja powinna teraz działać zgodnie z oczekiwaniami. Wprowadź szukany ciąg i naciśnij klawisz Enter.

Ponieważ nasza aplikacja jest również gotowa do renderowania ActivityIndicator
i błędów, musimy dodać trochę więcej kodu po komponencie 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> }
Możesz również przenieść komponenty renderujące poza główną metodę 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> ); } }
Pozostały tylko style. Umieść je poza klasą 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 } });
Możemy teraz dodać więcej poprawek, takich jak automatyczne otwieranie miękkiej klawiatury po uruchomieniu aplikacji.
Pamiętaj, że istnieje prostszy sposób, aby TextInput
automatycznie ustawiał ostrość ( autoFocus={true} prop
), ale ze względu na ten przykład nie będziemy go używać.
Dodaj referencję do TextInput
z prop:
ref={ref => this.searchInput = ref}
I nadpisz metodę cyklu życia componentDidMount()
w następujący sposób:
componentDidMount(){ this.searchInput.focus(); }
Załaduj ponownie aplikację, a klawiatura otworzy się dla nas automatycznie.
Metody cyklu życia komponentów
Stworzyliśmy już komponent, ale przejdźmy przez życie komponentu.
Oto cykl życia Reacta:
-
constructor()
— Konstruktor jest zawsze wywoływany podczas uruchamiania aplikacji -
static _getDerivedStateFromProps_(props, state)
— wywoływane przed renderowaniem i po aktualizacji. Zwraca obiekt do aktualizacji stanu. Zwróć wartość null, aby niczego nie aktualizować. -
render()
— renderowanie jest wymagane dla każdej klasy komponentu React. Służy do renderowania widoku. -
componentDidMount()
— jest wywoływana po wyrenderowaniu komponentu i zamontowaniu go w drzewie widoku. -
shouldComponentUpdate(nextProps, nextState)
— wywoływane po zmianie stanu lub właściwości. Po każdej aktualizacji stanu zwracana jest wartość domyślna prawda. Wywołujerender()
, jeśli zwraca true. -
getSnapshotBeforeUpdate(prevProps, prevState)
— wywoływane tuż przed zatwierdzeniem renderowanych danych wyjściowych. -
componentDidUpdate(prevProps, prevState, snapshot)
— wywoływane po wyrenderowaniu nowej aktualizacji. Nie jest wywoływana po pierwszymrender()
. -
componentWillUnmount()
— wywoływana tuż przed odmontowaniem i zniszczeniem komponentu.
Komponenty wielokrotnego użytku
Często musimy tworzyć komponenty wielokrotnego użytku podczas pracy nad projektem. Istnieją dwa sposoby tworzenia komponentu:
- Stworzenie klasy, która rozszerza
React.Component
. Ta metoda powinna być stosowana, jeśli potrzebujemy metod cyklu życia. - Pisząc funkcję, która zwraca View dla prostszej składni.
Skoro mamy już utworzone klasy Component, stwórzmy funkcję dla tej instancji.
Załóżmy, że potrzebujemy analogu do <CardView>
. Utwórz „wspólny” folder w katalogu ./src
.
Utwórz 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
przy użyciu naszego nowego układu 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' } }) } });
Zaimportuj klasę LoginForm
do klasy App
i zapakuj ją za pomocą View
<View style={{flex: 1, justifyContent: 'center'}}> <LoginForm/> </View>
Jeśli dostosujesz parametry w stylach, możesz uzyskać coś, co wygląda znacznie ładniej.
Nawigacja
Nawigacja do różnych scen jest istotną częścią większości aplikacji. Zamierzamy stworzyć aplikację przeglądarki Reddit /r/pics.
Tworzenie nawigacji w React Native jest dość łatwe.
Warunki wstępne
- Zainstaluj
react-navigation
React zyarn
lubnpm
- Zainstaluj
axios
zyarn
lubnpm
Zacznijmy od stworzenia dwóch różnych komponentów.
Uwaga: większość poniższego kodu powinna już być Ci znana. Wkleję całą klasę.
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}}/>) } }
Opcje navigationOptions
zostaną automatycznie wywołane przez React-Navigation.
Przejdźmy teraz do App.js
Uwaga: W React-Navigation istnieje wiele typów nawigacji. Dzisiaj skupimy się na StackNavigation
. Szczegółowe informacje można znaleźć na oficjalnej stronie internetowej.
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' } );
Jak widać, wszystko, co musimy zrobić, to stworzyć router nawigacyjny i sprawić, by aplikacja go wyrenderowała. Jeśli wszystko pójdzie dobrze, będziemy mieli działającą aplikację przeglądarkową Reddit /r/pics.
Android:
iOS:
React Native jest niesamowity!
Odkąd zacząłem programować, mam doświadczenie czysto mobilne. Ale teraz mogę za pomocą Reacta kodować praktycznie wszystko: mobilny, stacjonarny i webowy.
Jeśli zdecydujesz się rozpocząć tworzenie kolejnej niesamowitej aplikacji przy użyciu React Native, przekonasz się, że ma ona swoje dziwactwa i błędy tu i tam, ale React Native jest bardzo funkcjonalny i idealny do większości projektów.