😄

[Output]useDeferredValueを使ってみた。

2024/07/12に公開1

内容

Reactのドキュメントをざっくり見ていたときに、気になったfooksがあったので勉強がてら実装をしてみました。

環境

  • React
  • TypeScript
  • React Query

useDeferredValueとは

[useDeferredValue – React]
https://ja.react.dev/reference/react/useDeferredValue

useDeferredValue は、UI の一部の更新を遅延させるための React フックです。

引数に遅延させたい値をいれることができます。

初回時には、渡した初期値になるのでレンダリングが重複することはなさそうです。

基本的にsuspenseと一緒に使うものと記載があります。

またSuspenseとuseDeferredValueどちらも、React Queryなどのデータフェッチライブラリと組み合わせて使用するのが一般的とあるので、今回はReact Queryを使用して実装をしてみます。

本題(実装)

完成コード

Search.tsx

import { useDeferredValue, useState, Suspense } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import SearchResult from "./SearchResult";

const queryClient = new QueryClient();

const Search = () => {
  const [query, setQuery] = useState<string | number>("");
  const deferredQuery = useDeferredValue(query);

  return (
    <QueryClientProvider client={queryClient}>
      <label>
        Search Data:
        <input type="text" onChange={(e) => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <SearchResult query={deferredQuery} />
      </Suspense>
    </QueryClientProvider>
  );
};

export default Search;

react-queryを使用するためにQueryClientProvider で要素をラップします。

SeachResult.tsx

import { useQuery } from "@tanstack/react-query";

interface SearchResultInterface {
  query: string | number;
}

interface DataInterface {
  body: string;
  id: number;
  title: string;
  userId: number;
}

const API_URL = "https://jsonplaceholder.typicode.com/posts";

const fetchPosts = async (): Promise<DataInterface[]> => {
  const response = await fetch(API_URL);
  if (!response.ok) {
    throw new Error("Network response was not ok");
  }
  return response.json();
};

const SearchResult = ({ query }: SearchResultInterface) => {
  const { data, error, isLoading } = useQuery<DataInterface[], Error>({
    queryKey: ["posts"],
    queryFn: fetchPosts,
  });

  if (!query) return null;
  if (isLoading) return <h2>Loading...</h2>;
  if (error) return <h2>Error: {error.message}</h2>;

  const filteredData = query
    ? data?.filter((item) => item.body.includes(String(query)))
    : [];

  return (
    <>
      {filteredData && filteredData.length > 0
        ? filteredData.map((item) => (
            <div key={item.id}>
              <h3>{item.title}</h3>
              <p>{item.body}</p>
            </div>
          ))
        : query !== "" && <h2>No Search Data</h2>}
    </>
  );
};

export default SearchResult;

useQueryで状態を管理し、propsで渡したqueryに応じてデータをソートしています。

これで非同期コンポーネントとして扱え、恩恵が受けれた?かもしれないです。

まとめ

実際コードを実行してみると、特に恩恵を受けた感じはありませんでしたが、そもそもデータ量が少ないのでわかりにくかったかもしれないです。もっと大きなデータを扱うときになったら再度使用してみようと思います。

Discussion