「Jotai」で状態管理を手軽に行う
最近業務外では React Native を使ったモバイルアプリ開発を行っています。
React Nativeに関してはかなりブランクがあったため、開発を行うにあたり、あらためて最新の情報を色々とキャッチアップしました。
その中で、状態管理ツールとして「Jotai」を選択し、実際に導入してみた感想です。
「Jotai」を導入することで、ビューとロジックの分離がスムーズに進み、可読性・保守性が向上しました。
※この記事はnote株式会社Advent Calendar 2024 9日目の記事です。
他にも連日楽しい記事が公開されますので、ぜひご覧ください。
状態管理ツールの選択
以前React Nativeで開発を行っていたときも、状態管理ツールの選択肢は複数ありました。
最近はどうなっているのかと、まずはnpm trendsを見てみました。
他にも候補はありそうでしたが、ぱっと名前が思い出せるものを並べてみたところ、Reduxはもちろん、Zustandも人気がありそうでした。
実は、きちんと調べる前は「まあRecoilでいいだろ」と思っていたのですが、どうやらRecoilはメンテが止まっているようで、新規プロジェクトの選択肢としては外した方が良さそうです。
今回のプロジェクトは比較的小規模の想定でもあり、Reduxは選択肢から外していました。
そうするとトレンド的にはZustandかなと思い、あらためて中身を見てみました。
チュートリアルを見た感じ、ZustandはReduxに近い書き方や扱い方ができるようで、導入はしやすそうです。
import { create } from 'zustand'
// Storeをhookとして作成する
// プリミティブ型、オブジェクト、関数、なんでも扱える
const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
updateBears: (newBears) => set({ bears: newBears }),
}))
// どこでもhookできる
// 状態が変化するとコンポーネントが再レンダリングされる
function BearCounter() {
const bears = useStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
Hookはどこからでも利用可能で、いわゆるプロパイダー的なものでラップする必要はないようです。
感覚的にはRecoilに近いようですし、ライブラリ自体も軽量で非常に扱いやすそうに感じました。
Jotaiとは
Jotaiの作者はZustandと同じ Daishi Kato さんで、こちらもRecoilに近しい感覚で扱えそうです。
JotaiはRecoilのようにatomを作っていきますが、Recoilと異なりユニークなkeyを設定する必要がありません。
単純なものであれば以下のように書くだけです。
import { atom } from 'jotai';
export const countAtom = atom(0)
export const lastUpdatedAtom = atom(new Date())
あるいはgetを使って他のatomを参照することもできます。
export const isPositiveAtom = atom(
(get) => get(countAtom) > 0
)
get、set、updateなどを利用して「atom同士を同期して取得・更新する」「他のatomの状態によって取得するデータを変える」といったロジックをatomとして定義できるため、ビューとの分離が非常にやりやすいです。
// ビュー側の利用イメージ
import { useAtom } from 'jotai'
import { SafeAreaView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
import { countAtom, isPositiveAtom, lastUpdatedAtom } from './atom'
const App = () => {
const [count, setCount] = useAtom(countAtom)
const [lastUpdated, setLastUpdated] = useAtom(lastUpdatedAtom)
const [isPositive] = useAtom(isPositiveAtom)
const handleIncrement = () => {
setCount(prev => prev + 1)
setLastUpdated(new Date())
}
const handleDecrement = () => {
setCount(prev => prev - 1)
setLastUpdated(new Date())
}
return (
<SafeAreaView style={styles.container}>
<Text style={styles.countText}>カウント: {count}</Text>
<Text style={[
styles.statusText,
isPositive ? styles.positiveText : styles.nonPositiveText
]}>{isPositive ? 'プラス' : '0またはマイナス'}</Text>
<Text style={styles.timeText}>
最終更新: {lastUpdated.toLocaleTimeString()}
</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.button}
onPress={handleIncrement}
>
<Text style={styles.buttonText}>増加</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={handleDecrement}
>
<Text style={styles.buttonText}>減少</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
)
}
// styleは省略
export default App
Jotaiの特徴
ドキュメントの充実
Jotaiはシンプルな使い方であれば、ドキュメントを熟読するまでもなく扱える気軽さがありました。
ドキュメントが非常に分かりやすい形で充実しており、しっかりと理解したうえで扱う場合でも、ハードルがとても低いのではないかと思います。
作者の方がZennなどで色々なテクニックを発信してくれている情報も参考になります。
atomの書きやすさ
基本的な使い方だけであればすぐに理解して使い始められる容易さもJotaiの特徴だと思います。
個人的には、実際に導入してみてもっともメリットに感じたことが「atomを作るときにkeyを設定する必要がない」ということかもしれません。
この辺りを考える必要なく、どんどんと書いていける体験だけでもJotaiを導入して良かったと感じました。
レンダリングの制御
Jotaiはatomを組み合わせて状態を構築しますが、レンダリングはatomの依存関係に基づいて自動的に最適化されます。
実際には複雑なatomの依存関係を作ってしまうと留意が必要なケースもありそうですが、今のところは非常に良い体験になっています。
ビューとロジックの分離が容易
前述しましたが、atomを使うことで、ロジック部分を綺麗にビューから分離できます。
この辺りは深く考えずとも、read、writeなatomをそれぞれ構築し、それをビューで利用することで自然とビューとロジックが分離できる感覚です。
AsyncStorageとの同期
もう1点、非常に便利なのが、React Nativeでローカルストレージとして利用するAsyncStorageと簡単に同期できることです。
`atomWithStorage` を利用してAsyncStorageを渡すだけで簡単にatomの状態を永続化できます。
こちらもAsyncStorageを生で利用するよりかなり使いやすくなりました。
Jotai良いぞ!
Jotaiにはこの他にも便利な機能が色々とありますし、継続的にアップデートがされています。
最初は「せっかくだから使ったことがないものを試しに使ってみるか」ぐらいの軽い気持ちで導入したのですが、Jotaiは想像以上に便利なライブラリでした。
状態管理ツールをお探しの方はぜひ一度Jotaiを試してみてください。
終わりに
noteのAdvent Calendarはこのあとも続きます。
ぜひお楽しみください!
また、note所属のエンジニアによる技術記事も随時更新しておりますので、こちらもよろしくお願いします。
noteではReact Nativeは導入しておらず、SwiftとKotlinでの開発になりますが、モバイルエンジニアを募集しています。
少しでもご興味あれば、まずはカジュアルにお話しできますので、こちらもよろしくお願いします!