React Native + Expo + TypeScriptで作るモバイルアプリ (Part1 - NativeBase編)
2019/10/30
Table of Contents
環境
- 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 ) を参照してください。
アプリが起動したら、以下のような画面になると思います。
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>
);
}
開発サーバーを起動して確認してみると以下のような画面になります。
外部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 />
);
}
アプリを確認してみましょう。これだけすでにいい感じの見た目になりましたね!
まとめ
Expoで簡単にReact Native開発を始めることができました。
さらに、NativeBaseを使えば綺麗なデザインのアプリもできそうです。
ちょっとしかコードを書いていないですがここまで作れるのすごいですね。とても開発効率や開発体験が良いと感じられます。
次回はReact Navigation を使って画面遷移の実装について書きたいと思います。