iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

React 18から出たhooks達を改めて知る

こんにちは。フロント担当のタクシです。

あっという間に秋の装いですね〜みなさまどうお過ごしでしょうか?

私は週末を利用し暖かい実家に帰省しつつこのブログを書いております🏝️

今日はReact18から登場したhooksをご紹介したいと思います。

React 18から登場したhooks達

リリース自体は2022年3月ごろにされているReact 18なのですが、

正直業務内で全てのhooksを使う機会がなく、理解が甘い部分が多いことに気づきました。。

もちろん、必要以上の機能を無理に使う必要はないのですが、

改めて理解だけはしておきたい!と思い、今回はReact 18で新登場したhooks達について触れたいと思います。

useId

useIdは一意(ユニーク)なID文字列を返します。引数は取りません。

コンポーネントレベルで使うことで繰り返し使う場合でも各レンダリングごとにユニークなIDを生成します。

主にアクセシビリティ属性などに使うことができます。(aria-属性など)

aria-属性について

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby

function PasswordField() {
  const passwordHintId = useId(); //引数は取らない
  return (
    <>
      <label>
        パスワード:
        <input
          type="password"
          aria-describedby={passwordHintId}
        />
      </label>
      <p id={passwordHintId}>
        パスワードは18桁以上で設定してください
      </p>
    </>
  );
}

function Form() {
    return (
        <>
            <p>パスワード設定</p>
            <PasswordField />
            <p>パスワード設定(確認用)</p>
            <PasswordField />
        </>
    )
}

ランダムなID文字列といっても、桁数などの指定はできないので、

仮パスワード発行や、セキュリティ関連で使用することには向きません。

その場合はuuidなどのライブラリなどを使用した方が良さそうです!

useTransition

useTransitionは、UIをブロッキングすることなく、状態更新を可能とします。

引数は取りません。

useTransitionは以下二つを返します。

isPendingペンディング状態にある更新があるかのフラグ

startTransition:startTransition内で状態更新をかけると、UIをブロッキングすることなく、更新が走ります。

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
}

useDeferredValue

引数内で指定したstateの値が更新されるまで、更新前の古い値使用する場合に使います。

Suspenseを使う場合、初回読み込み時はfallbackが表示されますが、

以降は、useDeferredValueによってデータが読み込まれるまで、更新以前のデータが表示されるようになります。

更新されるまでレンダリングをキャンセルしてくれるのでパフォーマンス向上にも役立つようです。

import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';

export default function App() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <SearchResults query={deferredQuery} />
      </Suspense>
    </>
  );
}

input入力ごとに検索結果を表示したり、など連続的な処理をする場合、

遅延処理として、debounceやthrottle(こちらの説明は省きます。。🙇)を使ったりしていたのですが、、

useDeferredValueを活用するのが良さそうです!

useSyncExternalStore

ブラウザなどのReact外部の状態(例:ブラウザのネットワーク接続状況など)にサブスクライブすることができます。

navigator.onlineを使ったブラウザのオンライン状態を表示するコンポーネント

import { useSyncExternalStore } from 'react';

export default function ChatIndicator() {
  const isOnline = useSyncExternalStore(subscribe, getSnapshot);
  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}

function getSnapshot() {
  return navigator.onLine;
}

function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

公式ドキュメントにもありますが、基本的にはReactでデータや状態を保つのは、既存のuseStateなどを使うようにして、

useSyncExternalStoreは、Reactではない既存コードとの統合が必要な場合に主に使うようです。

When possible, we recommend using built-in React state with useState and 
useReducer instead. The useSyncExternalStore API is mostly useful if you need to integrate with existing non-React code.

useInsertionEffect

CSS in JSライブラリを開発する人向けの機能です。

useLayoutEffect, useEffectよりも先に実行され、実行タイミングは

useInsertionEffect → useLayoutEffect → useEffect

のような形です。

console.logで確認してみるとこのような感じになります↓

useEffect(() => {
  console.log('use effect');
}, []);

useLayoutEffect(() => {
  console.log('use layout effect');
}, []);

useInsertionEffect(() => {
  console.log('use insertion effect');
}, []);

状態(state)更新が走る→useInsertionEffectでスタイルを挿入→そのスタイルをもとにuseLayoutEffectなどで何かしらの処理を走らせる、、

という理解なのですが、

大体の開発では状態更新と共に変更を加える場合、

useStateとuseLayoutEffect・useEffectの副作用を使うようにするのでほとんど使う機会はなさそうですね〜

でもあまり知らなかったので学びになりました!

今回ドキュメントを改めて読んでみただけでも、

知らないことが多く、もっと理解を深められることがたくさんありそうです!

hooksをより効果的に使っていこうと心に決めました!!