https://zenn.dev/honey32/articles/f6ec17edf13670

og-base_z4sxah.png

はじめに

やめ太郎さんによる記事

の末尾で紹介されている、「render hook と Promise を組み合わせた方法」について、簡潔に解説し、そのメリットも併せて述べます。

2番煎じですが、そのネタの提供者は僕なので #@!*... (循環参照エラー)

Promise を使うことのメリット

Promise を使うことによって、確認ダイアログの「確認ボタン」「キャンセルボタン」を押下したときの操作が、ダイアログ側ではなく、ページ側(ダイアログ呼び出し側) に 記述できるようになります。そのおかげで、深くなりがちな関数呼び出しの階層を浅くして、コードを追いやすくなります。

▼ こんな風に、簡潔でありながら、 階層が浅く、「モーダル操作と種々の処理」の記述が一つの階層にまとめて記述できています。

  const { renderDeleteDialog, confirmDelete } = useDeletePostConfirmDialog();
  const { deletePost } = useDeletePost();

  const handleDeletePost = useCallback(async () => {
    // 削除の確認ダイアログを開く。
    // acceptedは、 肯定的ボタン押下時に true,
    //   キャンセルボタン or ダイアログの外を押して閉じたときに false
    const { accepted } = await confirmDelete({ id, title });

    if (!accepted) return; // キャンセル時は処理に進まない

    await deletePost({ id });
    // 削除処理を実行する

    // あとは、一覧ページに遷移するなり何なり
  }, [confirmDelete, deletePost, id]);

  return (
    <>
      {renderDeleteDialog()}

Promise を使った実装方法は、APIを呼び出したあとのエラー表示 にも応用できます。 たとえばログイン画面を実装するとき、
一般的なエラー ... トーストやダイアログによる表示・エラー画面への遷移
◦ そのAPI呼び出し処理を記述するフック(今回の useDeletePost に当たる)の中で済ませる
特定種類のエラー ... フォームの中にメッセージを表示したい
◦ フックの中で処理せず、 confirmDelete と同様に { success: false, reason: .... } を非同期に返す ◦ あとは、ページ側で手続き的に処理する
といった実装にすることで、処理の流れが明快になります。

render hook のメリット

uhyo さんを通して有名になった実装パターンですが、render hook を使うことによって、モーダルの状態と、その状態に基づいた表示切替を一つのフックの中に閉じ込めることができるのがメリットになります。

Container コンポーネント もその条件を満たしますが、「ダイアログを開く関数を返す」ということが出来ず、その差が render hooks の利点です。

余談:目的駆動パッケージングしよう

関心を複数のフック・コンポーネントにキチンと分離することは、テスト容易性や、ファイルの読みやすさ、バージョン管理の利便性などの為に必須ですが、

そういった複数のファイルが互いに緊密に連携することになるので、