jotai vs mobx vs react-query vs recoil vs redux vs valtio vs xstate vs zustand
状態管理ライブラリ
jotaimobxreact-queryrecoilreduxvaltioxstatezustand類似パッケージ:

状態管理ライブラリ

状態管理ライブラリは、アプリケーションの状態を効率的に管理し、コンポーネント間でのデータの共有を容易にするためのツールです。これらのライブラリは、特にReactアプリケーションにおいて、状態の一貫性を保ちながら、ユーザーインターフェースの反応性を向上させることを目的としています。各ライブラリは異なるアプローチを持ち、特定のユースケースに最適化されています。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
jotai3,777,01921,297113 kB224日前MIT
mobx2,018,95028,2124.88 MB213日前MIT
react-query911,72650,4682.26 MB1654年前MIT
recoil019,4102.21 MB3204年前MIT
redux061,446290 kB153年前MIT
valtio010,243101 kB66ヶ月前MIT
xstate030,3482.31 MB1226日前MIT
zustand058,86296 kB42日前MIT

機能比較: jotai vs mobx vs react-query vs recoil vs redux vs valtio vs xstate vs zustand

状態管理のアプローチ

  • jotai:

    Jotaiはアトムベースの状態管理を採用しており、各アトムが独立しているため、必要な状態のみを選択的に管理できます。

  • mobx:

    MobXはリアクティブプログラミングを基盤としており、状態の変更を自動的に追跡し、UIを自動的に更新します。

  • react-query:

    React Queryはサーバー状態の管理に特化しており、データのフェッチやキャッシングを簡単に行えます。

  • recoil:

    Recoilはアトムとセレクタを使用して、状態の依存関係を明示的に管理します。これにより、状態の変更が他の状態に与える影響を簡単に把握できます。

  • redux:

    Reduxは単一のストアを使用し、アクションとリデューサーを通じて状態を管理します。これにより、状態の変更が予測可能になります。

パフォーマンス

  • jotai:

    Jotaiは必要な状態のみを管理するため、パフォーマンスが高く、不要な再レンダリングを避けることができます。

  • mobx:

    MobXは状態の変更を自動的に追跡するため、パフォーマンスが高く、UIの更新がスムーズです。

  • react-query:

    React Queryはデータのキャッシングを行うため、サーバーへのリクエストを最小限に抑え、パフォーマンスを向上させます。

  • recoil:

    Recoilは状態の依存関係を管理するため、必要な部分のみが再レンダリングされ、パフォーマンスが向上します。

  • redux:

    Reduxは状態の変更が明示的であるため、パフォーマンスの最適化が容易です。

学習曲線

  • jotai:

    JotaiはシンプルなAPIを持っており、学習曲線が緩やかで、すぐに使い始めることができます。

  • mobx:

    MobXはリアクティブプログラミングの概念を理解する必要があり、やや学習曲線が急ですが、強力な機能を提供します。

  • react-query:

    React Queryはデータフェッチの概念を理解する必要がありますが、APIが直感的で使いやすいため、比較的学習しやすいです。

  • recoil:

    RecoilはReactのコンテキストAPIに基づいているため、Reactに慣れている開発者には学習しやすいです。

  • redux:

    Reduxはアクションやリデューサーの概念を理解する必要があり、学習曲線が急ですが、強力な状態管理を提供します。

拡張性

  • jotai:

    Jotaiはシンプルな設計のため、必要に応じて簡単に拡張できます。

  • mobx:

    MobXはプラグインや拡張機能をサポートしており、柔軟な拡張が可能です。

  • react-query:

    React Queryはカスタムフックを作成することで、拡張性が高く、特定のニーズに合わせたデータ管理が可能です。

  • recoil:

    Recoilはアトムとセレクタを組み合わせることで、柔軟な拡張が可能です。

  • redux:

    Reduxはミドルウェアを使用することで、機能を拡張しやすく、カスタマイズが可能です。

デバッグツール

  • jotai:

    Jotaiはシンプルな状態管理のため、デバッグが容易で、状態の追跡が簡単です。

  • mobx:

    MobXは強力なデバッグツールを提供しており、状態の変更を視覚的に追跡できます。

  • react-query:

    React Queryはデバッグツールが充実しており、サーバー状態の管理が容易です。

  • recoil:

    RecoilはReact DevToolsと統合されており、状態のトラッキングが容易です。

  • redux:

    ReduxはRedux DevToolsを使用して、状態の変更を詳細に追跡でき、デバッグが容易です。

