【React】状態管理でuseContextとRecoilを試してみた。
概要
Reactにおける状態管理といえば、通常親コンポーネントから子コンポーネントにデータを渡す際はpropsを介して行います。ただ階層が深くなるごとに、バケツリレーのように受け渡していく方法では、管理が大変になってしまい、それを解決するために、ReactにはuseContext
というReactHooksがあります。
今回は、RecoilというMeta社(旧Facebook)が開発しているReactの状態管理ライブラリらしく昨年から??あったのかシンプルで使いやすそうなのでちらっと見てみた。
useContextとRecoilとの違いは?
そうなってくると気になるのがuseContextとRecoilとの違いです。
useContext
こちらはReactから提供されているReact純正のReactHooks。
基本的な使い方はuseContextもRecoilも似てるなーと思っていましたが、大きな違いはuseContextのでstate管理した場合は再レンダリングされる。
Recoil
こちらはReact同様にMetaが提供しているので、ほかのライブラリよりは安心??要素がおおいと思われる。
また学習コストもuseContext同様に基本的な使い方は時間もかからないと思われる。
useContextとの違いは不要な再レンダリングを発生させずに値を更新するらしい。
とは言っても設計次第でしょうか。
実際に試してみる。
今回はとりあえずuseContextとRecoilを比べてみたいので、カウンターを作成してみる。
使い方の詳細はまた今度。
下記のように子コンポーネントと孫コンポーネントなど階層が分かりやすくするためにCSSでボーダーで囲ってます。
- 親コンポーネント(Parent)
- 子コンポーネント(Child)
- 孫子コンポーネント(GrandChild)
親コンポーネントにあるボタンをクリックしたら孫コンポーネントで、カウンターの数字を表示してみる。子コンポーネント(Child)は孫コンポーネントまで階層を深くするためだけなので、意味はないです。
useContextの場合
とりあえずすべてのコード
import "./App.css";
function App() {
return (
<>
<Parent />
</>
);
}
export default App;
import Child from './components/Child';
import { createContext, useState } from 'react';
//createContext()でコンテキストオブジェクトを生成します。
export const sampleContext = createContext();
function Parent() {
const [countNum, setCounter] = useState(0)
const counterFunc=()=>{
setCounter((prevCount) => prevCount + 1)
}
return (
<div>
<h1>親の階層:App</h1>
<p>一番親の階層からボタンをクリックすると孫のコンポーネントの数値が変わります。</p>
<button onClick={counterFunc}>カウンター</button>
//Childコンポーネントをコンテキストオブジェクトで囲んで
//Provider経由でvalueに管理したい値を設定
<sampleContext.Provider value={countNum}>
<Child />
</sampleContext.Provider>
</div>
);
}
export default Parent;
import GrandChild from './GrandChild'
const Child = () => {
return (
<div>
<h1>子コンポーネント:Child</h1>
//GrandChildコンポーネントを呼び出す
<GrandChild />
</div>
)
}
export default Child
import { useContext } from "react";
import { sampleContext } from "../App";
const GrandChild = () => {
//Appで作成したコンテキストオブジェクトをimportしてuseContextに渡す
const context = useContext(sampleContext);
return (
<div>
<h1>孫コンポーネント:GrandChild</h1>
<p>親の階層にあるボタンをクリックしたらカウンターが更新される</p>
//context受け取った値を表示
<p>カウンター:{context}</p>
</div>
);
};
export default GrandChild;
Recoilの場合
useContext
の場合、コンテキストオブジェクトで囲みましたが、Recoilの場合も同様にRecoilRoot
で囲みます。
import Parent from "./components/Parent";
import { RecoilRoot } from "recoil";
function App() {
return (
<>
<RecoilRoot>
<Parent />
</RecoilRoot>
</>
);
}
export default App;
Recoilの場合、管理したいデータはAtomというオブジェクト??を使います。
Reduxのようにアプリケーション全体で状態管理を行うストアが一つなのに対し、
RecoilはAtom単位で一つ一つの状態管理を行うみたい。
Atomで指定するkey
は他と被らないようにユニークにする必要があり、default
はそのまま初期値を設定。
また、Ricoilの場合useState
の代わりにuseRecoilState
を使います。
注意点としてRecoilRoot
の中で使用しないとエラーがでます。
その為、ParentコンポーネントをRecoilRoot
で囲みますが、Parentコンポーネントより下層のコンポーネントでないとエラーがでます。
import React from 'react'
import Child from './Child';
import { useRecoilState,atom} from 'recoil';
//RicoilのAtomで管理したいデータを作成
//他でも使うのでexportしておく
export const countAtom = atom({
key: "countAtom",
default: 0,
});
const Parent = () => {
const [count, setCounter] = useRecoilState(countAtom)
const counterFunc = () => {
setCounter((prevcon) => prevcon + 1);
};
return (
<>
<h1>Parent</h1>
<button onClick = {counterFunc} > カウンター < /button>
<Child / >
</>
)
}
export default Parent
ちなみに状態管理したいファイルを外部にしてimportするのもよい。
import {atom} from 'recoil';
//RicoilのAtomで管理したいデータを作成
//他でも使うのでexportしておく
export const countAtom = atom({
key: "countAtom",
default: 0,
});
ChildコンポーネントはuseContext
の時と同じ、GrandChildコンポーネントを呼び出すだけなので省きます。
GrandChildでは状態管理しているカウントの数字を表示させるのでcountAtom
をimportします。
またRicoilの場合useState
ではなくuseRicoilState
を使うといったが、数字だけを表示させたい場合はuseRecoilValue
というメソッドがある。
import { useRecoilValue } from "recoil";
import { countAtom } from "./Parent";
const GrandChild = () => {
const countnum = useRecoilValue(countAtom);
return (
<div style={{ border: "1px solid #ff0000", padding: "10px" }}>
<h1>孫コンポーネント:GrandChild</h1>
<p>親の階層にあるボタンをクリックしたらカウンターが更新される</p>
<p style={{ fontSize: "20px", fontWeight: "bold" }}>
カウンター:{countnum}
</p>
</div>
);
};
export default GrandChild;
上記でuseContext
とRicoilの両方で状態管理を試してみた。
まとめ
という事でRecoilを使ってみたけど、簡単で使いやすいですね。
以下、参考にさせていただいたページです。
Discussion