Amplify JavaScript + React で一定期間ユーザー操作が無い場合に Cognito からサインアウトする実装


これは何?

  • Amplify JavaScript と React を使用して、一定期間ユーザーの操作が無い場合に Cognito からサインアウトする実装をしてみたメモ

ソースコード

import { Amplify, Auth } from 'aws-amplify';

import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';

import awsExports from './aws-exports';
import { useEffect, useState } from 'react';
Amplify.configure(awsExports);

export default function App() {
    const [timer, setTimer] = useState();

    useEffect(() => {
        const resetTimer = () => {
            if (timer) {
                clearTimeout(timer)
            }

            const newTimer = setTimeout(() => {
                console.log('Timeout!!!!!!!')
                Auth.signOut()
            }, 5000)

            setTimer(newTimer)
        }

        window.addEventListener('mousemove', resetTimer)

        return () => {
            window.removeEventListener('mousemove', resetTimer)

            if (timer) {
                clearTimeout(timer)
            }
        }
    }, [timer])

    return (
        <Authenticator>
            {({ signOut, user }) => (
            <main>
                <h1>Hello {user.username}</h1>
                <button onClick={signOut}>Sign out</button>
            </main>
            )}
        </Authenticator>
    );
}

コード解説

useState

    const [timer, setTimer] = useState();

useState • React
https://beta.reactjs.org/reference/react/useState

  • App コンポーネントで使用する state の定義
  • ユーザー操作が一定期間無いことをカウントするための timer と、 timer をセットするための setTimer

useEffect

    useEffect(() => {
        const resetTimer = () => {
            if (timer) {
                clearTimeout(timer)
            }

            const newTimer = setTimeout(() => {
                Auth.signOut()
            }, 5000)

            setTimer(newTimer)
        }

        window.addEventListener('mousemove', resetTimer)

        return () => {
            window.removeEventListener('mousemove', resetTimer)

            if (timer) {
                clearTimeout(timer)
            }
        }
    }, [timer])

useEffect • React
https://beta.reactjs.org/reference/react/useEffect


React 観点

  • App コンポーネントで動作する Effect の実装
  • 第一引数 setup: Effect のロジック
    • コンポーネントが DOM に追加されると setup 関数が実行される
    • 依存関係を更新して再レンダリングするたびに React によって古い値で cleanup 関数を実行し、新しい値で setup 関数を実行する
    • コンポーネントが DOM から削除される際にも cleanup 関数が実行される
    • cleanup を行う function を return しても良い
  • 第二引数 dependencies?: 依存関係
    • setup コードで参照される Reactive value のリスト
      • props, state, コンポーネントで宣言された変数や関数
      • 指定した値が更新された場合に effect が再実行される

        const resetTimer = () => {
            if (timer) {
                clearTimeout(timer)
            }

            const newTimer = setTimeout(() => {
                Auth.signOut()
            }, 5000)

            setTimer(newTimer)
        }

        window.addEventListener('mousemove', resetTimer)

        return () => {
            window.removeEventListener('mousemove', resetTimer)

            if (timer) {
                clearTimeout(timer)
            }
        }

setup logic 観点

  • resetTimer
    • 既存 timer が存在していれば clearTimeout でタイムアウト設定の解除
    • newTimer は setTimeout で新しいタイムアウトをセット
    • setTimer で newTimer を timer state にセット
  • addEventListener
    • アプリケーションとしてはグローバルなタイムアウトとして利用したいので window に対して addEventListener を行う
    • 今回は mousemove イベントを type として登録しているが、何を持ってユーザーの操作が無いとするかはアプリ要件によって異なるので、適宜カスタマイズする感じが無難だと思われる
  • return
    • useEffect における cleanup 処理
    • window に対して removeEventListener を行い、既存 timer があれば clearTimeout でタイムアウト設定を解除しておく