Recoilとは?
RecoilはReactの状態管理ライブラリ。
Reduxと同じような役割。
Reduxとの違いは?
Reduxのメリット デメリット
メリット
- アプリケーション全体で状態を管理するため、わかりやすい。
- コンポーネント間のデータの受け渡しが不要になる。
デメリット
- Reducer, Action, Middlewareの定義など、コード量が多くなるため、小規模アプリケーションでは大掛かりすぎる。
- 小さな単位の状態管理もアプリケーション全体で管理する必要がある。
- 難しい。
Recoilのメリット デメリット
メリット
- アプリケーション全体ではなく、指定したコンポーネント間で状態管理ができる。
- useStateの進化系のような感覚で使用できる。
- コンポーネント間のデータの受け渡しが不要になる。
デメリット
- アプリケーションが大規模になったときに複雑になる。
以上を踏まえ、小規模アプリケーションの場合、Recoilの方が実装しやすいのでは!?と思い、以下で実践。
実装方法
今回は、inputから追加されたタスクをリスト表示し、個数を表示するアプリをRecoilを使用して作成。
インストール
npm install recoil
Recoil Rootで状態管理にアクセスしたいコンポーネントを囲む
import AddTask from "./components/AddTask";
import InputTask from "./components/InputTask";
import "./App.css";
import { RecoilRoot } from "recoil";
function App() {
return (
<RecoilRoot>
<div className="App">
<InputTask />
<AddTask />
</div>
</RecoilRoot>
);
}
export default App;
上記の場合、Recoilで状態管理したデータにアクセスできるのは、InputTaskコンポーネントとAddTaskコンポーネントのみとなる。
Atomの設定
今回は、①inputに入力された値の状態 「inputTitleStatet」と、②追加されたタスクの状態「addTitleState」をRecoilにて管理する。
①の設定
import { atom } from "recoil";
export const inputTitleState = atom<string>({
key: "inputTitleState",
default: "",
});
上記のようにRecoilでは、atomという関数を使って、状態管理したいオブジェクトを定義する。
keyは一意の名前。 defaultは初期の状態となる。
②の設定
import { atom, selector } from "recoil";
import { Task } from "../types/Task";
export const addTitleState = atom<Array<Task>>({
key: "addTitleState",
default: [],
});
export const addTitleStateLength = selector<number>({
key: "addTitleStateLength",
get: ({ get }) => {
const addTitleNumber: Array<Task> = get(addTitleState);
return addTitleNumber?.length || 0;
},
});
②は追加されたタスクを管理するため、Task型(別途定義)の配列をデフォルト値に指定。
addTitleStateLengthに使用されている、selector関数は、atomのデータを使用して処理を行う関数を定義する際に使用される。
getはatomのデータを取得している。
addTitleStateLengthは、タスクの数の状態を管理するための関数となる。
Atomの参照と更新
import "./InputTask.css";
import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
import { inputTitleState } from "../states/inputTitleState";
import { addTitleState } from "../states/addTitleState";
import { useCallback } from "react";
const getKey = () => {
return Math.random().toString(32).substring(2);
};
const InputTask = () => {
// const inputTitle = useRecoilValue(inputTitleState);
// const setInputTitle = useSetRecoilState(inputTitleState);
const [inputTitle, setInputTitle] = useRecoilState(inputTitleState);
const [addTitle, setAddTitle] = useRecoilState(addTitleState);
const onChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setInputTitle(e.target.value);
},
[inputTitle]
);
const handleClick = () => {
setAddTitle([...addTitle, { id: getKey(), title: inputTitle }]);
setInputTitle("");
};
return (
<div className="inputField">
<input type="text" className="inputTitle" onChange={onChange} value={inputTitle} />
<button type="button" className="addButton" onClick={handleClick}>
追加
</button>
</div>
);
};
export default InputTask;
以下コード(抜粋)により、useStateのように参照用変数と更新用関数を定義できる。
(コメントアウト部分は、個々に定義する場合の記述方法。)
// const inputTitle = useRecoilValue(inputTitleState);
// const setInputTitle = useSetRecoilState(inputTitleState);
const [inputTitle, setInputTitle] = useRecoilState(inputTitleState);
const [addTitle, setAddTitle] = useRecoilState(addTitleState);
定義したらuseStateと同じように使うだけ。(コード抜粋)
const onChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setInputTitle(e.target.value);
},
[inputTitle]
);
const handleClick = () => {
setAddTitle([...addTitle, { id: getKey(), title: inputTitle }]);
setInputTitle("");
};
表示部分も同じ。参照用変数をJSX内に組み込むだけ。
import "./AddTask.css";
import { useRecoilValue } from "recoil";
import { addTitleState, addTitleStateLength } from "../states/addTitleState";
const AddTask = () => {
const addTitle = useRecoilValue(addTitleState);
const addTitleLength = useRecoilValue(addTitleStateLength);
return (
<div className="taskField">
<div>タスクの数:{addTitleLength}個</div>
<ul>
{addTitle.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
</div>
);
};
export default AddTask;
最後に
以上、Recoilの基礎的な使い方でした。
Reduxと比べコード量も減り、参照・更新に関しては、useState感覚で行えるのでとても楽だと感じました。
だがしかし。。。
記事を書きながらRecoilについて調べていた途中に、メンテが停止したことを知りました。
せっかく便利さを知って、今後使ってみようと思ったのにとても残念です。。。。。