はじめに
前回の記事に続いて、 React Navigation v5 に関する記事です。
Version 5.x では、公式ドキュメントで推奨の認証フローが紹介されています。
今回は、その認証フローの実装方法に関して記事にまとめました。
下準備
認証フローを実装するにあたり、アプリ全体で状態を管理する必要があります。
この記事では、 Context API を利用して実装します。
型定義
利用する型を定義します。
必要に応じて、Error
などの状態・処理も追加してください。
type State =
| { status: 'Unauthenticated'; token: string }
| { status: 'Loading';}
| { status: 'Authenticated'; token: string };
type Action =
| { type: 'START_LOGIN' }
| { type: 'COMPLETE_LOGIN'; token: string }
| { type: 'COMPLETE_LOGOUT' };
type Dispatch = (action: Action) => void
Reducer
各アクションにおける取りうる状態を定義します。
const authReducer = (prevState: State, action: Action): State => {
switch (action.type) {
case 'START_LOGIN':
return {
...prevState,
status: 'Loading',
};
case 'COMPLETE_LOGIN':
return {
...prevState,
status: 'Authenticated',
token: action.token,
};
case 'COMPLETE_LOGOUT':
return {
...prevState,
status: 'Unauthenticated',
token: undefined,
};
}
};
コンテクスト
アプリの状態、ディスパッチャーそれぞれのコンテクストを定義します。
const AuthStateContext = createContext<State>({
status: 'Unauthenticated',
token: undefined,
});
const AuthDispatchContext = createContext<Dispatch | undefined>(undefined);
自作 Hooks
慣習に従い、useXXXX
の形で Hooks を定義します。
export const useAuthState = () => {
const context = React.useContext(AuthStateContext);
return context;
};
export const useAuthDispatch = () => {
const context = React.useContext(AuthDispatchContext);
return context;
};
プロバイダー
アプリ全体で状態を扱えるように、プロバイダーを定義し、値を伝播させます。
interface Props {
readonly children: React.ReactNode;
}
const AuthProvider: React.FunctionComponent<Props> = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
status: 'Unauthenticated',
token: undefined,
});
return (
<AuthStateContext.Provider value={state}>
<AuthDispatchContext.Provider value={dispatch}>
{children}
</AuthDispatchContext.Provider>
</AuthStateContext.Provider>
);
};
定義したプロバイダーをアプリに適用します。
const App: React.FunctionComponent = () => {
return (
<AuthProvider>
<NavigationContainer>
...
</NavigationContainer>
</AuthProvider>
);
};
ここまでで状態管理をするためのプロバイダーが準備できました。
子コンポーネントでuseAuthState
とuseAuthDispatch
が扱えるようになりました。
これで準備は完了です。
認証状態による表示画面の出し分け
認証処理を実装します。
const Stack = createStackNavigator();
const StackNavigator = () => {
const state = useAuthState();
if (state.status === 'Loading') {
return <LoadingScreen />;
}
return (
<Stack.Navigator
headerMode="none"
screenOptions={{ animationEnabled: false }}
>
{state.status === 'Authenticated' ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<Stack.Screen name="SignIn" component={SignInScreen} />
)}
</Stack.Navigator>
);
};
useAuthSate
でアプリの状態を取得することができます。
上記の例では、状態を元に処理を分けています。
状態がLoading
の場合(データの取得中など)には、LoadingScreen
を表示します。
それ以外の状態の時は、Stack.Navigator
を表示します。
ナビゲーターを表示する際には、状態がAutheticated
(認証済)の場合はHomeScreen
を、それ以外の場合はSignInScreen
を表示するように処理しています。
初期状態はUnauthenticated
からスタートします。
そのため、SignInScreen
に遷移します。
では、それぞれの画面はどのようになっているのか、見ていきます。
各画面の実装
ログイン画面
const SignInScreen: React.FunctionComponent = () => {
const dispatch = useAuthDispatch();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="SignIn"
onPress={() =>
dispatch({ type: 'COMPLETE_LOGIN', token: 'dummy-token' })
}
/>
</View>
);
};
ここではログインボタンだけを実装しています。
ログインボタンを押した際、アプリにCOMPLETE_LOGIN
の状態をディスパッチします。
※本来は認証チェックをすべてパスした場合に、状態をディスパッチすべきですが今回は省略します。
これにより、状態はAuthenticated
へと変化します。
新しい状態に変化すると、state
が更新されて、再度レンダリングが行われます。
status
はAuthenticated
になっているため、今度はHomeScreen
にナビゲーションされます。
ホーム画面
const HomeScreen: React.FunctionComponent = () => {
const dispatch = useAuthDispatch();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="SignOut"
onPress={() => dispatch({ type: 'COMPLETE_LOGOUT' })}
/>
</View>
);
};
ここではログアウトボタンだけを実装しています。
ボタンを押下すると、ログアウトした状態(COMPLETE_LOGOUT
)がディスパッチされます。
これにより、再びログインページへと遷移します。
おわりに
いかがだったでしょうか。
今回は複雑な処理は省略しましたが、状態でStack.Screen
を制御することで許可されていないスクリーンに遷移することを防ぐことができます。
また、サーバーとの通信中はローディング画面を表示する、など柔軟に表示内容を変更することができます。
React Navigation で認証ありのアプリを作成する場合、この認証処理も選択肢としてはありかもしれません。
もし記事に関して何かありましたら、ぜひコメントをよろしくお願いします。