Reactの勉強がてらログイン機能を作ってみては?

React
Gerd AltmannによるPixabayからの画像

日本では今のところReactよりもVueの方が人気があるという話も聞きますが、

そんなことはどうでも良い!!

世界的にはReactは良く使われているし、公式のドキュメントもチュートリアルが分かりやすく
とっつきやすい印象があったのでちょっとばかし勉強してみました。

環境

  • OS: Windows10 home
  • エディタ: VSCode
  • ライブラリ: React ver17.0.1
  • 言語: Javascript
  • その他: firebase ver8.2.1
    React Router

ディレクトリ構成

studyin_app/
  ├ src/
  │   ├ components/
  │   │     ├ login.js
  │   │     └ mainPage.js
  │   ├ plugins/
  │   │     └ firebase.js
  │   ├ app.js
  │   ├ context.js
  │   └ index.js
  └ .env

※今回扱うファイルのみ表示しています。

また、Reactの環境の用意は公式ドキュメントに分かりやすくまとめられているので割愛します。

firebaseの用意

ログイン機能を実装するにあたってアカウントを用意する必要があります。

ログインユーザの用意と認証機能に関してはfirebaseの機能を使用するためまず準備します。

下画像のようにAuthenticationUsersにアカウントを追加します。

また、後に作るReactのアプリとfirebaseとを連携するために環境変数を用意します。

REACT_APP_API_KEY = "ここにfirebaseの設定に記載されているapiキーを入れる"
REACT_APP_AUTH_DOMAIN = "ここにfirebaseの設定に記載されているauth domainの値を入れる"
REACT_APP_PROJECT_ID = "ここにfirebaseの設定に記載されているproject IDを入れる"
REACT_APP_STORAGE_BUCKET = "ここにfirebaseの設定に記載されているstorage bucketを入れる"
REACT_APP_MESSAGING_SENDER_ID = "ここにfirebaseの設定に記載されているmessaging sender IDを入れる"
REACT_APP_APP_ID = "ここにfirebaseの設定に記載されているapp IDを入れる"

Create React Appの公式にもあるように環境変数を設定する場合はREACT_APP_から始まる変数名を設定する必要があるみたいですね。

次にReactアプリ内でfirebaseを使えるようにプラグインを自作します。

// studying_app/src/plugins/firebase.js
import firebase from "firebase/app";
import "firebase/auth";

var firebaseConfig = {  // 1
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_APP_ID
};

const initialized_fb = firebase.initializeApp(firebaseConfig);

export const auth = initialized_fb.auth();  // 2
  • 1: ここでfirebaseと接続するための設定をします。
    先ほど用意した環境変数から各値を入れ、初期化します。
  • 2: 認証機能を実装する際に必要なauthを用意、exportしておきます。

一旦ここまで準備できたらいよいよReactアプリの用意をします。

ログイン機能の実装

いきなりですが本命となるログイン機能を実装していきます。

// studying/src/components/login.js
import React from 'react';
import {
  Route,
  Redirect,
} from "react-router-dom";

import {auth} from '../plugins/firebase.js';
import AppContext from '../context.js';

class LoginForm extends React.Component {  // 1
  constructor(props) {
    super(props);
    this.state = {email: '', password: ''};

    this.getAuth = this.props.getAuth;  // 2
    this.getUserEmail = this.getUserEmail.bind(this);
    this.getUserPassword = this.getUserPassword.bind(this);
    this.activateViewer = this.activateViewer.bind(this);
  }

  getUserEmail(event) {  // 3
    this.setState({email: event.target.value});
  }

  getUserPassword(event) {  // 4
    this.setState({password: event.target.value});
  }

  activateViewer(event) {
    auth.signInWithEmailAndPassword(this.state.email, this.state.password)  // 5
    .then((response) => {
      this.getAuth(response.user.uid)  // 6
    })
    .catch((error) => {
      console.log(error);
    });
    event.preventDefault();  // 7
  }

