🔍
【Tips】TanStack Query で incremental search する
React と TanStack Query で incremental searchしたくなった時の備忘録です。
対象読者
- TanStack Query (useQuery) の基本的な使用方法を知っている方
普段通り実装すると よくない挙動をする
普段と同じように useQuery
に keyと取得関数を渡しただけでは検索文字列を変えるたびにdataがnullになってしまい よくない挙動になってしまいます。
const [query, setQuery] = useState("");
const searchResult = useQuery({
queryKey: ["items", "search", query],
queryFn: async () => {
// サーバから検索結果を取得
return await searchItems(query);
},
});
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<ul>
{searchResult.data?.map((item) => <li key={item}>{item}</li>) ?? "..."}
</ul>
検索文字列(query)を変えるたびに "..."
が表示されてしまっています。
これは検索文字列が変わるたびに queryKey
が変わることで searchResult.data
がnullになってしまうためです。
リセットしないようにする
文字列を打ち込んでも表示したアイテムが消えないようにするには useQueryの placeholderData: keepPreviousData
オプションを利用するといい感じになります。
const [query, setQuery] = useState("");
const searchResult = useQuery({
queryKey: ["items", "search", query],
queryFn: async () => {
return await searchItems(query);
},
+ placeholderData: keepPreviousData,
});
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<ul>
- {searchResult.data?.map((item) => <li key={item}>{item}</li>) ?? "..."}
+ {searchResult.data?.map((item) => <li key={item}>{item}</li>)}
+ {searchResult.isFetching && "..."}
</ul>
以下のドキュメントから着想を得ました。
まとめ
TanStack Queryで incremental search したいときに placeholderData: keepPreviousData
を指定すると良さそうな挙動になりました。
incremental search に限らず、「キーが変わっても一旦は前のデータを表示しといて欲しいんだけど」みたいな状況に利用できそうですね。
また、queryをdebounceするなどの処理も挟むとサーバにも優しくなってより良さそうです。
Discussion