React Native

React Native basics

2018/5/30

2019/05/05 (重新整理)

利用expo.io

https://snack.expo.io/ 是個不錯的起點,先下載expo到你的手機上,接下來在https://snack.expo.io/寫的程式碼,就可以直接在你的手機上看成果了 (詳參: Snack — A Playground for React Native)。

打開https://snack.expo.io/就會有一個範例程式檔 App.js:

import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Constants } from 'expo';

// You can import from local files
import AssetExample from './components/AssetExample';

// or any pure javascript modules available in npm
import { Card } from 'react-native-elements'; // Version can be specified in package.json

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.paragraph}>
          Change code in the editor and watch it change on your phone!
          Save to get a shareable url.
        </Text>
        <Card title="Local Modules">
          <AssetExample />
        </Card>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#34495e',
  },
});

https://snack.expo.io/中,並不需要

AppRegistry.registerComponent('testapp', () => ShowView);

利用VS Code

首先,先安裝react native tools

要安裝node.js,安裝後,根據React Native Getting Started,如果已經安裝了Android Studio或XCode,可以利用React Native CLI:

npm install -g react-native-cli

但是,如果是還沒有安裝Android Studio或XCode:

npm install -g expo-cli

以下範例是以expo為例。

就像使用create-react-app或create-react-native-app一樣,也可以利用expo init產生一個空白的專案:

expo init test-rn

expo init會產生一些設定檔,以及一個App.js,

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Open up App.js to start working on your app!</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

要執行這個專案的話

cd test-rn
npm start

就可以啟動expo的DevTools,DevTools會啟動瀏覽器http://localhost:19002/,讓我們可以利用安裝好的模擬器或利用expo提供的app測試我們寫好的app。

簡單的範例程式 Hello World

我們先參考 ReactNative開發入門教學(1)-HelloWorld 來做一些改變:

import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        
          <Text style={styles.welcome}>Hello World!</Text>

      </View>
    );
  }
}
const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    }
});

就會在手機上看到Hello World。

比起寫一個Android App或iOS App來說,這樣的程式其實是相當簡潔。

React native基本介面

加個按鈕

參考 ReactNative開發入門教學(2)-觸發事件 再來做一些改變,首先,將App.js改為:

import React, { Component } from 'react';
import App from './src/app';
export default class Main extends Component {
  render() {
    return (
          <App />
    );
  }
}

因為在snack.expo.io中,不使用AppRegistry,而是直接使用App.js。

接下來產生一個src目錄,並且新增一個app.js,就如同範例的內容一樣。(src/app.js)

import React, { Component } from 'react';
import {
    Text,
    View,
    TouchableHighlight,
    Button
} from 'react-native';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  _click() {
    alert('Clicked!');
  }
  render() {
    return(
      <View>
        <Button title="Try it!" onPress={this._click.bind(this)}/>
        <TouchableHighlight onPress={this._click.bind(this)}>
          <Text>HelloWorld!</Text>
        </TouchableHighlight>
      </View>
    );
  }
}

export default App;

把前一個範例中的StyleSheet加進來,會看起來好看一點:

import React, { Component } from 'react';
import {
    Text,
    View,
    TouchableHighlight,
    Button,
    StyleSheet
} from 'react-native';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  _click() {
    alert('Clicked!');
  }
  render() {
    return(
      <View style={styles.container}>
        <Button title="Try it!" onPress={this._click.bind(this)}/>
        <TouchableHighlight onPress={this._click.bind(this)}>
          <Text>HelloWorld!</Text>
        </TouchableHighlight>
      </View>
    );
  }
}
const styles = StyleSheet.create({
  container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    }
});

export default App;

程式改完之後,會自動reload。

這個範例用到兩個元件:

分頁

最好要把程式碼做一些基本的切割,例如,將HomeScreen 及 DetailsScreen切成兩個檔案放在src路徑下,App.js會改成:

// 2019/05/04 revised for react navigation 3.x 
// https://reactnavigation.org/docs/en/tab-based-navigation.html
import React from 'react';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import HomeScreen from './src/home';
import DetailsScreen from './src/details';

const TabNavigator = createBottomTabNavigator({
  Home: HomeScreen,
  Details: DetailsScreen,
});

export default createAppContainer(TabNavigator);

src/home.js

import React from 'react';
import { View, Text, Button } from 'react-native';
class HomeScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Home Screen</Text>
        <Button
          title="Go to Details"
          onPress={() => this.props.navigation.navigate('Details')}
        />
      </View>
    );
  }
}
export default HomeScreen;

src/details.js

import React from 'react';
import { View, Text, Button } from 'react-native';
class DetailsScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Details Screen</Text>
        <Button
          title="Go back"
          onPress={() => this.props.navigation.goBack()}
        />
      </View>
    );
  }
}
export default DetailsScreen;

**注意** 在exop.io會自動去下載用到的package,如果是使用vs code,就要手動安裝,先利用ctrl-C把npm停下來,執行

npm install react-navigation
npm start

使用ScrollView

使用List

import React from 'react';
import { StyleSheet, View, Text, FlatList } from 'react-native';
export default class FlatListScreen extends React.Component {
    render() {
        return ( <View style={styles.container}>
            <FlatList
              data={[
                {key: 'Devin'},
                {key: 'Jackson'},
                {key: 'James'},
                {key: 'Joel'},
                {key: 'John'},
                {key: 'Jillian'},
                {key: 'Jimmy'},
                {key: 'Julie'},
              ]}
              renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}
            />
          </View>);
    }
}
const styles = StyleSheet.create({
    container: {
     flex: 1,
     paddingTop: 22
    },
    item: {
      padding: 10,
      fontSize: 18,
      height: 44,
    },
  })

