React Native

【ReactNative・React Navigation】JWTを用いた認証と画面の出しわけの基本

React Native
この記事は約9分で読めます。

認証で管理するStateの例

authSlice.ts

import { createSlice } from '@reduxjs/toolkit';

const authinInitialState = {
  isLoanching: true,
  isLoading: false,
  error: null,
  token: null,
};

export const authSlice = createSlice({
  name: 'login',
  initialState: authinInitialState,
  reducers: {
    loginStart: (state) => {
      state.isLoading = true;
    },
    loginSuccess: (state, { payload }) => {
      state.isLoanching = false;
      state.isLoading = false;
      state.error = null;
      state.token = payload;
    },
    loginFailure: (state, { payload }) => {
      state.isLoanching = false;
      state.isLoading = false;
      state.error = payload;
      state.token = null;
    },
    logout: (state) => {
      state.isLoading = false;
      state.error = null;
      state.token = null;
    },
  },
});

export const { loginStart, loginSuccess, loginFailure, logout } = authSlice.actions;

JWTを端末に保存するためのAsyncStrage

Installation | Async Storagehttps://react-native-async-storage.github.io

yarn add @react-native-async-storage/async-storage

JWTの保存、読み込み、削除など一通り用意しておき、認証時の処理に差し込む。services/deviceStrage.ts

import AsyncStorage from '@react-native-async-storage/async-storage';

const JWT_KEY = 'jwt_key';

export const deviceStorage = {
  async saveItem(key: string, value: string) {
    try {
      await AsyncStorage.setItem(key, value);
    } catch (error) {
      console.log('AsyncStorage Error: ' + error.message);
    }
  },

  async saveJWT(token: string) {
    try {
      await AsyncStorage.setItem(JWT_KEY, token);
    } catch (error) {
      console.log('AsyncStorage Error: ' + error.message);
    }
  },

  async loadJWT() {
    try {
      const value = await AsyncStorage.getItem(JWT_KEY);
      return value;
    } catch (error) {
      console.log('AsyncStorage Error: ' + error.message);
      return error;
    }
  },

  async deleteJWT() {
    try {
      await AsyncStorage.removeItem(JWT_KEY);
    } catch (error) {
      console.log('AsyncStorage Error: ' + error.message);
    }
  },
};

スプラッシュ画面がすぐに終了しないようにする

ライブラリを使用する
https://github.com/crazycodeboy/react-native-splash-screenreact-native-make/set-splash.md at master 揃 bamlab/react-native-makehttps://github.com

スプラッシュ画面は、JWTの読み込みが完了したタイミングで明示的に終了させる。

yarn add react-native-splash-screen

Android

ReactNative(0.63)以降は、自動でライブラリとリンクするので、特に設定は必要ない。
以下、onCreate(アプリ起動時の処理)のみ追加する。

これによって、明示的に指示しない限りスプラッシュスクリーンが終了しなくなる。

package com.myapp;

import com.facebook.react.ReactActivity;

追加
++ import android.os.Bundle;
++ import org.devio.rn.splashscreen.SplashScreen;


public class MainActivity extends ReactActivity {

追加
++  @Override
++  protected void onCreate(Bundle savedInstanceState) {
++    SplashScreen.show(this);
++    super.onCreate(savedInstanceState);
++  }

  @Override
  protected String getMainComponentName() {
    return "myApp";
  }
}

iOS

同じく起動時の処理に追加する。

これによって、明示的に指示しない限りスプラッシュスクリーンが終了しなくなる。

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
追加
++ #import "RNSplashScreen.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // ...other code
    [self.window makeKeyAndVisible];
追加
++  [RNSplashScreen show];

    return YES;
}

@end

Splashコンポーネントの作成

Splashコンポーネントといっても、画面には何も表示する必要はなく、
JWTの読み込みや、アプリ起動時に必要な処理を行う。

また、処理が終了した際にスプラッシュを非表示にする。

SplashScreen.tsx

import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import SplashScreen from 'react-native-splash-screen';
import { deviceStorage } from 'src/services/deviceStrage';

export default function () {
  const dispatch = useDispatch();

  useEffect(() => {
    deviceStorage
      .loadJWT()
      .then((jwt) => {
        dispatch(loginSuccess(jwt)); // isLoanchingをfalseにする
      })
      .catch((error) => {
        dispatch(loginFailure(error)); // isLoanchingをfalseにする
      })
      .finally(() => {
        SplashScreen.hide(); // 
      });
  }, [dispatch]);

  return null;
}

React Navigation の設定

Page Not Found | React Navigation

アプリ起動時、未ログイン(JWTなし)、ログイン後(JWTあり)の画面を出し分ける。

例はReduxでTokenを管理している場合。

navigation.tsx

export const RootNavigator = () => {
  const { isLoanching, token } = useSelector((state: RootState) => state.auth);

  if (isLoanching) return <SplashScreen />;

  return (
    <NavigationContainer>
      <RootStack.Navigator headerMode="none">
        {!token ? (
          <>
            <RootStack.Screen name="SignIn" component={SignInScreen} />
            <RootStack.Screen name="SignUp" component={SignUpScreen} />
          </>
        ) : (
          <>
            <RootStack.Screen name="AppBottomTab" component={AppBottomTabNavigator} />
            <RootStack.Screen name="ChatRooms" component={ChatRoomScreen} />
          </>
        )}
      </RootStack.Navigator>
    </NavigationContainer>
  );
};