インストール

最初にライブラリをインストールしましょう。

$ npm i jotai

使用するバージョンです。

"jotai": "^2.5.1"
"react": "^18.2.0"

公式サイトはこちら。基本チュートリアルにあるやつです。

Jotai

簡単な使い方

最初に簡単なカウントプログラムの例からみてみましょう。

import { atom, useAtom } from 'jotai'

const countAtom = atom(0)

function App() {
  const [count, setCount] = useAtom(countAtom)
  const handleCountUp = () => setCount(prev => prev + 1);

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleCountUp}>+</button>
    </div>
  )
}

countAtomという定数でatomを作成している以外、ほとんどuseStateと使い方は変わりませんね。

コンポーネントを分割する

Jotaiを使いたい理由としては分割したコンポーネントから使いたいからだと思いますので分けてみます。
また、useAtomをそのまま使用してもいいですが、それぞれuseSetAtom, useAtomValueという読み書き専用のフックがあるのでこちらを使ってみます。

import { atom, useSetAtom, useAtomValue } from 'jotai'

const countAtom = atom(0)

function CountDisplay () {
    // const [count] = useAtom(countAtom)
    const count = useAtomValue(countAtom)
    return <p>{count}</p>
}

function CountButton () {
    // const [, setCount] = useAtom(countAtom)
    const setCount = useSetAtom(countAtom)
    const handleCountUp = () => setCount(prev => prev + 1)
    return <button onClick={handleCountUp}>+</button>
}

function App() {
    return (
        <div>
            <CountDisplay />
            <CountButton />
        </div>
    )
}

読み取り専用Atom

Atomを算出して表示したとき読み取り用のAtomを作成すると便利です。
試しにカンマ区切りで表示してみましょう。

const countAtom = atom(1000)
const countTextAtom = atom(get => get(countAtom).toLocaleString())

function CountDisplay () {
    const count = useAtomValue(countTextAtom)
    return <p>{count}</p>
}

書き込み専用Atom

読み取りとは逆に何かしらの算出をして保持するときは書き込み専用のAtomを作成します。
カウントアップの処理をAtomの処理に移行してみましょう。

const countUpAtom = atom(null, (_, set) => {
    set(countBaseAtom, prev => prev + 1)
})

function CountButton () {
    const setCount = useSetAtom(countUpAtom)
    return <button onClick={setCount}>+</button>
}

読み書き込み用Atom

atomの第一引数に読み取り用、第二引数が書き込み用なのでまとめて設定することもできます。

const countBaseAtom = atom(1000)

const countAtom = atom(
    get => get(countBaseAtom).toLocaleString(),
    (_, set) => {
        set(countBaseAtom, prev => prev + 1)
    }
)

function CountDisplay () {
    const count = useAtomValue(countAtom)
    return <p>{count}</p>
}

function CountButton () {
    const setCount = useSetAtom(countAtom)
    return <button onClick={setCount}>+</button>
}

Atomを関数にまとめる

Atomを再利用する場合は関数にまとめます。

import { atom, useSetAtom, useAtomValue, WritableAtom } from 'jotai'

function createCountAtoms(initialValue: number) {
    const baseAtom = atom(initialValue)
    return atom(
        get => get(baseAtom).toLocaleString(),
        (_, set) => {
            set(baseAtom, prev => prev + 1)
        }
    )
}

const countAtom1 = createCountAtoms(0)
const countAtom2 = createCountAtoms(1000)

type Props = {
    atom: WritableAtom<string, [], void>
}

function CountDisplay({atom}: Props) {
    const count = useAtomValue(atom)
    return <p>{count}</p>
}

function CountButton({atom}: Props) {
    const setCount = useSetAtom(atom)
    return <button onClick={setCount}>+</button>
}

function App() {
    return (
        <div>
            <CountDisplay atom={countAtom1} />
            <CountButton atom={countAtom1} />
            
            <CountDisplay atom={countAtom2} />
            <CountButton atom={countAtom2} />
        </div>
    )
}

以上。すごくシンプルに使えていい感じですね。