App.js加入FlatListScreen:

// revised for react navigation 3.x
// https://reactnavigation.org/docs/en/tab-based-navigation.html
import React from 'react';
//import { TabNavigator } from 'react-navigation';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import HomeScreen from './src/home';
import DetailsScreen from './src/details';
import FlatListScreen from "./src/FlatListScreen";

const TabNavigator = createBottomTabNavigator({
  Home: HomeScreen,
  Details: DetailsScreen,
  "Flat List": FlatListScreen
});

export default createAppContainer(TabNavigator);

react native elements提供的ListItem比FlatList、SectionList還好用,提供了不少的功能,如:avatar、icon、onPress,也可以跟FlatList合併使用。

src/listitemdemo.js

import React from 'react';
import { View, Alert } from 'react-native';
import { ListItem } from 'react-native-elements';
//https://github.com/react-native-training/react-native-elements/issues/349
const list = [
  {
    title: 'Appointments',
    info: 'more info about Appointments',
    icon: 'av-timer',
  },
  {
    title: 'Trips',
    info: 'more info about Trips',
    icon: 'flight-takeoff',
  },
];

export default class ListItemDemo extends React.Component {
  _onPressButton(item) {
    Alert.alert(item.info);
  }
  render() {
    return (
      <View>
        {list.map((item, i) => (
          <ListItem
            key={i}
            title={item.title}
            leftIcon={{ name: item.icon }}
            onPress={() => this._onPressButton(item)}
          />
        ))}
      </View>
    );
  }
}

src/listitemdemo2.js

import React from 'react';
import { View} from 'react-native';
import { ListItem } from 'react-native-elements';
//https://react-native-training.github.io/react-native-elements/docs/listitem.html

const list = [
  {
    name: 'Amy Farha',
    avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/ladylexy/128.jpg',
    subtitle: 'Vice President',
  },
  {
    name: 'Chris Jackson',
    avatar_url: 'https://s3.amazonaws.com/uifaces/faces/twitter/adhamdannaway/128.jpg',
    subtitle: 'Vice Chairman',
  },
];

export default class ListItemDemo2 extends React.Component {
  render() {
    return (
      <View>
        {list.map((l, i) => (
          <ListItem
            key={i}
            roundAvatar
            //this is the correct way:
            leftAvatar={{ source: { uri: l.avatar_url } }}
            //avatar={{ uri: l.avatar_url }}
            title={l.name}
            subtitle={l.subtitle}
          />
        ))}
     </View>
    );
  }
}

app.js

// revised for react navigation 3.x
// https://reactnavigation.org/docs/en/tab-based-navigation.html
import React from 'react';
//import { TabNavigator } from 'react-navigation';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import HomeScreen from './src/home';
import DetailsScreen from './src/details';
import FlatListScreen from "./src/FlatListScreen";
import ListItemDemo from "./src/listitemdemo";
import ListItemDemo2 from "./src/listitemdemo2";

const TabNavigator = createBottomTabNavigator({
  Home: HomeScreen,
  Details: DetailsScreen,
  "Flat List": FlatListScreen,
  "ListItem Demo": ListItemDemo,
  "ListItem Demo 2": ListItemDemo2
});

export default createAppContainer(TabNavigator);

Form

資料連接

JSON

import React from 'react';
import { FlatList, ActivityIndicator, Text, View  } from 'react-native';

export default class MovieScreen extends React.Component {

  constructor(props){
    super(props);
    this.state ={ isLoading: true}
  }

  componentDidMount(){
    return fetch('https://facebook.github.io/react-native/movies.json')
      .then((response) => response.json())
      .then((responseJson) => {

        this.setState({
          isLoading: false,
          dataSource: responseJson.movies,
        }, function(){

        });

      })
      .catch((error) =>{
        console.error(error);
      });
  }



  render(){
    //如果還沒完成,出現轉圈圈的AcitivityIndicator
    if(this.state.isLoading){
      return(
        <View style={{flex: 1, padding: 20}}>
          <ActivityIndicator/>
        </View>
      )
    }

    return(
      <View style={{flex: 1, paddingTop:20}}>
        <FlatList
          data={this.state.dataSource}
          renderItem={({item}) => <Text>{item.title}, {item.releaseYear}</Text>}
          keyExtractor={({id}, index) => id}
        />
      </View>
    );
  }
}

跟前面的範例整合起來:

// revised for react navigation 3.x
// https://reactnavigation.org/docs/en/tab-based-navigation.html
import React from 'react';
//import { TabNavigator } from 'react-navigation';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
//import HomeScreen from './src/home';
//import DetailsScreen from './src/details';
import FlatListScreen from "./src/FlatListScreen";
import ListItemDemo from "./src/listitemdemo";
import ListItemDemo2 from "./src/listitemdemo2";
import MovieScreen from "./src/movie";

const TabNavigator = createBottomTabNavigator({
  "Flat List": FlatListScreen,
  "ListItem Demo": ListItemDemo,
  "ListItem Demo 2": ListItemDemo2,
  Movie:MovieScreen
});

export default createAppContainer(TabNavigator);


參考資料