LoginSignup
2
1

More than 1 year has passed since last update.

【Next.js】localStorageの期限設定とリダイレクト処理の実装

Posted at

はじめに

Next.jsでログイン処理時にログインAPIを呼び出し、レスポンスのログイン情報をlocalStorageに保持するような処理を実装しました。
しかし、このままではログアウトをしない限り、ほぼ永続的にデータが保持されてしまいます。
そこでlocalStorageに保持したログイン情報に有効期限を設定し、期限切れの場合はログイン画面へリダイレクトする処理を実装しました。

前提

Next.jsプロジェクトを作成済み
ログインAPIをバックエンドで実装済み
axiosインストール済み

環境

  • React 17.0.1
  • Next.js 10.2.3
  • lscache 1.3.0
  • tailwindcss 2.2.0

実装の流れ

1.プロジェクトにlscacheをインストール
2.ログイン画面の実装
3.ログイン用のフックを作成
4.ホーム画面とAboutページの実装
5._app.jsxの修正

1.プロジェクトにlscacheをインストール

$ yarn add lscache

2.ログイン画面の実装

Next.jsのファイルシステムルーティングの通り、下記パスにlogin.jsxファイルを作成。
ログイン画面を実装します。

src/pages/login.jsx
import Head from "next/head";
import { useState } from "react";
import { useAuth } from "src/hooks/useAuth";

const Login = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const { login } = useAuth();

  const onChangeEmail = (e) => {
    setEmail(e.target.value);
  };

  const onChangePassword = (e) => {
    setPassword(e.target.value);
  };

  const onClickLogin = () => {
    login(email, password);
  };

  return (
    <div className='bg-gray-500 flex flex-col items-center justify-center min-h-screen py-2'>
      <Head>
        <title>ログイン画面</title>
        <link rel='icon' href='/favicon.ico' />
      </Head>
      <main className='flex flex-col items-center justify-center w-full flex-1 px-20 text-center'>
        <div className='container mx-auto h-full flex flex-1 justify-center items-center'>
          <div className='w-full max-w-md'>
            <div className='leading-loose'>
              <div className='max-w-md m-4 p-10 bg-white bg-opacity-25 rounded shadow-xl'>
                <input
                  className='w-full px-5 py-1 text-gray-700 bg-gray-300 rounded focus:outline-none focus:bg-white'
                  type='email'
                  placeholder='メールアドレス'
                  required
                  value={email}
                  onChange={onChangeEmail}
                />
                <div className='mt-2'>
                  <input
                    className='w-full px-5 py-1 text-gray-700 bg-gray-300 rounded focus:outline-none focus:bg-white'
                    type='password'
                    placeholder='パスワード'
                    required
                    value={password}
                    onChange={onChangePassword}
                  />
                </div>
                <div className='mt-4 items-center flex justify-between'>
                  <button
                    className='px-4 py-1 text-white font-light tracking-wider bg-gray-900 hover:bg-gray-800 rounded'
                    onClick={onClickLogin}
                  >
                    ログイン
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </main>
    </div>
  );
};

export default Login;


ログインボタンを押下時に後述のログイン用のフックを呼び出して、ログイン処理をしています。

3.ログイン用のフックを作成

axiosでログインAPIを呼び出し、ログイン処理を行うフックを下記の通り実装します。
ここでlocalStorageに期限付きでログイン情報を保持します。

src/hooks/useAuth.js
import axios from "axios";
import { useRouter } from "next/router";
import lscache from "lscache";

export const useAuth = () => {
  const router = useRouter();

  const login = (email, password) => {
    axios
      // ご自身のログインAPIのURLを設定
      .post(`http://localhost:3001/auth/sign_in`, {
        email: email,
        password: password,
      })
      .then((res) => {
        if (res.data) {
          // ポイント① lscacheを用いてlocalStorageに期限付きでログインIDを保持
          lscache.set("loginId", JSON.stringify(resData.id), 1);
          router.push("/");
        } else {
          console.log("error");
        }
      })
      .catch(() => {
        console.log("catch");
      })
      .finally(() => {
        console.log("finally");
      });
  };
  return { login };
};


ポイント①箇所で期限設定とログイン情報の保持を行い、ホーム画面へ遷移させています。
第1引数、第2引数でレスポンスデータのIDをloginIdに保存し、第3引数で有効期限を設定しています。
上記のように「1」と設定すると1分間の有効期限が設定されます。

