🐥

ネイティブアプリ開発日記(2/100)市井のアプリを再現

に公開

前回はこちら
https://zenn.dev/nabeken/articles/f59508cae98a82

今回は、市井のアプリを再現する。
題材として家計簿アプリを取り上げた。

  1. 要件定義
# 要件 説明 画面
要件1 支出一覧参照 登録した支出を一覧で参照できる 画面1. 支出一覧参照画面
要件2 支出1件登録 支出を1件ずつ登録できる 画面2. 支出1件登録画面

画面1の実装がこちら

画面2の実装がこちら

  1. 実装に関する学び
    編集したファイルは下記
$ 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()
        • 初期値:「支出一覧」の初期値。空のリストを用いる
    • 画面表示の確認:isFocused from '@react-navigation/native'
      • const isFocused = useIsFocused();
        • isFocused:Boolean。画面が表示されているとTrue、他画面に遷移するとFalse
    • コンポーネントの表示以外の処理:useEffect from 'react'
      • useEffect(() => {実行したい処理}, [依存する値]);
        • 第1引数:関数(副作用処理)
        • 第2引数:依存配列(この中の値が変化したときだけ処理が再実行される)
      • ロード処理:時間がかかるので非同期
        • const fetchData = async () => {時間がかかる処理};
        • 時間がかかる処理を待つ:const data = await ロード処理;
        • 状態を更新する:setExpenses(data);
      • 画面が表示されている時のみ、データ取得・状態更新を行う
        • if (isFocused) { fetchData(); }
    • 画面表示:View from 'react-native'
      • 文章:Text from 'react-native'
      • 箇条書き:FlatList from 'react-native'
      • ボタン:Button from 'react-native'
        • 押下時の処理:onPress={()=>
        • 画面2に遷移する:navigation.navigate(画面2)}
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'
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 [];
    }
};
  1. 次回の展望
    今回作ったアプリを土台として、市井のアプリを再現し、実装の理解を深める

Discussion