ike81818
@ike81818

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

useQueryのrefetchの挙動

解決したいこと

ページングのための、「次へ」ボタンをクリックしたら、
現在のページに1を足した値をクエリパラメータpageにセットして、
useQueryのrefetch()を実行することで、外部JSONデータを取得したいです。

発生している問題

クライアントで設定したエンドポイントのクエリパラメーターが、
サーバー側で取得できず、前回のクエリーパラメーターが設定されてしまいます。

コード
クライアント側

pages/index.js
import { useQuery} from "react-query";
import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      refetchOnWindowFocus: false,
    },
  },
});

export default function Home() {
  return (
    <QueryClientProvider client={queryClient}>
      <Main />
    </QueryClientProvider>
  );
}

function Main() {

  const [page, setPage] = useState(1); //現在のページ
  
  const params = {
    page: page,
  };

  const urlSearchParam = new URLSearchParams(params).toString();
  const defaultEndpoint = "/api/items?" + urlSearchParam;

  console.log("クライアント エンドポイント", defaultEndpoint);

  const { data, refetch } = useQuery(
    ["items"],
    async () => await fetch(defaultEndpoint).then((res) => res.json()),{
        enabled: true,
    }
  );

  const getItems = async () => {
    refetch();
  };

  // Pagination +
  const pageIncrement = () => {
      const newPage = page + 1;
      setPage(newPage);
      params.page = newPage;
      getItems();
  };

  return (
    <>
      <button onClick={() => pageIncrement()}>
        次へ
      </button>
    </>
  );
}

サーバー側

api/items/index.js
export const getSearchItems = async (req, res) => {
    const page = req.query.page;
  
    const params = {
      page: page,
    };

    const urlSearchParam = new URLSearchParams(params).toString();
  
    const defaultEndpoint =
      "http://xxxxx.ne.jp/search?" + urlSearchParam;
    
      console.log("サーバー エンドポイント",defaultEndpoint);
    
      try {
      const response = await fetch(defaultEndpoint);
      if (!response.ok) {
        throw new Error(`Error! status: ${response.status}`);
      }
      const users = await response.json();
      res.status(200).json({ users });
    } catch (err) {
      console.log(err);
    }
  };
  
  export default getSearchItems;

初回ロードで
クライアント側で 
page に1がセットされる
「次へ」ボタンをクリックすると、
クライアント側で
pageに2がセットされる
が、
サーバー側では、
pageが前回の1のまま

console.log("クライアント エンドポイント", defaultEndpoint);
出力 /api/items?page=2

console.log("サーバー エンドポイント",defaultEndpoint);
出力 http://xxxxx.ne.jp/search?page=1

0

1Answer

  const urlSearchParam = new URLSearchParams(params).toString();
  const defaultEndpoint = "/api/items?" + urlSearchParam; // ★

  console.log("クライアント エンドポイント", defaultEndpoint);

  const { data, refetch } = useQuery(
    ["items"],
    async () => await fetch(defaultEndpoint).then((res) => res.json()),{
        enabled: true,
    }
  );

refetch() すると useQuery() に渡した非同期関数が実行されますが、その中で参照している defaultEndpoint は、★の位置で文字列 "/api/items?page=1" にセットされたままです。それは pageIncrement() の中で setPage(newPage);params.page = newPage; しても変化しません。

params の値を反映するには非同期関数の中で改めて defaultEndpoint を組み立ててください:

  const { data, refetch } = useQuery(
    ["items"],
    async () => {
      const urlSearchParam = new URLSearchParams(params).toString();
      const defaultEndpoint = "/api/items?" + urlSearchParam;
      return await fetch(defaultEndpoint).then((res) => res.json());
    },
    {
      enabled: true,
    }
  );
2Like

Comments

  1. @ike81818

    Questioner

    サンプルコードまで書いていただきありがとうございます。
    おっしゃるとおり、
    const urlSearchParam = new URLSearchParams(params).toString();
    const defaultEndpoint = "/api/items?" + urlSearchParam;
    をasync() = {}の中に移動したところ、正常動作するようになりました。
    私の質問自体理解されるかどうか心配でしたが、解読していただきありがとうございます。

    なお、今回教えていただいた方法を応用して、次の問題を解決できないか考えています。
    https://qiita.com/ike81818/questions/65008371e30a912a16d1
    なにかお気づきの点がございましたら、よろしくお願いいたします。

Your answer might help someone💌