4.ホーム画面とAboutページの実装

動作確認しやすいように相互に行き来できるリンクを含めて実装します。

src/pages/index.jsx

import Head from "next/head";
import Link from "next/link";

const Home = () => {
  return (
    <div>
      <Head>
        <title>ホーム</title>
        <link rel='icon' href='/favicon.ico' />
      </Head>

      <div className='content'>
        <div className='flex items-center justify-between w-full my-4 pl-4 sm:pr-4'>
          <div className='mr-6'>
            <h2 className='text-3xl md:text-4xl font-semibold tracking-tight leading-7 md:leading-10 mb-1 truncate'>
              ホーム画面
            </h2>
            <div className='text-teal-600 '>
              <Link href='/about'>
                <a>Aboutページへ</a>
              </Link>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Home;
src/pages/about.jsx
import Head from "next/head";
import Link from "next/link";

const About = () => {
  return (
    <div>
      <Head>
        <title>About</title>
        <link rel='icon' href='/favicon.ico' />
      </Head>

      <div className='content'>
        <div className='flex items-center justify-between w-full my-4 pl-4 sm:pr-4'>
          <div className='mr-6'>
            <h2 className='text-3xl md:text-4xl font-semibold tracking-tight leading-7 md:leading-10 mb-1 truncate'>
              Aboutページ
            </h2>
            <div className='text-teal-600 '>
              <Link href='/'>
                <a>HOMEへ</a>
              </Link>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default About;

5._app.jsxの修正

_app.jsxではログイン時に保持したloginIdの有効期限が切れていた場合に削除処理を行い、ログイン画面へリダイレクトする処理を実装しています。
useEffectを用いて、画面遷移や画面リロードの際に上記の処理が動作するように実装しています。

src/pages/_app.jsx
import "tailwindcss/tailwind.css";
import { useEffect } from "react";
import lscache from "lscache";

const MyApp = ({ Component, pageProps, router }) => {
  // ポイント② useEffectを使用
  useEffect(() => {
    // ポイント③ ログインページへのアクセスの場合は処理をしない
    if (router.pathname === "/login") return;

    // ポイント④ 期限切れのデータを削除
    lscache.flushExpired();

    // ポイント⑤ loginIdが取得できない場合、ログインページへリダイレクト
    if (!lscache.get("loginId")) {
      router.push("/login");
    }
  }, [Component]);
  return (
    <>
      <Component {...pageProps} />
    </>
  );
};

export default MyApp;

ポイント②でuseEffectの第2引数にComponentを設定して、画面遷移や画面リロードなどの際に処理が走るようにしています。
ポイント③でログインページへのアクセスの際は処理を行わないようにしています。
ポイント④で有効期限の切れたストレージのデータを削除しています。
ポイント⑤loginIdのデータが取得できなかった場合、ログインページにリダイレクトするように実装しています。

動作確認

ログイン情報の状態をChromeのデベロッパーツールで確認しながら動作確認をしていきます。

1.next devでサーバを起動。

next dev

2.ログイン画面にアクセス

Chromeで 「http://localhost:3000/login」 にアクセスします。
デベロッパーツールを開き、「Application」パネルのLocal Storageの中身を表示します。
現在は空っぽの状態です。

スクリーンショット 2021-06-30 16.57.38.png

3.ログインと画面遷移

ログインすると画像のようにlscache-loginIdにデータが保持されています。
ホーム画面とAboutページを行き来してもlscache-loginIdは削除されず、画面遷移ができています。

スクリーンショット 2021-06-30 16.50.02.png

スクリーンショット 2021-06-30 16.50.30.png

4.1分後に画面遷移

1分後、画面遷移をするとログイン画面にリダイレクトし、
lscache-loginIdが削除されていることが確認できます。

スクリーンショット 2021-06-30 16.51.18.png

以上で動作確認完了です。

最後に

localStorageに期限を設定し、期限が切れている場合はログイン画面にリダイレクトする機能を実装していきました。
認証周りは将来的にはライブラリや認証サービスを使用するように修正するかもしれませんが、上記の技術での実装方法を記録として残します。
不備等ございましたら、ご指摘とご教授のほどお願いいたします。
ありがとうございました。

参考文献

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1