Top View


Author Daiki Urata

React Native + Expo + TypeScriptで作るモバイルアプリ (Part1 - NativeBase編)

2019/10/30

環境

  • Node.js v10.15.0
  • Expo CLI v3.3.0

プロジェクトを作成する

Expo CLIをインストール、プロジェクトを作成します。

$ npm install -g expo-cli
$ expo init MyApp

色々聞かれます。今回は、

  • Template: blank (TypeScript)
  • Package Manager: npm

を選択しました。

選択や入力が終わると依存関係のインストールが始まります。

それが終わったら試しにアプリを動かしてみましょう。

$ cd MyApp
$ npm start

すると、QRコードやアドレスがブラウザ上で表示されます。

実機で確認したい場合は Expo Client アプリをストアからインストールしてQRコードをカメラアプリで読み取ることで動作を確認できます。

またはブラウザで表示されたページからエミュレータの起動などができます。

詳細な説明はここでは省きますので、知りたい方はドキュメント( iOS Simulator / Android Studio Emulator ) を参照してください。

アプリが起動したら、以下のような画面になると思います。

First screen when creating a new expo project

NativeBaseをインストール

NativeBaseは様々なUIコンポーネントを提供しているライブラリで、簡単に見た目の綺麗なアプリが作ることができます。

$ npm install native-base
$ expo install expo-font

NativeBaseではExpo SDKの expo-font が必要なのでインストールしています。

レイアウトを作成

NativeBaseのインストールが完了したので、レイアウトを作ってみます。

src/Layout.tsxを作成します。

// src/Layout.tsx
import React from "react";
import {
  Container,
  Header,
  Title,
  Content,
  Footer,
  FooterTab,
  Button,
  Body,
  Icon
} from "native-base";

interface IProps {
  children: React.ReactNode;
}

const Layout = (props: IProps) => {
  return (
    <Container>
      <Header>
        <Body>
          <Title>My App</Title>
        </Body>
      </Header>
      <Content>{props.children}</Content>
      <Footer>
        <FooterTab>
          <Button
          >
            <Icon name="home" />
          </Button>
          <Button
          >
            <Icon name="camera" />
          </Button>
          <Button
          >
            <Icon name="bookmark" />
          </Button>
        </FooterTab>
      </Footer>
    </Container>
  );
};

export default Layout;

App.tsxも編集します。

// App.tsx
import React from 'react';
import { Text } from 'react-native';
import Layout from './src/Layout'

export default function App() {
  return (
    <Layout >
      <Text>Hello Expo!</Text>
    </Layout>
  );
}

開発サーバーを起動して確認してみると以下のような画面になります。

Layout using NativeBase components

外部APIからデータを取得表示する

レイアウトが完成したので、中身を作っていきます。

Fetch API を使って外部APIから画像データを取得、NativeBaseのコンポーネントを使って表示してみます。

まずはカードリストを表示するための src/components/CardList.tsx を作成します。

// src/components/CardList.tsx

import React, { useContext } from "react";
import {
  Button,
  Left,
  Right,
  Body,
  Icon,
  Text,
  Card,
  CardItem,
  Thumbnail
} from "native-base";
import { Image } from "react-native";

export interface Photo {
  download_url: string;
  id: string;
  author: string;
}

interface IProps {
  photos: Photo[];
}

const CardList = (props: IProps) => {
  return (
    <>
      {props.photos.map(photo => (
        <Card key={photo.id}>
          <CardItem>
            <Left>
              <Thumbnail
                source={{
                  uri: `https://na.ui-avatars.com/api/?name=${photo.author.replace(
                    "¥s",
                    "+"
                  )}`
                }}
              />
              <Body>
                <Text>My Image</Text>
                <Text note>{photo.author}</Text>
              </Body>
            </Left>
          </CardItem>
          <CardItem cardBody>
            <Image
              source={{ uri: photo.download_url }}
              style={{ height: 200, width: null, flex: 1 }}
            />
          </CardItem>
          <CardItem>
            <Left>
              <Button
                transparent
              >
                <Icon active name="thumbs-up" />
              </Button>
            </Left>
            <Body>
              <Button transparent>
                <Icon active name="chatbubbles" />
                <Text>4 Comments</Text>
              </Button>
            </Body>
            <Right>
              <Text>11h ago</Text>
            </Right>
          </CardItem>
        </Card>
      ))}
    </>
  );
};

export default CardList;

次にホーム画面の src/HomeScreen/HomeScreen.tsx を作成します。

// src/HomeScreen/HomeScreen.tsx

import React, { useState, useEffect } from "react";
import Layout from "../Layout";
import CardList from "../components/CardList";
import { Spinner } from "native-base";

// 画像読み込み
const getPhotos = () => {
  return fetch("https://picsum.photos/v2/list")
    .then(res => res.json())
    .catch(err => console.error(err));
};

const HomeScreen = () => {
  const [photos, setPhotos] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    let unmounted = false;
    const init = async () => {
      const photos = await getPhotos();
      if (!unmounted) {
        setLoading(false);
        setPhotos(photos);
      }
    };
    init();

    // Cleanup関数
    return () => {
      unmounted = true;
    };
  }, [photos]);
  const content = loading ? <Spinner /> : <CardList photos={photos} />;
  return <Layout>{content}</Layout>;
};

export default HomeScreen;

ここではHooks APIを使ってphotosやloadingの状態管理を行なっています。 ポイントとしてCleanup関数を利用する事で、HomeScreenコンポーネントから離れる(unmounted)タイミングでsetPhotos()やsetLoading()などの状態セットを行なってエラーにならないようにしています。

最後にApp.tsx を編集します。

// App.tsx

import React from 'react';
import HomeScreen from './src/HomeScreen/HomeScreen'
export default function App() {
  return (
    <HomeScreen />
  );
}

アプリを確認してみましょう。これだけすでにいい感じの見た目になりましたね!

Card list using NativeBase components

まとめ

Expoで簡単にReact Native開発を始めることができました。

さらに、NativeBaseを使えば綺麗なデザインのアプリもできそうです。

ちょっとしかコードを書いていないですがここまで作れるのすごいですね。とても開発効率や開発体験が良いと感じられます。

次回はReact Navigation を使って画面遷移の実装について書きたいと思います。

Daiki Urata

Daiki Urata

Twitter X

フロントエンド/モバイルアプリなどを主に開発しています。