● 1. State Hooks
・useState
・useReducer
● 2. Context Hooks
・useContext
● 3. Ref Hooks
・useRef
・useImperativeHandle
● 4. Effect Hooks
・useEffect
● 5. Performance Hooks
・useMemo
・useCallback
● 6. Other Hooks
・useDebugValue
・useId
・useSyncExternalStore
・useActionState
https://react.dev/reference/react/hooks#other-hooks
・「状態を持つ変数」と「更新する関数」を管理するReactフックです。
・「状態を持つ変数」の値が変わると useState を宣言したコンポーネントで再レンダリングが起こります。
・(jsxの中でその変数が使われていてもいなくても再レンダリングがおこります)
useStateの使い方
import { useState } from "react";
const [email, setEmail] = useState<string>("");
// const [変数, 変数を更新するための関数(setter アクセサ)] = useState(状態の初期値);
// (例)変数 email / 更新する関数 setEmail() を宣言する
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email..."
/>
const [member, setMember] = useState({ name: "", part: "" });
const [isLogined, setIsLogined] = useState<boolean>(true);
TypeScriptで
const [startDate, setStartDate] = useState(new Date());
の初期値を null にしたい場合は、以下のように記述します
const [startDate, setStartDate] = useState <Date|null>(null);
useStateで更新したstateは即座に更新されるわけではない https://tyotto-good.com/blog/usestate-pitfalls
前の値を保持する時にuseRefを使います。 https://qiita.com/cheez921/items/9a5659f4b94662b4fd1e
非同期 async で使いたい時は useAsyncEffect() 関数を用意します https://github.com/jeswr/useAsyncEffect/blob/main/lib/index.ts
React.useState()の初期値は一度しかセットされない https://zenn.dev/lilac/articles/9e025186343058
useEffectとは、関数コンポーネントで副作用を実行するためのhookです。
useEffectで関数コンポーネントにライフサイクルを持たせることができます
useEffectを使うと、useEffectに渡された関数はレンダーの結果が画面に反映された後(マウント時)に1回だけ動作します。( = componentDidMount() )
またクリーンアップ処理を返すとアンマウント時にも実行できます。( =componentWillUnmount() )
useEffect の宣言方法
// 第1引数に「実行させたい副作用関数」を記述
// 第2引数に「副作用関数の実行タイミングを制御する依存データ」を記述
useEffect(() => {
// 実行したい処理
return () => {
// クリーンアップの処理
}
}, [input])
引用 : https://bit.ly/3SVp3ne
https://bit.ly/3Mn4Kwq
第2引数が指定されている場合は、マウント時以降は第2引数に渡された値が更新された時 に実行されます
初回レンダリング完了時1回だけ実行する
useEffect(() => {
console.log('useEffectが実行されました');
},[]);
// 第2引数の配列を空にして記述すると初回レンダリング完了時(マウント時)のみ1回だけ実行されます。(実行を1回だけに限定できます)
第2引数を省略するとコンポーネントがレンダリングされるたび毎回実行されます。 (非常に危険です)
const [count, setCount] = useState(0);
useEffect(() => {
alert('変数 count が変更されましたよ');
}, [count]); // 第二引数の配列に任意の変数を指定
マウント解除時に実行するにはクリーンアップ関数を返せばokです
const FunctionalComponent = () => {
React.useEffect(() => {
// クリーンアップ関数
return () => {
console.log("Bye");
};
}, []);
return <h1>Bye, World</h1>;
};
https://zenn.dev/catnose99/scraps/30c623ba72d6b5
以下のようなタイミングの違いがあります
NoUseEffectComponent.tsx
import React, { useState } from "react"
const NoUseEffectComponent: React.FC = () => {
console.log("=====(NoUseEffectComponent.tsx) function top =====")
const [count, setCount] = useState(0)
console.log("=====(NoUseEffectComponent.tsx) called =====")
return (
<>
<button onClick={() => setCount(count + 1)}>
({count})NoUseEffectComponent
</button>
{console.log("=====(NoUseEffectComponent.tsx) render last =====")}
</>
)
}
export default NoUseEffectComponent
UseEffectComponentNoSecondArgument.tsx
import React, { useEffect, useState } from "react"
const UseEffectComponentNoSecondArgument: React.FC = () => {
console.log("=====(UseEffectComponentNoSecondArgument.tsx) function top =====")
const [count, setCount] = useState(0)
useEffect(() => {
console.log("=====(UseEffectComponentNoSecondArgument.tsx) called =====")
})
return (
<>
<button onClick={() => setCount(count + 1)}>
({count})UseEffectComponentNoSecondArgument
</button>
{console.log("=====(UseEffectComponentNoSecondArgument.tsx) render last =====")}
</>
)
}
export default UseEffectComponentNoSecondArgument
それぞれ実行すると次のようなタイミングとなります
useEffectを使用していないので render より前に実行される
=====(NoUseEffectComponent.tsx) function top =====
=====(NoUseEffectComponent.tsx) called =====
=====(NoUseEffectComponent.tsx) render last =====
useEffectを使用しているので render の後に実行される
=====(UseEffectComponentNoSecondArgument.tsx) function top =====
=====(UseEffectComponentNoSecondArgument.tsx) render last =====
=====(UseEffectComponentNoSecondArgument.tsx) called =====
useLayoutEffect
すべてのDOM変異後、ペイントフェーズの前に同期的に発火します。これを使用して、DOMからレイアウト(スタイルまたはレイアウト情報)を読み取り、レイアウトに基づいてカスタムDOM変異のブロックを実行します。
useEffect
レンダリングが画面にコミットされた後、つまりレイアウトとペイントフェーズの後に実行されます。視覚的な更新のブロックを避けるために、可能な限りこれを使用してください。
Contextは、propsのバケツリレーを回避するために使用します。
グローバルなステートを管理するのに使用する Redux, Recoil とどちらを使うかについては設計段階で検討しましょう。
ReduxはMiddlewareを間に挟むことができるので、Middlewareを使いたい場合はReduxを使用します
Reduxライクで人気な zustand もおすすめです
以下の4つの要素から作成されます
・React.createContext関数でステートを作成する
・<Context.Provider> を用いて値を渡す
・<Context.Consumer> を用いて値を受け取る(re-renderの範囲を限定しやすい)
・React.useContext を用いると、Contect.Consumer と同じ役割をさせることができます(コンポーネント全体で再レンダリングが起きるのでre-render範囲を限定したい場合は、さらに子コンポーネントとして切り出す事。)
React hooksの概要 useContext による Provider|プログラムメモ
useReducer で setState 関連のロジックを閉じ込める
deleteItem メソッドは、配列のうち該当する index の item を削除するメソッドであるが、こういったロジックをどこに書くかをかなり悩んできた。結論としては useReducer 内にロジックを保持するパターンが、一番疎結合である。
useReducerというAPIも登場しています。 useReducerはReduxにおけるReducerのような振る舞いをします。
useReducer が生きるのは、複数の関連したステート、つまりオブジェクトをステートとして扱うときです。
useReducerの使い方
import { useReducer } from "react";
const [state, dispatch] = useReducer(reducer, initialState);
[ステート, 発火関数 ] = useReducer(関数[, 初期値])
なお reducer は自作する必要があります。
const reducer = (state, action) => {
if(action === 'INCREMENT'){
return {count: state.count + 1}
}else{
return {count: state.count - 1}
}
}
Reduxと全く同じ使い方ですので、Reduxをさらっておくことをおすすめします。
useReducer は useState と似ている。
useState では「数値」、「文字列」、「論理値」を扱うことができるがそれを拡張して
useReducerでは「配列」、「オブジェクト」を扱えるようにする
memo , useMemo , useCallback は コンポーネントのレンダリング最適化を考えるときに登場します
重たいコンポーネントの計測方法はこちらを参考にすると良いでしょう ↓ 。
【React】重い処理のあるコンポーネントはパフォーマンスチューニングまで忘れずに
以下のようにメモ化する対象が変わります。
memo → コンポーネント全体をメモ化する。
useCallback → 関数をメモ化する。(子コンポーネントに渡す関数など。)
useMemo → 変数やchildrenをメモ化する。
「メモ化」とは
コンポーネントの出力を「記憶」し、同じ入力が与えられた場合に再計算を省略するものです。キャッシュですね。
Reactで再レンダリングが起きる条件
・stateが更新されたコンポーネントは再レンダリング
・propsが更新されたコンポーネントは再レンダリング
・再レンダリングされたコンポーネント配下の子コンポーネントは再レンダリングされる
コンポーネントをメモ化する
コンポジション(props.children)を使って子コンポーネントを渡す
memo() とは
使い方
MyChild.jsx
const MyChild = () => {
console.log('🤙 MyChildのレンダリングが開始されました');
return (
<div>MyChild</div>
);
}
export default MyChild;
↓ このように変更することでメモ化されます
MyChild.jsx
import { memo } from "react";
const MyChild = memo( () => {
console.log('🤙 MyChildのレンダリングが開始されました');
return (
<div>MyChild</div>
);
})
export default MyChild;
全体を memo() で囲っておきます。このようにすることで親コンポーネントに変更があった場合に MyChild は再レンダリングはされなくなります。
useMemo() とは
useMemoとは変数に対して memo化を行うものです。
useMemoは、以前行われた計算の結果をキャッシュし、useMemoが属するコンポーネントが再レンダリングしても、useMemoの第2引数(deps)が更新されていない場合は計算をスキップして以前の結果を使うという機能を提供します。
useCallback() とは
再レンダリングを抑えるための手法
useCallbackはパフォーマンス向上のためのフックで、メモ化したコールバック関数を返します。
useEffectと同じように、依存配列(=[deps] コールバック関数が依存している要素が格納された配列)の要素のいずれかが変化した場合のみ、メモ化した値を再計算します。
引用 https://blog.uhy.ooo/entry/2021-02-23/usecallback-custom-hooks/
const App: React.VFC = () => {
const handleClick = useCallback((e: React.MouseEvent) => {
console.log("clicked!");
}, []);
return (
// memo化されたコンポーネント
<SuperHeavyButton onClick={handleClick} />
);
こちらのように、SuperHeavyButtonをmemo化 + props を useCallback する事で、App コンポーネントが再レンダリングされた際にもSuperHeavyButtonはすでに生成されたものが再利用されます
例えば ReactQuery の カスタムフックで以下のように使用してる場合に
const { data } = useMySampleQuery() // 型は MyData | undefined
const dataWithGetter = withGetter( data ) // undefined を受け付けない場合
↓ useMemo を使って次のようにすることができます
const { data } = useMySampleQuery() // 型は MyData | undefined
const dataWithGetter = withGetter( data ) // undefined を受け付けない場合
const dataWithGetter = useMemo(() => {
return data ? withGetter( data ) : undefined;
}, [data]);
children にも useMemoは有効です
【React】メモ化したコンポーネントに children を渡すと効果がなくなる
2つの使い方があります。
関数コンポーネントでは、Classコンポーネント時のref属性の代わりに、useRefを使って要素への参照を行います。
なお、propsで受け取ったrefをさらに子コンポーネントに渡す場合は forwardRef でなくてもokです。
const Child = React.forwardRef((props, ref) => {
return (
<div ref={ref}>DOM</div>
);
});
const Component = () => {
const el = useRef(null);
useEffect(() => {
console.log(el.current);
}, []);
return (
<Child ref={el} />
);
};
useStateを利用している場合はstateの変更される度にコンポーネントの再レンダリングが発生しますが、
useRefは値が変更になっても、コンポーネントの再レンダリングは発生しません。
コンポーネントの再レンダリングはしたくないけど、内部に保持している値だけを更新したい場合は、
保持したい値をuseStateではなく、useRefを利用するのが良いです。
useState と比較したとき useRef の重要な特徴は3つです。
更新による再レンダリングが発生しない
値が同期的に更新される
返却されるRefオブジェクトは同一である
こちらでは preact の useSignal との比較が記述されています
https://www.builder.io/blog/usesignal-is-the-future-of-web-frameworks
refを「ref」propとして渡さないでください。これはReactで予約されている属性名であり、エラーが発生します。
代わりに、forwardRefを使用します
https://github.com/preactjs/signals
https://zenn.dev/kobayang/articles/9145de86b20ba6
React.memo と useCallbackで state の変化に伴う{個別,共通}コンポーネントの再描画を抑制する - DEV Community