👻

React状態管理ライブラリ Jotai のPrimitives(必要最小限のcore APIの3つ)の概要 #JotaiFriends

2022/01/16に公開

タイトルがPrimitivesということですが、日本語的には「必要最小限のcore APIの3つ」らしいです。https://jotai.org/docs/api/core には、各APIの説明+αがあります。

State in Jotai is a set of atoms. An atom is a piece of state. Unlike useState in React, atoms are not tied to specific components. Let's see how to define and use atoms.

Jotaiにおける状態とはatomの集まりです。atomは状態の一部分です。ReactのuseStateとは異なり、アトムは特定のコンポーネントに縛られることはありません。では、アトムの定義と使い方をみていきましょう。

CounterコンポーネントのためだけのcounterAtom、とかではないですね。

atom

There's an exported function called atom, which is to create an atom config. We call it "config" as it's just a definition and it doesn't hold a value. We may just call it "atom" if the context is clear.
To create a primitive atom (config), all you need is pass an initial value.

atomというエクスポートされた関数がありますが、これはatom configを作成するためのものです。これは単なる定義で、値を保持しないので「config」と呼んでいます。文脈が明確な場合は、単に「atom」と呼ぶこともあります。
原始的なアトム(config)を作るには、初期値を渡すだけです。

configと書かれていますが、実際に使うときは初期値を与えることが多いと思います。

import { atom } from 'jotai'

const priceAtom = atom(10)
const messageAtom = atom('hello')
const productAtom = atom({ id: 12, name: 'good stuff' })

You can also create derived atoms. We have three patterns.

  • Read-only atom
  • Write-only atom
  • Read-Write atom

派生atomを作ることも可能です。3つのパターンを用意しました。

  • 読み込み専用atom
  • 書き込み専用atom
  • 読み書きatom

To create derived atoms, we pass a read function and an optional write function.

派生atomを作るために、読み込み関数とオプションの書き込み関数を渡します。

readOnlyAtomの引数には関数1つ(atomの値を読む用)、writeOnlyAtomは読み取り用関数はnullにして書き込み用関数を、readWriteAtomは2つとも与えればよいです。

const readOnlyAtom = atom((get) => get(priceAtom) * 2)
const writeOnlyAtom = atom(
  null, // it's a convention to pass `null` for the first argument 訳)最初の引数に `null` を渡すのが慣例です。 
  (get, set, update) => {
    // `update` is any single value we receive for updating this atom 訳) `update`は、このアトムを更新するために受け取る任意の単一の値です。
    set(priceAtom, get(priceAtom) - update.discount)
  }
)
const readWriteAtom = atom(
  (get) => get(priceAtom) * 2,
  (get, set, newPrice) => {
    set(priceAtom, newPrice / 2)
    // you can set as many atoms as you want at the same time 訳)同時にいくつでもatomを設定できます
  }
)

get in the read function is to read the atom value. It's reactive and read dependencies are tracked.
get in the write function is also to read atom value, and it's not tracked. Furthermore, it can't read unresolved async values. For async behavior, please refer to the async doc.
set in the write function is to write atom value. It will invoke the write function of the target atom.

read関数でのgetはatomの値を読むことです。反応的でreadの依存関係は追跡されます。
write関数でのgetもatomの値を読むことで、追跡はしません。さらに、未解決の非同期の値は読めません。非同期の動作については、asyncのドキュメントを参照してください。
write関数のsetは、atomの値を書き込むことです。対象atomの書き込み関数が呼び出されます。

write関数内では自由にatomを読み、書き込みできます。一見無関係そうなatomも問題なく読み書きできます。

Atom configs can be created anywhere, but referential equality is important. They can be created dynamically too. To create an atom in render function, useMemo or useRef is required to get a stable reference. If in doubt about using useMemo or useRef for memorization, use useMemo.

Atom configはどこでも作成可能ですが、参照一致が重要です。また、動的に作成することもできます。レンダー関数でatomを作成するには、安定した参照を得るために useMemo または useRef が必要です。メモのために useMemouseRef を使用するかどうか迷ったら、useMemo を使用しましょう。

コンポーネント内で動的にatomを生成することは稀だとおもいますが、頭の片隅においておきましょう。

const Component = ({ value }) => {
  const valueAtom = useMemo(() => atom({ value }), [value])
  // ...
}

useAtom

The useAtom hook is to read an atom value in the state. The state can be seen as a WeakMap of atom configs and atom values.

useAtom hookは、ステートにあるatomの値を読み込むためのものです。ステートは、atom configとatomの値のWeakMapと見なすことができます。

The useAtom function returns the atom value and an updating function as a tuple, just like React's useState. It takes an atom config created with atom().

useAtom関数は、ReactのuseStateのように、atomの値と更新関数をタプルとして返します。atom()で作成したatom configを受け取ります。

見比べてみましょう。

const [count, setCount] = useState(0);

// const countAtom = atom(0);
const [count, setCount] = useAtom(countAtom);

Initially, there is no value stored in the state. The first time the atom is used via useAtom, the initial value is stored in the state. If the atom is a derived atom, the read function is executed to compute an initial value. When an atom is no longer used, meaning all the components using it is unmounted, and the atom config no longer exists, the value in the state is garbage collected.

初期状態では、ステートには何も値が保存されていません。useAtomで初めてアトムが使われたとき、初期値がstateに格納されます。atomが派生atomの場合、read関数が実行され、初期値が計算されます。atomが使用されなくなったとき、つまりatomを使用しているコンポーネントがすべてアンマウントされ、atom configが存在しなくなったとき、ステートの値はガベージコレクションされます。

const [value, updateValue] = useAtom(anAtom)

The updateValue takes just one argument, which will be passed to the third argument of write function of the atom. The behavior totally depends on how the write function is implemented.

updateValueは引数を1つだけ取り、atomのwrite関数の第3引数に渡されます。その動作は書き込み関数がどのように実装されるかに完全に依存します。

updateValue()は以下のように使うことが出来ます。

<input type="text" onChange={(e) => updateValue(e.targe.value)} />
or
<input type="text" onChange={(e) => updateValue((s) => e.targe.value + s)} />

Provider

Provider is to provide a state for component sub tree. Multiple Providers can be used for multiple subtrees, even nested. This works just like the normal React Context.

Providerとは、コンポーネントのサブツリーに対してステートを提供するものです。複数のサブツリーに対して複数のProviderを使用することができ、入れ子にすることもできます。これは、通常のReact Contextと同じように動作します。

さらっと重要なことが書いてあります。
複数のProviderを使ってこんな事ができます。atomはconfig、ということですね。

If an atom is used in a tree which no Providers exist, it will use the default state. This is so-called provider-less mode.

Providerが存在しないツリーでアトムが使用された場合、デフォルトのステートが使用されます。これはいわゆるProviderレスモードです。

ここにもサラッと書いてあります。なんと、Provider-lessで使えます。

Providers are useful for some reasons.

  1. It can provide a different state for each sub tree.
  2. Provider can hold some debug information.
  3. Provider can accept initial values of atoms.

Providerはいくつかの理由で便利です.

  1. サブツリーごとに異なる状態を提供することができる
  2. Providerはデバッグ情報を保持することができる
  3. Providerは、atomの初期値を受け入れることができる

デバッガーと連携して使うときはProviderありで書かなきゃいけません。そしてなんと、ReduxDevToolsが使えます。

const SubTree = () => (
  <Provider>
    <Child />
  </Provider>
)

Jotaiの紹介特集について

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

Jotai Friendsとは

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

https://jotaifriends.dev/

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

Jotai Friends

Discussion