選び方: jotai vs mobx vs react-query vs recoil vs redux vs valtio vs xstate vs zustand

  • jotai:

    シンプルで軽量な状態管理を求める場合、Jotaiが適しています。アトムベースのアプローチにより、必要な状態のみを管理し、パフォーマンスを最適化します。

  • mobx:

    リアクティブプログラミングを重視し、状態の変更を自動的に追跡したい場合はMobXが良い選択です。特に大規模なアプリケーションでの状態管理に強みがあります。

  • react-query:

    サーバーからのデータ取得やキャッシングを効率的に行いたい場合、React Queryが最適です。データのフェッチやキャッシュ管理を簡素化し、サーバー状態を扱うのに特化しています。

  • recoil:

    ReactのコンテキストAPIを活用しつつ、より柔軟な状態管理を求める場合はRecoilが適しています。アトムとセレクタを使った状態管理が可能です。

  • redux:

    大規模なアプリケーションでの一貫した状態管理を求める場合、Reduxが適しています。ミドルウェアや開発者ツールが豊富で、状態の変更を予測可能にします。

  • valtio:

    シンプルで直感的なAPIを持つValtioは、プロキシベースの状態管理を提供します。状態の変更がリアルタイムで反映されるため、使いやすさが特徴です。

  • xstate:

    状態遷移を明示的に管理したい場合、XStateが適しています。状態マシンを使って、複雑な状態管理を視覚的に表現できます。

  • zustand:

    軽量で簡潔なAPIを求める場合、Zustandが最適です。フックを使った状態管理が可能で、簡単に導入できます。

jotai のREADME


Jotai (light mode)


visit jotai.org or npm i jotai

Build Status Build Size Version Downloads Discord Shield Open Collective

Jotai scales from a simple useState replacement to an enterprise TypeScript application.

  • Minimal core API (2kb)
  • Many utilities and extensions
  • No string keys (compared to Recoil)

Examples: Demo 1 | Demo 2

First, create a primitive atom

An atom represents a piece of state. All you need is to specify an initial value, which can be primitive values like strings and numbers, objects, and arrays. You can create as many primitive atoms as you want.

import { atom } from 'jotai'

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

Use the atom in your components

It can be used like React.useState:

import { useAtom } from 'jotai'

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

Create derived atoms with computed values

A new read-only atom can be created from existing atoms by passing a read function as the first argument. get allows you to fetch the contextual value of any atom.

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

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

Creating an atom from multiple atoms

You can combine multiple atoms to create a derived atom.

const count1 = atom(1)
const count2 = atom(2)
const count3 = atom(3)

const sum = atom((get) => get(count1) + get(count2) + get(count3))

Or if you like fp patterns ...

const atoms = [count1, count2, count3, ...otherAtoms]
const sum = atom((get) => atoms.map(get).reduce((acc, count) => acc + count))

Derived async atoms needs suspense

You can make the read function an async function too.

const urlAtom = atom('https://json.host.com')
const fetchUrlAtom = atom(async (get) => {
  const response = await fetch(get(urlAtom))
  return await response.json()
})

function Status() {
  // Re-renders the component after urlAtom is changed and the async function above concludes
  const [json] = useAtom(fetchUrlAtom)
  ...

You can create a writable derived atom

Specify a write function at the second argument. get will return the current value of an atom. set will update the value of an atom.

const decrementCountAtom = atom(
  (get) => get(countAtom),
  (get, set, _arg) => set(countAtom, get(countAtom) - 1)
)

function Counter() {
  const [count, decrement] = useAtom(decrementCountAtom)
  return (
    <h1>
      {count}
      <button onClick={decrement}>Decrease</button>
      ...

Write only derived atoms

Just do not define a read function.

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>
}

Async actions

Just make the write function an async function and call set when you're ready.

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>
    ...

Note about functional programming

Jotai's fluid interface is no accident — atoms are monads, just like promises! Monads are an established pattern for modular, pure, robust and understandable code which is optimized for change. Read more about Jotai and monads.

Links