💿

Remixで一覧から何かを削除→再取得→覧を更新する方法

2024/02/28に公開
2

背景

具体的にはfetcher.submitでDELETEした後に何らかの方法で再取得処理を行うことで削除したものを一覧から表示させないようにしたい。

愚直に書こうにもfetcherはawaitできないので再取得を削除前に行ってしまう。
う〜ん、ベストプラクティスを知りたい...。

// 失敗例
onClick={() => {                
  await fetcher.submit({ id: 1 }, { action: "/users/delete", method: "POST" });
  fetcher.load('/users')
}}

削除処理が終わったことをどう知るか

https://gist.github.com/samselikoff/510c020e4c9ec17f1cf76189ce683fa8
https://zenn.dev/yupix/articles/65dca9fe6eb317#コンポーネントからサーバー側にアクセスする

ここらへんを見ると、useEffect を使って特定の状態になった時に何かしらを実行することが推奨されているっぽい。

結論

@coji さんコメントありがとうございます🙏🏻
https://zenn.dev/link/comments/7272b03e67ab56

https://remix.run/docs/en/main/discussion/form-vs-fetcher#updating-a-record

action実行後にloaderを実行してくれるとのことで何もしなくても大丈夫でした。
他のページのactionを実行してもloaderが実行されるようで便利。

(これで動かなかった気がするけど当時のコード覚えておらず...)

Remixではこのようにaction関数の結果にかかわらず、すべてのアクティブなloaderを再検証しますが、オプトアウトするには以下のよう shouldRevalidate を利用する。

export const loader = () => {
  return json({ null });
};
 
// このルートでは常に再検証を行わない
+ export const shouldRevalidate = () => false;

結論(ボツ)

fetcher.load() とかで良いのかと思ったけど、以下を見ると2つの方法があった。
2つ目を採用した。

https://github.com/remix-run/react-router/discussions/10381

// 1つ目
const navigate = useNavigate()
navigate('.', { replace: true })

// 2つ目
const revalidator = useRevalidator();
import { useEffect } from "react";

import { DeleteRounded } from "@mui/icons-material";
import { IconButton } from "@mui/joy";
import {
  Outlet,
  useFetcher,
  useLoaderData,
  useRevalidator,
  useRouteError,
} from "@remix-run/react";

function Layout() {
  const fetcher = useFetcher();
  function onDelete(id: number) {
    fetcher.submit({ id }, { action: "/users/delete", method: "POST" });
  }

  const revalidator = useRevalidator();
  useEffect(() => {
    if (fetcher.data && fetcher.formAction === "/users/delete") { // 削除を検知
      revalidator.revalidate(); // ここで再取得
    }
  }, [fetcher.data, fetcher.formAction, revalidator]);

  return (
    <div>
      <tbody>
        <tr>
          <td>
            <IconButton
              size="sm"
              variant="plain"
              color="danger"
              onClick={() => onDelete(1)} // 削除ボタンをクリック
            >
              <DeleteRounded />
            </IconButton>
          </td>
        </tr>
      </tbody>
    </div>
  );
}

Discussion

saneatsusaneatsu

なんだか最初再fetchしてくれなかった気もするんですが全然ちゃんと動きますね...。いらぬ記事を作ってしまいました。。

ありがとうございます!記事更新しました🙇🏻‍♂️