こんにちは。最近、韓国ドラマとSPY×FAMILYにハマっているHLCのエンジニアの掛橋です。
前回の記事ではRecoilの簡単な説明や、Atomの使い方などざっくり説明させていただきました。
まだご覧になってない方はこちらをご覧ください。
今回の記事では無駄なレンダリングを防ぐRecoilの強みについて説明します。
無駄なレンダリングとは?
カウントアップするアプリを例にします。
前回の例に引き続き、親(Parent.tsx)、子(Child.tsx)、孫(GrandChild.tsx)というコンポーネントがあり、
親は子のコンポーネントを持ち、子は孫コンポーネントを持つものとします。
親にはボタンがあり、クリックすると孫が持っている数値がカウントアップされる
という動きです。(Parent,Child,GrandChildにはレンダリングされるとログを吐き出す様にしています。)
状態管理はContextで行っています。
親のもつボタンを押すと、親、子、孫全てのコンポーネントが再レンダリングされるのが分かります。
親のボタンを押したら、孫のみ再レンダリングされていれば十分ですが
親、子の2つのコンポーネントが余分に再レンダリングされています。こちらが無駄なレンダリングになります。
どうやって無駄なレンダリングを防ぐのか?
RecoilでAtomの状態を変化させたり取得するhooksは代表して以下3つ
useRecoilState
useSetRecoilState
useRecoilValue
こちらを適宜必要な箇所で使用することで実現できます。
useRecoilState
こちらはReactのuseStateと同じように
Atomの値とAtomの値を更新する関数がタプル型で返されます。
const [value, setValue] = useRecoilState(Atom)
useSetRecoilState
こちらはAtomの値を更新する関数のみを作成できます。
const setValue = useSetRecoilState(Atom)
setValue(100) //Atomに100という値を入れる
useRecoilValue
こちらはAtomの値そのものを取得します。
const value = useRecoilValue(Atom)
console.log(value) //Atomに100の値が入っていれば、ログに100と表示される
親はuseSetRecoilStateを扱い
孫はuseRecoilValueを使うことで無駄なレンダリングを防ぐことが可能です。
※親の階層でuseRecoilStateを使うと、state が更新されると孫まで再レンダリングがされてしまいます。
実装方法
1.状態を管理したい最上位層をRecoilRootタグで囲む
import React from "react"
import { RecoilRoot } from "recoil"
import { Parent } from "./components/Parent"
export const App = () => {
return (
<div>
<div className="App">
<RecoilRoot>
<Parent />
</RecoilRoot>
</div>
</div>
)
}
2.Atomの準備
import { atom } from "recoil"
export const countState = atom({
key: "countState",
default: 0,
})
3.親コンポーネントでuseSetRecoilStateを使用
import React from "react"
import { useSetRecoilState } from "recoil"
import { countState } from "../globalStates/counterAtom"
import { Child } from "./Child"
export const Parent = () => {
const setCount = useSetRecoilState(countState)
const handleClick = () => {
console.log("count up")
setCount((value) => value + 1)
}
console.log("Parent")
return (
<div style={{ backgroundColor: "orange", padding: 30 }}>
親やで
<Child />
<button onClick={handleClick}>+1</button>
</div>
)
}
4.孫コンポーネントでuseRecoilValueを使用
import React from "react"
import { useRecoilValue } from "recoil"
import { countState } from "../globalStates/counterAtom"
export const GrandChild = () => {
const count = useRecoilValue(countState)
console.log("GrandChild")
return (
<div style={{ backgroundColor: "white", padding: 30 }}>
<div>孫やで</div>
{count}
</div>
)
}
結果
上:Recoil 下:Context
Contextを使用していた時と比べると親、子のレンダリングがなくなっているのが分かりますね。
Recoil、ええやん!素敵やん!