React x TS x SessionStrage
フロントエンド開発で、セッションストレージを使ってゴニョゴニョすることってありますよね。実装自体は簡単で単に set して get するだけです。ただ、値が一旦 String に置き換えられる性質上、なんというか、気持ち悪い実装になることが多いです。実務ではこんな感じの実装をしているのでメモしておきます。
概要
実装は React x TypeScript で行います。ただ、実装の性質上 Vue や Angular でも少し方法を変えるだけで使用できると思います。処理が高速になったりするわけではないのですが、コードの見通しが良くなることや、React でカスタムフック化することで useState のように使用できることが良い点です。また、コード上では Bool 値で使用することで見通しが良くなるという感じです。
実装
実装は以下のとおりです。ライトモード/ダークモードの切り替えを想定してみました。
import { useCallback, useMemo, useState } from 'react'
const BROWSER_THEME_KEY = 'BROWSER_THEME_KEY'
export const useBrowserTheme = () => {
const [browserThemeValue, setBrowserThemeValue] = useState(
sessionStorage.getItem(BROWSER_THEME_KEY) ?? 'light',
)
const isLightMode = useMemo(() => {
return browserThemeValue === 'light'
}, [browserThemeValue])
const setBrowserTheme = useCallback((theme: 'light' | 'dark') => {
setBrowserThemeValue(theme)
sessionStorage.setItem(BROWSER_THEME_KEY, theme)
}, [])
return [isLightMode, setBrowserTheme] as const
}
一つずつ見ていきましょう。import とセッションストレージの key はそのままですね。constants.ts
などのファイルを別で用意してあげて、そちらにまとめておくのも良いでしょう。
カスタムフックの中身についてです。まずは、内部で使うステートを定義しています。こちらの初期値にはセッションストレージの値が入る想定です。つまり、クライアントがライトモードを選択していればlight
が入るし、ダークモードを選択していればdark
が入ります。注意しなければならないのが、何も設定されていない場合はnull
が入る点です。そこを考慮して、Nullish coalescing を使用しています。つまり、null の場合のみlight
が初期値に入るというわけです。isDark
と判定したい場合はこの部分は必要ない書き方もできますが、ちょっとわかりにくのでこの方が個人的には好きです。
次に、isLightMode
関数です。これは見ての通り、useState の値がlight
の場合はtrue
を返します。tsx などの実際に使用するコンポーネント側ではこちらの Bool 値を使用することになります。props として渡して css を書き換えるような使い方ですかね。いずれにせよ Bool 値の方が使いやすいのでこの方が良いと思います。またこちらは useMemo を使用していますので、deps の値が更新された場合のみ実行されます。
次に、setBrowserTheme
関数です。こちらも見ての通りです。まず、引数は型で縛っておりlight
or dark
しか受け付けないようにしています。実際セッションストレージを書き換えられるとその他の値も入るのですが、そこは本質とずれるので考えないことにします。受け取った値は最初に useState に渡されます。ステートが更新されたのでisLightMode
が発火し実際に使用されている Bool 値が書き換わります。次に、その値をセッションストレージに保存します。このとき、型で縛っているのでそれ以外の値が入ることは通常のフローではありえません。
最後に、return される値と関数ですが、これらをas const
しています。これは TypeScript の機能で、一言でいうと ReadOnly にする機能です。詳しく知りたい方はこちらを見てください。要は開発中に誤ってこの値を書き換える処理を別で入れたりすると怒られるということです。これで、正常なフローでしか値が変わらないことを担保しつつ、必要なロジックはすべてカスタムフック内に閉じ込めることができました。
使うときはこんな感じです。
const [isLightMode, setBrowserTheme] = useBrowserTheme()
useState ライクに使用できて見通しよくなりますよね。VScode で開発している方ならセット関数に hover すると何が入るべきか教えてくれるのでさらにいい感じです。また、true
or false
を String で受け取ることもできますが、セッションストレージの性質上意味の通る値を受け取ったほうが何となく気持ちが良いかなと思います。この辺りはお好みで。
これ結構良くないですか?個人的には結構気に入っています。