🐥
ネイティブアプリ開発日記(2/100)市井のアプリを再現
前回はこちら
今回は、市井のアプリを再現する。
題材として家計簿アプリを取り上げた。
- 要件定義
| # | 要件 | 説明 | 画面 |
|---|---|---|---|
| 要件1 | 支出一覧参照 | 登録した支出を一覧で参照できる | 画面1. 支出一覧参照画面 |
| 要件2 | 支出1件登録 | 支出を1件ずつ登録できる | 画面2. 支出1件登録画面 |
画面1の実装がこちら

画面2の実装がこちら

- 実装に関する学び
編集したファイルは下記
$ ls
App.js screens utils
$ ls ./screens/
AddExpenseScreen.js HomeScreen.js
$ ls ./utils/
storage.js
| # | ファイル | 説明 |
|---|---|---|
| ファイル1 | App.js | アプリ起動時に最初に実行されるエントリファイル |
| ファイル2 | HomeScreen.js | 画面1を表示するファイル |
| ファイル3 | AddExpenseScreen.js | 画面2を表示するファイル |
| ファイル4 | storage.js | データを保存する。画面2で書き込んだ内容を画面1で読み込む |
- ファイル1. App.js
- 画面を定義
- ライブラリ
- createNativeStackNavigator from '@react-navigation/native-stack'
- NavigationContainer from '@react-navigation/native'
- 最初に表示する画面を定義
- initialRouteName=画面1
- 画面一覧を定義
- <Stack.Screen name=画面1 component={画面1} />
- 「ナビゲーション・スタック」構造を使うと遷移先から遷移前の画面に戻れる。
- ライブラリ
- 画面を定義
App.js
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
(略)
// 画面を定義
const Stack = createNativeStackNavigator();
(略)
<NavigationContainer>
<Stack.Navigator initialRouteName=画面1>
<Stack.Screen name=画面1 component={画面1} />
<Stack.Screen name=画面2 component={画面2} />
</Stack.Navigator>
</NavigationContainer>
- ファイル2. HomeScreen.js
- 表示対象のデータ「支出一覧」の状態管理:useState from 'react'
- const [状態の変数, 状態を更新する関数] = useState(初期値);
- 状態の変数:「支出一覧」の現在の値:expenses
- 状態を更新する関数:「支出一覧」を更新する:SetExpenses()
- 初期値:「支出一覧」の初期値。空のリストを用いる
- const [状態の変数, 状態を更新する関数] = useState(初期値);
- 画面表示の確認:isFocused from '@react-navigation/native'
- const isFocused = useIsFocused();
- isFocused:Boolean。画面が表示されているとTrue、他画面に遷移するとFalse
- const isFocused = useIsFocused();
- コンポーネントの表示以外の処理:useEffect from 'react'
- useEffect(() => {実行したい処理}, [依存する値]);
- 第1引数:関数(副作用処理)
- 第2引数:依存配列(この中の値が変化したときだけ処理が再実行される)
- ロード処理:時間がかかるので非同期
- const fetchData = async () => {時間がかかる処理};
- 時間がかかる処理を待つ:const data = await ロード処理;
- 状態を更新する:setExpenses(data);
- 画面が表示されている時のみ、データ取得・状態更新を行う
- if (isFocused) { fetchData(); }
- useEffect(() => {実行したい処理}, [依存する値]);
- 画面表示:View from 'react-native'
- 文章:Text from 'react-native'
- 箇条書き:FlatList from 'react-native'
- ボタン:Button from 'react-native'
- 押下時の処理:onPress={()=>
- 画面2に遷移する:navigation.navigate(画面2)}
- 表示対象のデータ「支出一覧」の状態管理:useState from 'react'
HomeScreen.js
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, Button, StyleSheet } from 'react-native';
import { useIsFocused } from '@react-navigation/native';
(略)
// 表示対象のデータ「支出一覧」の状態管理
const [状態の変数, 状態を更新する関数] = useState(初期値);
// 画面表示の確認
const isFocused = useIsFocused();
(略)
// コンポーネントの表示以外の処理
useEffect(() => {
const fetchData = async () => {
const data = await ロード処理;
状態を更新する関数(data);
};
if (isFocused) {
fetchData();
}
}, [isFocused]);
(略)
// 画面表示
return (
<View>
<Text>支出一覧</Text>
<FlatList data={状態の変数} (略) />
<Button title="支出を追加" onPress={() =>
navigation.navigate(画面2)} />
</View>
)
- ファイル3. AddExpenseScreen.js
- 登録対象のデータの状態管理:useState from 'react'
- const [状態の変数, 状態を更新する関数] = useState(初期値);
- データの保存処理:時間がかかるので非同期
- const handleSave = async () => {時間がかかる処理};
- 時間がかかる処理を待つ:await 支出の保存処理();
- 前画面に戻る:navigation.goBack();
- 画面表示:View from 'react-native'
- 入力欄:TextInput from 'react-native'
- 登録対象のデータの状態管理:useState from 'react'
AddExpenseScreen.js
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
(略)
// 登録対象のデータの状態管理
const [状態の変数, 状態を更新する関数] = useState(初期値);
(略)
// データの保存処理
const handleSave = async () => {
const newExpense = {状態の変数};
await 支出の保存処理(newExpense);
navigation.goBack();
};
(略)
// 画面表示
return (
<View>
<Text>支出を追加</Text>
<TextInput
value={状態の変数}
onChangeText={状態を更新する関数}
/>
(略)
<Button title="保存" onPress={handleSave} />
</View>
)
- ファイル4. storage.js:AsyncStorage from '@react-native-async-storage/async-storage'
- 支出の保存処理
- 別ファイルにモジュールを提供するおまじない:export
- ストレージ処理:時間がかかるので非同期
- saveExpense = async() => {時間がかかる処理};
- 時間がかかる処理を待つ
- const ストレージ取得値 = await AsyncStorage.getItem(キー指定);
- await AsyncStorage.setItem(キー指定, 登録する値);
- ロード処理
- 別ファイルにモジュールを提供するおまじない:export
- ストレージ処理:時間がかかるので非同期
- loadExpense = async () => {時間がかかる処理};
- 時間がかかる処理を待つ
- const ストレージ取得値 = await AsyncStorage.getItem(キー指定);
- 支出の保存処理
util.js
import AsyncStorage from '@react-native-async-storage/async-storage';
(略)
// 支出の保存処理
export const saveExpense = async(入力された値) => {
try {
const ストレージ取得値 = await AsyncStorage.getItem(キー指定);
(略:登録する値 = f(ストレージ取得値, 入力された値))
await AsyncStorage.setItem(キー指定, 登録する値);
} catch (e) {
console.error('保存エラー:', e);
}
};
(略)
// ロード処理
export const loadExpense = async () => {
try {
const ストレージ取得値 = await AsyncStorage.getItem(キー指定);
(略)
} catch (e) {
console.error('読み込みエラー:', e);
return [];
}
};
- 次回の展望
今回作ったアプリを土台として、市井のアプリを再現し、実装の理解を深める
Discussion