  render() {
    return (
      <div>
        <form onSubmit={this.activateViewer}>
          <label>
            email:<input type="text" value={this.state.email} onChange={this.getUserEmail} />
            password: <input type="text" value={this.state.password} onChange={this.getUserPassword} />
          </label>
          <input type="submit" value="Submit" />
        </form>
      </div>
    );
  }
}
LoginForm.contextType = AppContext;  // 8


function RouteLogin(props) {
  let context = React.useContext(AppContext);  // 9
  return (  // 10
    <Route
      render={({ location }) =>
      context.user ? (
          <Redirect
            to={{
              pathname: '/',
              state: {from: location}
            }}
          />
        ) : (
          <LoginForm getAuth={props.getAuth} />
        )
      }
    />
  );
}

export default RouteLogin;
  • 1: フォームに入力されたメールアドレスとパスワードからfirebaseのauth機能を使ってログイン認証を行うコンポーネントを作ります。
    クラスで作る方法と関数で作る方法がありますが、ここではさまざまな機能を実装していくのでクラスで作ります。
  • 2: 詳しいことは後に説明しますが、外部から渡された認証ユーザーのデータをアプリで保存する際に使う関数をここで渡しています。
  • 3, 4: ここではフォームに入力された値をLoginForm内のstateに保存しています。
    この関数はinputタグのonchangeで使用するためフォームに文字を入力するたびに実行されます。
    そのため関数内で変数を用意して保存しようとしても一文字だけが上書き保存されてしまい、結果として最後に打ち込んだ一文字だけが認識されてしまいます。(経験済み)
    ですのでLoginFormコンポーネントのstate機能を使って単語ごとに保存できるようにしました。
  • 5: 最初に用意したfirebaseの実装ファイルがここで役に立ちます。
    firebaseに用意されているログイン機能はいろいろありますが、今回は無難にメールアドレスとパスワードで実装してみたいと思います。
    使い方は簡単でメールアドレス形式の文字列、8文字以上のパスワードを入れて実行するだけでfirebase上にそのアカウントが存在するかどうかを認証してくれます。
  • 6: LoginFormのconstructorで用意したgetAuthという関数を使い、firebaseから帰ってきたユーザーのデータを受け取ります。
    今回は簡単にユーザーIDだけを保存します。
  • 7: これを書かないとactivateViewerがすぐに終了してしまいうまく動作しません。
  • 8: ここではこのアプリ全体で使用できるcontextをLoginForm内でも使えるように設定しています。
    このコンテキストにはログインしたユーザーのデータを入れる訳ですがこのコンポーネント内からは入れることができません。
  • 9: ここも8と同じようにRouteLogin内でコンテキストを使えるように設定します。
  • 10: ログインしている場合にメインのページにリダイレクトし、まだログインしていない場合はログイン画面に遷移するように処理しています。
    また、ログイン画面に遷移する場合にfirebaseのログイン認証で取得したユーザIDをコンテキスト内に入れるための関数を渡します。

メイン画面の作成

次にログイン後のメイン画面を作っていきます。

この画面では特に複雑な処理は実装しないので簡単に進めます。

// studying_app/src/components/mainPage.js
import React from 'react';
import {
  Route,
  Redirect,
} from "react-router-dom";

import AppContext from '../context.js';


function MainPage() {
  return <h3>Main Page</h3>;
}

function RouteMainPage() {
  let auth = React.useContext(AppContext);
  return (
    <Route
      render={({ location }) =>
        auth.user ? (
          <MainPage/>
        ) : (
          <Redirect
            to={{
              pathname: '/login',
              state: {from: location}
            }}
          />
        )
      }
    />
  );
}

export default RouteMainPage;

RouteMainPage()の部分はRouteLogin()の時と大体同じなので説明は省きます。

コンテキストの作成

次にReactアプリ内全体で扱うコンテキストを用意します。

