monologue

クズエンジニアの独白

React 各hooksの覚え書き

Reactでは今やスタンダートになったhooksについて、公式APIとして提供されているものは色々あるが、今まではせいぜい useState useEffect 程度しか使っていなかった。

最近徐々に他のhooksを使うシーンがあり、かなり便利だし、パフォーマンス観点でも使いこなせないとまずい気がして調べたのでまとめ。

公式のhooksは下記にまとまっている。 ja.reactjs.org

TL;DR

  • useState はいわゆる state の保持・変更、複雑state管理なら、useReducerを検討してもいいかも
  • useEffectレンダリング後に処理(副作用)したいときに利用、処理してからレンダリングしたいなら useLayoutEffect ※ただし使いすぎ注意
  • useContextコンポーネント階層をまたぐバケツリレーの代替案、ただしコンポーネントが密結合になりかねないので設計に注意
  • useCallback useMemo を利用することで不要な処理・レンダリングを抑制して高パフォーマンスを目指そう
  • useRef はDOMアクセスの手段にとどまらず、インスタンス変数のイメージで書き換え可能な値の保持しておく使い方もできる

useState

多分最も基本的なhooksで特に使い方も難しくないので軽くメモ。 useStateはstateを保持するために使う。

const [count, setCount] = useState(0);

useStateの引数には初期値をセットして、戻り値として、stateとstateを変更する関数を得る。 state更新関数の引数に更新したい値をセットして利用。

ちなみに下記のように前のstateを元に更新もできる。

setCount(prevCount => prevCount - 1);

useStateは、Object.isによる比較ロジックが組み込まれていて、前回の値と同じ際はレンダリング等の実行を回避するようになっている

useEffect

quki.hatenablog.com

useContext

主にバケツリレーの防止に約役立つ(バケツリレーは上位コンポーネントから下位コンポーネントへ値を渡していくこと、場合によっては何階層にも渡り辛みがある) ただ、useContext の利用は素直にpropsを受け取るような形で値を得ないので、上手くコンポーネント設計をしないと密結合になりやすいので乱用は避けた方が良さそう。

何となくだけど、アプリケーション全体に関連するstateを最上部で定義してどこからでもアクセスするように設計するとか、再利用の必要がなく、複雑、または多段でstateを管理するコンポーネント群に限定的に使う場合とかに使うとかの方が良い気がする。

また、useContext を利用してグローバルstateライクに設計するパターンも見たことがある。

例として公式のコードを拝借

// 変数定義
const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

// Contextを定義して、themes.lightを初期値に設定
const ThemeContext = React.createContext(themes.light);

function App() {
  return (
        {/* ThemeContext.Provider で子のコンポーネントでコンテクストを有効化, themes.dark をセット */}
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
    // ThemeContext を読み込み
  const theme = useContext(ThemeContext);
  return (
        {/* theme.backgound 等で値を読み込み */}
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

useCallback useMemo

詳細は別記事にまとめる予定なので軽く。

基本的にuseCallback useMemoはパフォーマンス改善のための hooks 。利用することで余計なレンダリングや再計算を抑制する。

Reactは何も気を付けずに構築すると、かなり再レンダリングや再計算が発生する。試しに自分のコンポーネントにlogを吐くようにしてみると、何回も同じlogが吐かれ、あれ?となるときがある。

自分の知っている限り理由はいくつかあるが、コンポーネントツリーの構築の仕方、stateの管理、propsでの値の受け渡し方等がある。

もちろんuseCallback useMemoで全てが解決できるわけではない。

useCallbackは、関数をメモ化して、依存配列が変更されたとき以外は再生成しない。 が、結構使い方に注意があり、どうやらReact.memoと組み合わせて利用しないと効果を発揮しないらしい。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMemoは、値をメモ化する。 何か値を計算して変数に入れることはよくあるが、Reactではコンポーネントの再レンダリングによって、変数の値も無駄に再計算されることがあるので、これを防止するために利用する。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useRef

ぶっちゃけあまり分かってない(使ったこと無い)

パッと見はDOMにアクセスするためのものだが、意外といろいろ使い方がありそう。 ここはもう少し調べて使ってみて理解が必要なので割愛。