React で認証用の Context を作れたら便利そうだなと思ったので、開発イメージを掴むために Hooks も活用して認証用のコードを書いてみました。
Context と Hooks
Context はコンポーネントツリーでの所謂「バケツリレー」をスキップして子孫のコンポーネントに値を渡すことのできる機能です。
Hooks は Functional Component で State や Lifecycle などを使うことのできる機能です。
従来は Context を使うと Consumer の子要素では関数を埋め込む必要があって少々散らかった見た目になってしまいますが、 useContext
を使えば見た目がスッキリします。(ただしコンポーネント自身がすでに Provider
の中にあるという条件が増える)
Auth
話は変わり、 Redux で Auth 回りの実装をしていると、 Auth から取得できる情報が必要な処理、例えばログイン時にしか見られないページの制限やユーザーの名前を表示する場所、などが出てきます。これらが出てくる度に Redux の connect
でつなぐ必要があり面倒です。( React Redux の対応 で変わる可能性はある)
Hooks の公式ドキュメントにもあるように、 auth と Context 、 Context と Hooks の組み合わせは良いはずです。
ただし、今は Redux で API への認証用トークンが必要にであろうと思うので、トークンを利用する Redux と Auth 用 Context を共存するのは厳しいと思われます。
もし Redux を useReducer + Context で簡単に置き換えていたり Suspense for Data Fetching が安定版でリリースされたら便利な方法の一つになるかもしれません。
実装例
例のごとくサンプルプロジェクトを作りました。
Context を作る
Context を作るのはとても簡単ですが、初期値を何にすべきか考えるのは少々面倒です。
この辺を隠蔽できるようにモジュールを作れば便利そうではありますが。
15 | const context = React.createContext<AuthContext>({ |
Provider を作る
認証回りの機能だけを提供する Renderless Component として AuthProvider を定義します。
useState
により作った state から useMemo
を介して生成される info
と、 Provider コンポーネント内で定義した本物の login
と logout
関数を提供しています。詳しくはリンク先を参照のこと。
25 | <AuthContext.Provider value={{ login, logout, info }}> |
Consumer を使う
const { info, login, logout } = useContext(AuthContext);
と使って値(と関数)を得ます。他の記事でも注意書きされていますが、 useContext
へは AuthContext.Consumer
ではなく AuthContext
そのものを渡します。
これを AuthProvider
以下のコンポーネントの任意の場所に置くだけで、 info !== null
という判定で 認証時ページ を作ったり、 info!
でトークンやユーザ名を扱えたり、もちろん、 login
・ logout
によりログイン・ログアウトもできたりします。 connect
を使うよりもずっと簡単です。
毎回 useContext(AuthContext)
と書くのが面倒であれば、 useAuth
などというカスタムフックにして利用するのも手かと思います。