👻

React状態管理ライブラリ Jotai の始め方 #JotaiFriends

2022/01/15に公開
2

Jotaiとは

軽くJotaiについての説明をします。React状態管理ライブラリの1つであるJotaiは、以下のような特徴があります。

  • ボトムアップアプローチ型の状態管理ライブラリ(単一のStateではなく複数のStateを自由にグローバル定義・利用する。1つのStateをAtomと呼称する)
  • ミニマルなAPI
  • AtomにユニークなKeyを設定する必要は無し(Recoilは必要)
  • Providerレスモードで利用可能
  • TypeScriptで開発されている

Jotaiコンセプトについてはこちらの記事で紹介しています。

インストール方法

$ npm install jotai
or 
$ yarn add jotai

Atomの定義方法(グローバルStateを定義)

AtomはStateの1つを表現します。atom(initialValue)と書くことでatomを定義します。
プリミティブな値から配列、オブジェクトまで自由に扱うことが出来ます。必要な数だけ定義可能です。

atoms.js
import { atom } from 'jotai'

export const countAtom = atom(0)
export const countryAtom = atom('Japan')
export const citiesAtom = atom(['Tokyo', 'Kyoto', 'Osaka'])
export const mangaAtom = atom({ 'Dragon Ball': 1984, 'One Piece': 1997, Naruto: 1999 })

もちろん、ファイル分割して好きに定義可能です。

Atomをコンポーネント内で使う

React.useState()と同じように使うことが出来ます。
const [count, setCount] = useAtom(countAtom)です。
(const [count, setCount] = useState(0)と比較するとまさにそれですね)

Counter.jsx
import { useAtom } from 'jotai'
import { countAtom } from '../atoms.js'

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount(c => c + 1)}>one up</button>
    </div>
  )
}

countAtomはグローバルです。他のコンポーネントでも同じ用に使うことが出来ます。

Derived Atomをつくってみる

Atomから派生Atomを作ることが出来ます。例えばcountAtomを派生元として、countAtomの値を2倍するAtomを定義して使ってみます。

DoubleCounter.jsx
import { countAtom } from '../atoms.js'

const doubledCountAtom = atom((get) => get(countAtom) * 2)

function DoubleCounter() {
  const [doubledCount] = useAtom(doubledCountAtom)
  return <div>{doubledCount}</div>
}

先程はatomの定義に値を指定しましたが、今回はread-only atomをread関数((get) => get(countAtom) * 2)を使って定義しています。

Writable Derived Atomって?

とあるatomを派生元として編集可能なatomを作ることが出来ます。どういうことでしょう。

Counter.jsx
const countAtom = atom(0)
const addingCountAtom = atom(
  (get) => get(countAtom), // read関数ではそのままcountAtomの値を返す
  (get, set, num) => { // write関数では与えられた値を加算する
    set(countAtom, get(countAtom) + num)
  }
)

function Counter() {
  const [count, add] = useAtom(addingCountAtom)
  return (
    <div>
      <div>{count}</div>
      <button onClick={() => add(Math.random())}>Add random number</button>
    </div>
  )
}

atom()には、初期値となる値、read関数、read関数とwrite関数、の3種類の引数をとれます。
Writable derived atomとはread関数とwrite関数を与えたatom()のことを言います。使い方でイメージしやすいのはuseReducer()でしょうか。

Write only atoms

read関数にnullを与えればWrite-onlyなatomを定義することが出来ます。何かのatomを色々と操作したいときに便利です。

const multiplyCountAtom = atom(null, (get, set, by) => set(countAtom, get(countAtom) * by))

function Controls() {
  const [, multiply] = useAtom(multiplyCountAtom)
  return <button onClick={() => multiply(3)}>triple</button>

Derived async atoms

非同期な処理をするatomを定義できます。(この書き方は、少なくとも1つの<Suspense>でラップしたコンポーネントツリー内でatomを使う必要があります)

const urlAtom = atom("https://json.host.com")
const fetchUrlAtom = atom(
  async (get) => { // read関数をasync関数とできる
    const response = await fetch(get(urlAtom))
    return await response.json()
  }
)

function Status() {
  // 上記のasync関数が終了次第、再レンダリングされます
  const [json] = useAtom(fetchUrlAtom)

Async actions

asyncなwrite関数も定義可能です。(コード内でasyncなread関数を使わず、write関数はasyncにしたい場合、Suspenseでラップする必要はありません)

const fetchCountAtom = atom(
  (get) => get(countAtom),
  async (_get, set, url) => {
    const response = await fetch(url)
    set(countAtom, (await response.json()).count)
  }
)

function Controls() {
  const [count, compute] = useAtom(fetchCountAtom)
  return <button onClick={() => compute("http://count.host.com")}>compute</button>

おわりに

いかがでしたか。シンプルで簡単ですよね?(語気強め)
昔々、Reduxを始める時を思い返すとこんな簡単でいいのかと疑ってしまうほどです。
欠点を上げるならば、自由すぎることでしょうか。でもそこはエンジニアの皆さんの腕の見せ所ですね、俺の考えたさいきょうの◯◯を見つけてください💪
冗談はさておき、ぜひ一度使ってみてください。特にContext地獄になってきたけどReduxは入れたくない・・・となっている方にはもってこいかと思います。

Jotaiの紹介特集について

この記事はJotai FriendsによるJotai紹介特集記事の1つです。記事一覧はこちらからどうぞ。

Jotai Friendsとは

いちJotaiファンとして、エンジニアの皆さんにもっとJotaiを知ってもらって使ってもらいたい、そんな思いから立ち上げたのがJotai Friendsです。

https://jotaifriends.dev/

現在まだまだ準備中ですが今後ともよろしくお願いします!
(ご興味持っていただけた方は是非jotaifriends.devにてEメールアドレスのご登録をお願いします🙏)

Jotai Friends

Discussion

ちぇんちぇん

jotaiいいですよね〜。自分も個人開発では全部jotaiです

Teruhisa - T6ADEVTeruhisa - T6ADEV

いいですよね〜😄 全部jotai!すごい!
使ってるぜ勢力が増えてほしいです。