今日もReactを勉強する上で新しく学んだ概念をいい感じにまとめていきたいと思います!
Contextを使用したグローバルなstate管理よりも、簡単にグローバルなstate管理を行えるライブラリとしてRecoilというものがある。
recoilは2020年にfacebookが発表したグローバルなstate管理を簡単に行えるようにするライブラリ。
まずはインストール
npm install recoil
使い方としては以下のような感じ。
コンポーネントを作るわけではないので、ファイルの拡張子は.jsでおっけい。
import { atom } from "recoil"; //atomをインポート
export const testState = atom({
key: "modalState", //どのファイルでもこのstateを参照できるキーとなる、一意の値を設定。(ファイル名と揃えるのが自然)
default: { false } //デフォルトの値を渡しておく
});
以上のコードのみで、グローバルなstateを定義することが可能になる。
ここで定義したstateは、ルートのコンポーネントをRecoilRoot要素で囲ってやることで、グローバルにどこからでも呼び出せるようになる。
import { RecoilRoot } from "recoil";
export default function App() {
return(
<RecoilRoot>
<div>いろんなコンポーネントが入るで~</div>
</RecoilRoot>
)
}
recoilで定義したstateを参照するにはまずuseRecoilStateをインポートする必要がある。
import { useRecoilState } from 'recoil'
useRecoilStateの引数にグローバルstateのキーを指定して、あとはuseStateと同じように分割代入をすることで、stateを参照できる。
const [ open, setOpen ] = useRecoilState(testState);
recoilで定義したstateの値のみを参照するにはまずuseRecoilValueをインポートする必要がある。
import { useRecoilValue } from 'recoil'
useRecoilValueの引数にグローバルstateのキーを指定して、useStateと同じように分割代入することで、stateの値のみを参照できる
const openState = useRecoilValue(testState)
recoilで定義したstateの更新関数のみを参照するにはまずuseSetRecoilStateをインポートする必要がある。
import { useSetRecoilState } from 'recoil'
useSetRecoilStateの引数にグローバルstateのキーを指定して、useStateと同じように分割代入することで、stateの更新関数のみを参照できる。
・再レンダリングをいい感じにしてくれる。
useRecoilStateは値と更新関数をセットで提供してくれるので、値が更新されるとコンポーネントを再レンダリングする。(これは普通)
useSetRecoilStateは更新関数のみを提供してくれているが、stateの値が更新されても再レンダリングが起きないようになっている。(これがいい感じ)
コンポーネントの再レンダリングについての話が出てきたので、次はそれについてまとめていきます。
コンポーネントの再レンダリングというのはwebページを正しく表示する上で必要不可欠なものだが、必要のないコンポーネントまで再レンダリングされてしまうと、アプリケーションのパフォーマンス低下につながってしまう。
・stateが更新されたコンポーネントは再レンダリング
・propsが更新されたコンポーネントは再レンダリング
・再レンダリングされたコンポーネント配下の子要素は再レンダリング
Reactではこれらのレンダリングをいい感じに制限してパフォーマンスを上げてくれる便利ツールがたくさん用意されている。
ここではそれをいい感じにまとめていく。
親のコンポーネントが再レンダリングされても、propsに変更がない限りは子のコンポーネントを再レンダリングしないようにする方法として、Reactに標準で用意されている、memoという機能を使用する方法がある。
まずはインポート
import { memo } from 'react';
具体的にはこんな感じで使用する。
export const TestComponent = memo((props) => {
const { name } = props;
return(
<div>{name}</div>
)
});
memoでコンポーネント全体を囲ってやることで、「このコンポーネントはpropsが変更されない限り再レンダリングされませんよ」という意味になる。
親のコンポーネントにstateやpropsの変更があって再レンダリングが走っても、自らのpropsに変更がない限りは再レンダリングが走らないようになるので、アプリケーションのパフォーマンスをいい感じにあげることができる。
Reactでは、memo化したコンポーネントのpropsが変化していないのに再レンダリングが走ってしまうという挙動がある。
この原因は、親コンポーネントからpropsで渡される関数にある。
Reactでは、関数はレンダリングが走る度に毎回新しいものが生成されているという判断がされる。
なので、その関数を受け取る子コンポーネントのpropsの値も変更されているという判断がされ、コンポーネントをmemo化していたとしても再レンダリングが走ってしまう。
例えばこんな時は、ChildComponentをmemo化していたとしても、親コンポーネントがレンダリングされる度にChildコンポーネントにもレンダリングが走ってしまう。
export const ParentComponent = () => {
const onClickFunction = () => {
console.log('onClickFunction');
}
return(
<div>
<ChildComponent onClickFunction={onClickFunction}/>
</div>
)
};
これを解決するためのツールがuseCallbackだ。
まずはインポートする
import { useCallback } from 'react';
具体的にはこんな感じで使用する
export const ParentComponent = () => {
const onClickFunction = useCallback(() => {
console.log('onClickFunction');
}, [])
return(
<div>
<ChildComponent onClickFunction={onClickFunction}/>
</div>
)
};
useCallbackで関数全体を囲ってやることで「この関数は同じものを毎回使い回しますよ」という意味になる。
なのでmemo化された子コンポーネントにpropsでこの関数を渡しても新しい関数が生成されているとは判断されず、再レンダリングが走ることはない。
useCallbackの第二引数に指定されている値は、その関数の「関心」を表す。useEffectのアレと似た感じ。
今回は空配列なので最初に読み込まれた時にのみ生成されるみたいな感じ。stateの値とかを入れるとその値が更新された時には関数も再生成されるということになる。
useMemoでは変数のmemo化を行うことができる。
使い所としては、変数内で複雑な計算が行われる場合などだ。
コンポーネントがレンダリングされる度に計算を行うとアプリケーションのパフォーマンスが下がるので、そういった変数はあらかじめuseMemoを使用してmemo化しておく、useCallbackと同じように、第二引数に空配列を指定してやると最初に読み込まれた時にしか計算を行わないのでいい感じ。
import { useMemo } from 'react';
const test = useMemo(() => /*複雑な計算*/, []);
こんな感じに使用する。
今日はこんな感じで終了しま~す!
いい感じに理解できた気がします!
次はReact+TypeScriptとかについて書いていきたい!
最後までご覧いただきありがとうございました〜