2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

確認ページ以外に遷移したときにフォームの内容をリセットする【Next.js x Redux】

Last updated at Posted at 2021-04-09

Form消去成功.mov.gif

やりたいこと

Next.jsで書かれたサイトに、確認ページ付きのフォームを実装する。

  • フォーム(A)→確認ページ(B) の、ページを跨いだデータ共有にReduxを使う
  • しかし、**それ以外のページ(C)**に遷移した時には、フォーム内容に紐づいたReduxのストアを消去しないといけない。

環境

  • Node.js 12.18.4
    • npm 6.14.8
  • Typescript 4.0.7
  • React.js 16.13.0
    • React Redux 7.2.3
    • Redux Toolkit 1.5.1
  • Next.js 10.1.3

Before

Reduxでデータを保持するだけだと、**確認ページ(B)**にはきちんと入力した内容が反映されるが、**トップページ(C)に一度戻ってから入力ページ(A)**に再度進んだ時に前回の入力内容が残ってるのでダメ。
form消去失敗.mov.gif

Next.js ドキュメントを読んでみると…

Next.js Documents | next/router # router.events

router.eventsを使うことで、ページ遷移時のイベントハンドラを登録できるらしい。
この機能を盛り込んだCustom Hookを次のように定義した。

useActionOnRouteChange.tsx
import { useRouter } from "next/dist/client/router";
import { useEffect } from "react";

type RouteChangeAction = (
  pathName: string,
  options: { shallow?: boolean }
) => void;

type PathnamePredicate = (pathName: string) => boolean;

// どんなPathnameが来ても真
const anyPath: PathnamePredicate = () => true;

/**
 * Route変更時に、pathnameが条件を満たす場合にのみアクションを実行するように設定するHook
 * @param action Route変更時に呼び出されるアクション
 * @param pathPred アクションが呼び出されるかどうかを決める条件 デフォルトでは常に真
 */
const useActionOnRouteChange = (
  action: RouteChangeAction,
  pathPred: PathnamePredicate = anyPath
) => {
  // routerオブジェクトを使うためのおまじない
  const router = useRouter();

  useEffect(() => {
    const handler: RouteChangeAction = (path, options) => {
      if (pathPred(path)) action(path, options);
    };

    // mount時にイベントハンドラを登録
    router.events.on("routeChangeStart", handler);
    return () => {
      router.events.off("routeChangeStart", handler);
    };
  }, [action, pathPred]); // 一応引数に与えられる関数が変わった時にも再設定する。
};

export default useActionOnRouteChange;

このHookを、遷移前のページ(今回はフォーム入力ページ(A))で使用すると、第2引数で与えた条件を満たすPathname(ここでは確認ページ以外のページ(C))に遷移したときに、第1引数で与えたアクションが実行される。

pages/input-name/index.tsxの一部

const InputNamePage = () => {
  // ...
  const dispatch = useDispatch();

  useActionOnRouteChange(
    () => {
      console.log("reset");
      // reduxのActionをここから呼び出す
    },
    (pathName) => pathName !== "/input-name/confirm" // このPath以外に遷移したとき
  );

  // ...

  return (
    <div>
      { /* ... */ }
    </div>
  );
}

export default InputNamePage;

結果

一度トップページ(C)に戻ると、ちゃんとフォームがリセットされた。
Form消去成功.mov.gif

ページ遷移の時のデータ消去は、中央管理にしても良いかもしれないが、この方法を使うと、消したいデータを入力する画面の定義の中に消去のロジックも書くことになり、凝集度が高くなって良いかもしれない。
中央管理にするには、_app.tsxの中でuseActionOnRouteChangeを使えば良いでしょう。

Custom Hookにすれば、ロジックだけを取り出して、部品化できる!
Hooks, バンザイ!

参考にした記事

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?