これはログインの認証情報等を入れて置くためのものです。

これを用意することでアプリ内で他のページに遷移してもログイン状態を維持することが可能になります。

// studying_app/src/context.js
import React from "react";

const app_context = React.createContext();
  // 1

export default app_context
  • 1: たったこれだけでコンテキストを用意することができました。
    後は、いろんなjavascriptファイル内でインポートしてuseContextするだけです。

仕上げ

後は、ここまで作ってきたコンポーネント等を加えて画面遷移できるようにappファイル、indexファイルを作っていきます。

// studying_app/src/app.js
import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Link,
} from "react-router-dom";

import AppContext from './context.js';
import RouteLogin from './components/login.js';
import RouteMainPage from './components/mainPage.js';


export class App extends React.Component {
  constructor(props) {
    super(props);

    this.getAuth = (authUser) => {
  // 1
      this.setState({user: authUser});
    };

    this.state = {
  // 2
      user: '',
    };
  }

  render() {
    return (
      <AppContext.Provider value={this.state}>
  // 3
        <Router>
          <div>
            <ul>
              <li>
                <Link to="/login">login</Link>
              </li>
              <li>
                <Link to="/">main page</Link>
              </li>
            </ul>

            <Switch>
  // 4
              <RouteLogin path='/login' getAuth={this.getAuth} />
              <RouteMainPage />
            </Switch>
          </div>
        </Router>
      </AppContext.Provider>
    );
  }
}
  • 1: 後に説明すると言っていた部分がここになります。
    アプリ内全体で使う値が2のstateにuserの値として保存するためにここで設定しています。
    このgetAuthをLoginページに遷移する際にpropsに渡してユーザーIDを取得し保存します。
  • 3: 遷移先のいろんなページでログインデータを共有するためにcontext.jsで用意したcontextを使います。
  • 4: 今まで作ってきたコンポーネントをここに書くことでLinkから飛んできた際にそれぞれのコンポーネントに遷移できます。
    またLinkと同じ順番でSwitch内の各コンポーネントは対応しています。
    (1つ目のLink to=”/login”と対応しているのはSwitch内で1番目のRouteLoginみたいな感じ)
    ただこれは恐らく、違う順番でも対応させる方法があると思いますが、まだ勉強中なのでとりあえず今はこの書き方で行きます。
    また、RouteLoginpath=’/login’と設定していますが、これ自体はあまり意味がありません。
    ただこれを付けないとどうにも上手くいかなかったのでとりあえずつけています。

もうちょっと調べないと・・・

// studying_app/src/index.js
import ReactDOM from 'react-dom';
import {App} from './app.js';

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);

出来上がったappをindexに設定し、アプリにアクセスしたときにすぐ表示されるようにしました。

実行

では実際に動かしてみましょう。

yarn start

上記のコマンドを実行するとブラウザに以下の画面が最初に表示されるかと思います。

Login、MainPageそれぞれのコンポーネント内でユーザーIDを持っていないときログイン画面にリダイレクトする処理を実装しているので、上の画像の段階ではlogin, main pageのリンクどちらをクリックしてもログイン画面が表示されます。

メールアドレス、パスワードそれぞれfirebaseで設定したものを入力し、submitすると下画像のメイン画面が表示されます。

また、この画面でlogin、main pageのどちらをクリックしても上のメイン画面が表示されます。

まとめ

以上でReactで勉強したことをアウトプットしてみました。
とてもシンプルなログイン機能でしたがまだまだ課題は残っています。
上記の方法だとログインした後にメイン画面を更新するとまたログイン画面に戻されてしまいます。
それ以外にも理解できていない部分がまだまだあるので勉強の進捗が進み次第新しく記事を投稿していこうと思います。
また、この記事が何かの参考になれば幸いです。

ここまで読んでくれてありがとう!!

以下のリンクが勉強に使った公式チュートリアルです

タイトルとURLをコピーしました