こんにちは!kintone のフロントエンド刷新プロジェクト(フロリア)の@nkgrnkgrです。
フロリアでは、kintone のフロントエンドの ClosureToolsで書かれたコードを React に置き換えています。
本記事では フォーム画面の UI の状態管理に使うライブラリを選定する際に、どのような検証と意思決定を行ったかについて紹介します。この記事が UI の状態管理を行う際に何かの参考になれば幸いです。
はじめに
アプリ作成フォーム画面
アプリ作成フォーム画面とは?
アプリ作成フォーム画面(以降フォーム画面)とは、ユーザーが kintone のアプリを作成するための画面です。 フィールドと呼ばれる入力パーツをキャンバスと呼ばれる空間にドラッグアンドドロップで配置してアプリを作成・編集できます。
ユーザーの操作と状態管理で考慮すべきこと
- フィールドは左側のフィールドの一覧から右側のキャンバスに配置できる
- キャンバスには行の概念があり、キャンバス内に複数の行を配置できる
- 行に対して任意の数のフィールドを配置できる
- フィールドは別の行に移動できる
- フィールド自体の横幅や高さの変更ができる
このような UI の状態管理を行う場合、サーバーからのレスポンスや入力フォームのような一般的な状態管理以外に、以下のような特徴を考慮する必要が出てきます。
- 行とフィールドを二次元的に管理する必要がある
- 1つのアクションで複数の状態の更新が発生する(例えばフィールドの追加だと、フィールドと行の両方への変更が発生するなど)
- 管理する状態が多いが、特定の状態更新で不用意にコンポーネントの再レンダリングが発生しない仕組みが必要
ライブラリを選ぶ上での前提
今のチームはフォーム画面に着手する前に別の画面をRedux(ReduxToolkit)で React 化していました。しかしチームとして新しい技術に挑戦したいという意見もあり技術選定を行っています。
今回の選定で考慮したライブラリの種類と特徴
UI の状態管理を行うためのどのライブラリが適切かチームで議論しました。 Redux(ReduxToolkit) 以外で社内で利用実績のあるものや、「今後利用してみたい」という意見を踏まえて以下をリストアップしました。
- ContextAPI + useState
- Redux(ReduxToolkit)
- Recoil
- Zustand
- Jotai
そのうち Recoil と ContextAPI + useState は以下の理由で比較対象外となりました。
選定を行った 2023 年 3 月時点では、Recoil は 2022 年 10 月から半年近くライブラリのアップデートが途絶えており今後メンテナンスされるか不明でした。(※執筆日時点では Recoil の新しいバージョンが公開されています 😊) 状態管理が複雑になることから ContextAPI + useState では管理が難しいと判断しました。
上記を踏まえて 継続的にメンテナンスされている Redux(ReduxToolkit)、Zustand、Jotai を比較して決めることとしました。
3つのライブラリの特徴
Redux(ReduxToolkit)、Zustand、Jotai の 3 つのライブラリの特徴を紹介します。
Redux(ReduxToolkit) について
React を使うときに最もメジャーな UI 状態管理ライブラリです。 Flux ベースの実装で状態は大きな1つのオブジェクトとして作られます。
サンプルコード
import { createSlice } from "@reduxjs/toolkit"; const { actions, reducer } = createSlice({ name: "counter", initialState: { value: 0 }, reducers: { incrementByAmount: (state, action: PayloadAction<number>) => { state.value += action.payload; }, }, }); function App() { const value = useSelector((state) => state.counter.value); const dispatch = useDispatch(); const handleClickAddButton = () => { dispatch(actions.incrementByAmount(2)); }; return ( <div> <p>{value}</p> <button onClick={handleClickAddButton}>Add</button> </div> ); }
Zustand について
https://github.com/pmndrs/zustand
状態管理に Hooks を利用し簡潔に書けるライブラリです。
サンプルコード
import { create } from "zustand"; const useCounterStore = create<{ value: number; incrementByAmount: (amount: number) => void; }>((set) => ({ value: 0, incrementByAmount: (amount: number) => set((state) => ({ value: state.value + amount })), })); function App() { const { value, incrementByAmount } = useCounterStore(); const handleClickAddButton = () => { incrementByAmount(2); }; return ( <div> <p>{value}</p> <button onClick={handleClickAddButton}>Add</button> </div> ); }
Jotai について
https://github.com/pmndrs/jotai
Recoil にインスパイアされたモデルで、Atomic な状態をグローバルで利用するための機能を提供しています。
サンプルコード
import { atom } from "jotai"; const counter = atom(0); const useCounter = () => { const [value, setValue] = useAtom(counter); const incrementByAmount = (amount: number) => setValue(value + amount); return { value, incrementByAmount, }; }; function App() { const { value, incrementByAmount } = useCounter(); const handleClickAddButton = () => { incrementByAmount(2); }; return ( <div> <p>{value}</p> <button onClick={handleClickAddButton}>Add</button> </div> ); }
利用状況とプロダクト要件をもとにした比較
これら3つのライブラリについて利用状況やプロダクトの性質上欲しい要件を中心に調査を行いました。
なおライブラリの比較はそれぞれ次のバージョンで行っています。
ライブラリ | Redux(ReduxToolkit) | Zustand | Jotai |
---|---|---|---|
バージョン | v1.9.3 | v4.3.6 | v2.0.3 |
利用状況
Redux(ReduxToolkit) が最も利用されており、次いで Zustand、Jotai となっています。 Zustand の利用数が最近伸びてきていることが新たな発見でした。
選定で重視したポイント
重視したポイントをライブラリごとに表にしました。
重視したポイント | ReduxToolkit | Zustand | Jotai |
---|---|---|---|
Production での利用実績 | あり | 自チームではないが別チームで利用している | なし |
状態を集合として扱う機能 | あり(createEntityAdapter) | なし | あり(atomFamily) |
複数の状態を分割して管理する機構 | 1つの GlobalStore を作成し Slice で分割して管理する | store を分割して管理可能 | atom を分割して管理可能 |
分割された状態をまたがって参照する仕組 | あり | あり(一部制限あり) | あり |
「Production での利用実績」は今後長く開発・運用していく中で躓きポイントがわかっているかどうかなどを判断する上で安心材料になると思い評価対象としています。
「状態を集合として扱う機能」は、N 行と N 個のフィールドの状態を管理していく上で管理のしやすさという観点で重要だと判断しています。
「複数の状態を分割して管理する機構」は複雑な画面の中で状態を分割して管理することで状態同士を疎結合に保つために必須だと判断しています。
「分割された状態をまたがって参照する仕組」があることでサーバーへの保存時やエラーの判断で複数の状態をまたがったロジックへの対処ができるため重要だと判断しています。
実際に選定するためにやったこと
ドキュメントや記事を読んで比較しましたが、読んだり簡単なサンプルを実装するだけでは複雑な状態管理が必要な kintone で本当に利用できるのかわからず決め手にかけていました。
そこで、kintone のフォーム画面の状態管理に近いサンプル実装を行ってみて、書き味や好みも踏まえて決めることにしました。
kintone のフォーム画面に必要そうな状態管理を考慮して、次のような要件をもとにサンプル実装を行いました。
- フィールドの追加・更新・削除ができる
- 行の追加・削除
- 行の入れ替え
- フィールドの移動
Redux(ReduxToolkit), Zustand, Jotai の全てで次の画面のようなサンプル実装を行いました。
選定結果と理由
ライブラリを比較検討した結果、最終的に Redux(ReduxToolkit) を選択することになりました。
サンプル実装を行ってみて、どのライブラリにもそれぞれの良さがあることは確認できました。しかしながら、すでに使い慣れている Redux 以外を選択するほどの大きなメリットは見いだせませんでした。
Redux 以外の状態管理ライブラリを使うことのメリットとデメリットを天秤にかけて検討してみましたが、今回 React 化するフォーム画面の複雑さとそれに伴う難易度の高さを考慮し、保守的に判断することにしました。
最後に
今回紹介した事例が一つでもフロントエンドの UI 状態管理ライブラリ選定をするときの参考になれば幸いです。
フロントエンドの React 化に興味がある、一緒に取り組みたい方は是非、ご応募お待ちしております!