家計簿アプリを作ってみた
  • タグ画像

    ChatGPT

2024-11-18

目次

はじめに

皆さん、こんにちは!佐々木 遥加です。

今回は、React初心者の私が学習で作成した「家計簿アプリ」の制作時に引っかかった使用技術のバージョン更新について書いてみようと思います。

もともと「家計簿アプリ」を作成する際に参考にしたサイトが4年前のものと古く、当時の最新バージョンに合わせて作られていたので、現在のバージョンに沿ったコードに書き直す必要がありました。

そこで一部ではありますが、どの技術がどのように変わったのか記録として残しておきたいと思います。

家計簿アプリを製作するにあたって、私が参考にしていたサイトは「初心者がReact & Firebaseを使って収支管理アプリを作成(解説編)」です。

●使用技術
  React(version 18.3.1)
  Router(version 6.27.0)
  Hooks (useState, useEffect, useContext, useNavigate)
  Firebase(version 10.14.1)
  Authentication
  Cloud Firestore
  Hosting
  Material UI(version 6.1.4)

●ディレクトリ
.
└── src
├── auth
│ ├── AuthProvider.js
│ ├── Login.js
│ ├── PrivateRoute.js
│ └── Sign.js
├── components
│ ├── AddItem.js
│ ├── Balance.js
│ ├── ExpenseItem.js
│ ├── Header.js
│ ├── Home.js
│ ├── IncomeExpense.js
│ ├── IncomeItem.js
│ ├── ItemsLists.js
│ └── TotalIncomeExpense.js
├── firebase
│ └── firebase.js
├── App.css
├── App.js
├── back-img.jpg
└── index.js

アップデート箇所

①Firebaseのモジュール構造の変更

Firebase v9以降、モジュール構造が変更され、必要なメソッドだけインポートするモジュール形式に変わりました。これは、アプリのサイズを小さく保ち、必要な機能のみを読み込むことを目的としています。
修正前はfirebaseモジュールから直接authオブジェクトをインポートしていましたが、修正後はfirebase/authから関連する関数を個別にインポートするように変更しました。

【修正前コード】

import React, { useEffect, useState } from "react";
import { auth } from "../firebase/Firebase";

const AuthContext = React.createContext() 

const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);

  //サインアップ後認証情報を更新
  const signup = async (email, password, history) => {
    try { 
      await auth.createUserWithEmailAndPassword(email, password);
      auth.onAuthStateChanged(user => setCurrentUser(user));
      history.push("/");
    } catch (error) {
      alert(error);
    }
  };

  //ログインさせる
  const login = async (email, password, history) => {
    try {
      await auth.signInWithEmailAndPassword(email,password);
      auth.onAuthStateChanged(user => setCurrentUser(user));
      history.push("/");
    } catch (error) {
      alert(error);
    }
  }

  //初回アクセス時に認証済みかチェック
  useEffect(() => {
    auth.onAuthStateChanged(setCurrentUser);
  }, []);

  return (
    <AuthContext.Provider value={{ signup, login, currentUser}}>
      {children}
    </AuthContext.Provider>
  )
}

export {AuthContext, AuthProvider}

【修正後コード】

import React, { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, onAuthStateChanged } from "firebase/auth";
import { auth } from "../firebase/firebase";

const AuthContext = React.createContext ();

const useAuth = () => {
  return useContext(AuthContext);
};

const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);
  const navigate = useNavigate();

  //サインアップ後認証情報を更新
  const signup = async (email, password) => {
    try { 
      await createUserWithEmailAndPassword(auth, email, password);
      navigate("/");
    } catch (error) {
      alert(error);
    }
  };

  //ログインさせる
  const login = async (email, password) => {
    try {
      await signInWithEmailAndPassword(auth, email,password);
      navigate("/");
    } catch (error) {
      alert(error);
    }
  };

  //初回アクセス時に認証済みかチェック
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user);
    });
    return unsubscribe;
  }, []);

  return (
    <AuthContext.Provider value={{ signup, login, currentUser}}>
      {children}
    </AuthContext.Provider>
  );
};

export { AuthContext, AuthProvider, useAuth };

②React RouterのAPI変更

React Router v6以降では、historyオブジェクトが廃止され、useNavigateフックが導入されました。そのおかげで、よりReactのフックシステムに統合された形でページ遷移を扱えるようになりました。
修正前のコードでは、ページ遷移を扱うhistoryオブジェクトを使用していましたが、修正後のコードではuseNavigateフックに変更しました。

【修正前コード】

import React, { useEffect, useState } from "react";
import { auth } from "../firebase/Firebase";

const AuthContext = React.createContext() 

