• トップ
  • ブログ一覧
  • React18で追加されたhooksとその使い方
  • React18で追加されたhooksとその使い方

    岡坂(エンジニア)岡坂(エンジニア)
    2022.07.04

    IT技術

    useId

    今回は、2022年3月安定版リリースのReact 18で追加された新Hooksの紹介をしていきます!

    useIdはhtmlのID生成をサポートするhookです。ハイドレーション時の不整合を防ぎ、サーバーとクライアント間で一意のIDを生成できます。

    使い方

    useIdを呼び出すコンポーネントごとに一意のIDが提供されます。
    必要なIDが1つであればそのまま使用してOKですし、複数のIDが必要な場合は任意の接尾辞をつけます。

    【注意】
    1. keyに使うものではありません。公式ドキュメントでも注意されてますが、map使用時などのkey等に使うものではありません🙅‍♀️
    2. 自動生成されるIDに任意のプレフィックスを付与可能です。hydrateRootReactDOMServerの設定を追加することで、ID自体に任意のプレフィックスをつけることが可能です。

    1import { useId } from 'react'
    2
    3const SingleSample = () => {
    4  const id = useId()
    5  return (
    6    <>
    7      <input id={id} type="checkbox" name="confirm" />
    8      <label htmlFor={id}>Are you sure?</label>
    9    </>
    10  )
    11}
    12
    13const MultipleSample = () => {
    14  const id = useId()
    15  return (
    16    <>
    17      <div>
    18        <input id={id + 'strawberry'} type="radio" name="fruit" />
    19        <label htmlFor={id + 'strawberry'}>strawberry</label>
    20      </div>
    21      <div>
    22        <input id={id + 'banana'} type="radio" name="fruit" />
    23        <label htmlFor={id + 'banana'}>banana</label>
    24      </div>
    25      <div>
    26        <input id={id + 'kiwi'} type="radio" name="fruit" />
    27        <label htmlFor={id + 'kiwi'}>kiwi</label>
    28      </div>
    29    </>
    30  )
    31}

    生成されたHTML

    SingleSampleとしたコンポーネントには:r5:、MultipleSampleとしたコンポーネントには:rb:という文字列が出力されていることが分かります💡

    useTransition

    更新処理に緊急度を付与できるhookです。
    緊急度の低い処理をマークすることで、緊急度の高い処理を優先できるようになります。

    使用例

    useTransitionは、対象処理の実行状態を表す状態値(boolean)と、トランザクションを開始するための関数(TransitionStartFunction)を返します。
    緊急度が低い処理をstartTransition(トランザクションを開始するための関数)内に置きます。

    1import { useEffect, useState, useTransition } from 'react'
    2
    3export const Sample = () => {
    4  const [value, setValue] = useState<string>('')
    5  const [list, setList] = useState<string[]>([])
    6  const [isPending, startTransition] = useTransition()
    7
    8  // リスト更新処理の緊急度を下げる
    9  const updateList = (values: string[]) => {
    10    startTransition(() => {
    11      setList(values)
    12    })
    13  }
    14
    15  // ...
    16
    17  return (
    18    <>
    19      <input
    20        value={value}
    21        onChange={(e) => setValue(e.target.value)}
    22      />
    23      {isPending && <p>Pending...</p>}
    24      <div>
    25        {list.map((item, index) => (
    26          <span key={`item-${index}`}>{item}</span>
    27        ))}
    28      </div>
    29    </>
    30  )
    31}

    useDeferredValue

    useTransitionと同じで、緊急度に関わるhookで、緊急度が低い値に使用します。
    緊急性の高い更新と、useDeferredValue値の更新が重なった場合、useDeferredValueの値の更新が遅延されます。
    デバウンス (debounce) やスロットル (throttle)との違いとしては、指定した固定時間の遅延をするのではなく、緊急性の高い更新が終わるタイミングまで遅延をするということです。

    使用例

    1import { useDeferredValue, useEffect, useState } from 'react'
    2
    3export const Sample = () => {
    4  const [value, setValue] = useState<string>('')
    5  const [list, setList] = useState<string[]>([])
    6  const deferredList = useDeferredValue(list)
    7
    8  // ...
    9  // list更新処理
    10  // ...
    11
    12  return (
    13    <>
    14      <input
    15        value={value}
    16        onChange={(e) => setValue(e.target.value)}
    17      />
    18      <p>{deferredList.map((item) => item)}</p>
    19    </>
    20  )
    21}

    useSyncExternalStore

    これはライブラリ製作者向けのhookです。
    React18で並行処理/並行レンダリング機能が追加された結果、従来は更新のレンダーは中断不可同期的にレンダーされたのに対し、開始一時停止再開or破棄が可能になりました。
    そういった並行レンダリング機能との互換性を保ちながら、外部データソースから読み出しやデータの購読を行うために推奨されているhookです。

    使用例

    useSyncExternalStoreは3つの引数を受け取ります。第1はsubscribe(ストア変更時に実行されるコールバック関数)、第2はgetSnapshot(ストアの現在値を返す関数)、第3はgetServerSnapshot(SSR時にスナップショットを返す関数※任意※)です。
    また、後方互換性保持と、getSnapshotの自動的なメモ化をサポートするパッケージとして、use-sync-external-storeが公開されています。

    1// source: https://github.com/reactwg/react-18/discussions/86
    2import { useSyncExternalStore } from 'react';
    3
    4// 後方互換性のあるhook
    5// import { useSyncExternalStore } from 'use-sync-external-store/shim';
    6
    7const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
    8
    9// 特定のフィールドをsubscribeすることもできる
    10const selectedField = useSyncExternalStore(store.subscribe, () => store.getSnapshot().selectedField);
    11
    12// getSnapshot の結果のメモ化
    13import {useSyncExternalStoreWithSelector} from 'use-sync-external-store/with-selector';
    14const selection = useSyncExternalStoreWithSelector(
    15  store.subscribe,
    16  store.getSnapshot,
    17  getServerSnapshot,
    18  selector,
    19  isEqual
    20);

    useInsertionEffect

    こちらもライブラリ製作者、特に css-in-js ライブラリの作者向けのhookです。
    DOM 更新の前に同期的に呼び出されるため、他のhook(useLayoutEffect含む)が動く前にDOMにスタイルを注入することができます。

    css-in-jsライブラリとして有名なemotionでは、React18の安定版がリリースされる前からuseInsertionEffectの適用を準備していたようです👀
    useLayoutEffectを使用していた箇所をuseInsertionEffectに置き換えたりしてますね。

    使用例

    使い方はuseEffect, useLayoutEffectと同様です。
    第二引数に依存する値を配列で指定することができ、クリーンアップする関数をリターンすることもできます。

    1import { useInsertionEffect } from 'react'
    2import { insertStyles, cleanup } from '../util'
    3
    4export const useStyle = () => {
    5  useInsertionEffect(() => {
    6    insertStyles()
    7    return () => {
    8      // クリーンアップ処理
    9    }
    10  }, [])
    11}

    最後に

    React18で追加されたhooksの紹介でした!
    なかなか使う機会のないhookもあったかとは思いますが、必要な時に思い出せるよう、頭の片隅にでも置いておきましょう💫

    岡坂(エンジニア)

    岡坂(エンジニア)

    おすすめ記事