Kitsune Blog

useTransitionをGlobalに使用したい。

useTransitionは再レンダリングを滑らかにするために非常に有効なhookです。

再レンダリングの際に重い処理があったとしても、裏側で計算処理を行い計算結果をDomに反映するため、画面がかくついたりすることを抑えられます。

CRUD処理でデータ更新→更新後のデータをリフェッチ→画面に反映

というようなケースで有用です。

Next.jsのApp RouterでServer Actionsを使用するときにも有用です。

Server ActionsのDocumentにも記載されています。

To improve the user experience, we recommend using other React APIs like useOptimistic and useTransition to update the UI before the Server Action finishes executing on the server, or to show a pending state.

このuseTransitionを用いて、実行処理中のローディングのUIを出したいときは、useTransitionをGlobalに使用する必要があります。useTransitionの返り値のisPendingがtrueの時は実行処理中のローディングのUIを表示する。

この処理は、useTransitionとcontextを組み合わせることで実現が可能です。

サンプルコードは下記です。

TransitionCotnext.tsx

'use client';

import { createContext, TransitionStartFunction, useContext, useTransition } from 'react';

type ContextValue = {
  isPending: boolean;
  startTransition: TransitionStartFunction;
};

const TransitionContext = createContext<ContextValue>({} as ContextValue);

export const useTransitionContext = () => useContext(TransitionContext);

export const TransitionContextProvider = ({ children }: { children: React.ReactNode }) => {
  const [isPending, startTransition] = useTransition();
  return (
    <TransitionContext.Provider
      value={{
        isPending,
        startTransition,
      }}>
      {children}
    </TransitionContext.Provider>
  );
};

呼び出し元

const { isPending, startTransition } = useTransitionContext();

const onClick = (userId: string) => {
  if (isPending) return;
  startTransition(async () => {
    const result = await updateSomeAction(userId);
    if (result.success) alert("更新しました。");
    if (!result.success) alert("更新に失敗しました。");
  });
};

このようになります。

上記例だとupdateSomeActionはupdate処理を行うServer Actionsで、返り値で処理が成功したか否かを判定できるような作りになっています。

Zustandなど他の状態管理ライブラリと組み合わせて、useTransitionをGlobalに使用する方法がないかも調べてみたのですが、あまり良い方法は見当たらなかったので、contextを使用する方法がベターなのではと思っています。