const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);

  //サインアップ後認証情報を更新
  const signup = async (email, password, history) => {
    try { 
      await auth.createUserWithEmailAndPassword(email, password);
      auth.onAuthStateChanged(user => setCurrentUser(user));
      history.push("/");
    } catch (error) {
      alert(error);
    }
  };

  //ログインさせる
  const login = async (email, password, history) => {
    try {
      await auth.signInWithEmailAndPassword(email,password);
      auth.onAuthStateChanged(user => setCurrentUser(user));
      history.push("/");
    } catch (error) {
      alert(error);
    }
  }

  //初回アクセス時に認証済みかチェック
  useEffect(() => {
    auth.onAuthStateChanged(setCurrentUser);
  }, []);

  return (
    <AuthContext.Provider value={{ signup, login, currentUser}}>
      {children}
    </AuthContext.Provider>
  )
}

export {AuthContext, AuthProvider}

【修正後コード】

import React, { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, onAuthStateChanged } from "firebase/auth";
import { auth } from "../firebase/firebase";

const AuthContext = React.createContext ();

const useAuth = () => {
  return useContext(AuthContext);
};

const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);
  const navigate = useNavigate();

  //サインアップ後認証情報を更新
  const signup = async (email, password) => {
    try { 
      await createUserWithEmailAndPassword(auth, email, password);
      navigate("/");
    } catch (error) {
      alert(error);
    }
  };

  //ログインさせる
  const login = async (email, password) => {
    try {
      await signInWithEmailAndPassword(auth, email,password);
      navigate("/");
    } catch (error) {
      alert(error);
    }
  };

  //初回アクセス時に認証済みかチェック
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user);
    });
    return unsubscribe;
  }, []);

  return (
    <AuthContext.Provider value={{ signup, login, currentUser}}>
      {children}
    </AuthContext.Provider>
  );
};

export { AuthContext, AuthProvider, useAuth };

③Material-UIのインポートパスの更新

Material UIはv5以降、パッケージ名が@material-ui/coreから@mui/materialに変更され、同時に多くのAPIが更新されました。ライブラリのブランディングとパッケージ構造の改善を目的としており、新しいインポートパスを使用することで最新のコンポーネントと機能にアクセスできるようになります。
この更新により、新しいスタイリング機能やコンポーネントが利用可能になり、開発者はより柔軟かつ効率的にUIを構築できるようになりました。

【修正前コード】

import React, { useContext } from 'react'
import { auth } from "../firebase/Firebase";
import { AuthContext } from '../auth/AuthProvider';
import { styled } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';

const SignOutButton = styled(Button)({
  background: '#C1C1C1',
  fontSize: '1.0rem',
  border: 0,
  borderRadius: 3,
  color: 'white',
  height: 30,
  padding: '0 10px',
  margin: '0 0 0 auto',
  display: 'block',
  '&:hover': {
    backgroundColor: '#B4B4B4',
  },
});

export const Header = ({date, setPrevMonth, setNextMonth}) => {

  const { currentUser } = useContext(AuthContext)

  const today = date;
  const year = today.getFullYear();
  const month = today.getMonth()+1;

  return (
    <div className="head">
      <SignOutButton onClick={() => auth.signOut()}>Sign Out</SignOutButton>
      <div className="showMonth">
        <button onClick={() => setPrevMonth()}>←前月 </button>
        <h1>{year}年{month}月</h1>
        <button onClick={() => setNextMonth()}> 次月→</button>
      </div>
    </div>
  )
}

【修正後コード】

import React from 'react'
import { auth } from "../firebase/firebase";
import { styled } from '@mui/material/styles';
import Button from '@mui/material/Button';

const SignOutButton = styled(Button) ({
    background: '#C1C1C1',
    fontSize: '1.0rem',
    border: 0,
    borderRadius: 3,
    color: 'white',
    height: 30,
    padding: '0 10px',
    margin: '0 0 0 auto',
    display: 'block',
    '&:hover': {
        backgroundColor: '#B4B4B4',
    },
});

export const Header = ({date, setPrevMonth, setNextMonth}) => {
    const today = date;
    const year = today.getFullYear();
    const month = today.getMonth() +1;

    return (
        <div className="head">
            <SignOutButton onClick={() => auth.signOut()}>Sign Out</SignOutButton>
            <div className="showMonth">
                <button onClick={() => setPrevMonth()}>←前月</button>
                <h1>{year}年{month}月</h1>
                <button onClick={() => setNextMonth()}>次月→</button>
            </div>
        </div>
    )
}

終わりに

以上が使用技術のバージョンアップによる変更点です。

他にも細かい変更があったり、適用箇所が膨大だったりと、エラーの解消にかなり時間を取られてしまいました。参考サイトの情報だけを鵜呑みにするのではなく、公式の情報や他のサイトの情報も参考にすることが大切ですね。

また、今回はChat GPTにもかなり助けられました!(自分で気づかない誤字を見つけてくれたり…)
今後もますますAIが手放せなくなります。

もし同じところで躓いている方がいたら、ぜひ参考にしてみてください!
最後までお読みいただきありがとうございました。