React-Query为何返回上一次搜索数据?求助排查问题
问题原因与修复方案
核心问题
- React Query 缓存未区分不同搜索请求:你的
queryKey固定为"tv-results",React Query会基于这个key缓存请求结果。当搜索关键词变化时,queryKey没有同步更新,导致React Query直接复用了旧的缓存数据。 - 异步状态更新导致请求参数滞后:
onSubmit中先调用setTvSearchQuery再执行refetch,但React的setState是异步操作,此时tvSearchQuery还没更新为新的搜索词,请求依然使用旧参数发起。 - 请求配置未动态绑定最新参数:
options定义在组件顶层,虽然每次渲染会重新创建,但fetchTvData直接引用该配置,无法确保每次请求都使用最新的搜索关键词。
修复步骤
1. 给queryKey添加搜索关键词标识
将tvSearchQuery加入queryKey,让React Query为不同的搜索词维护独立缓存:
const { data, refetch } = useQuery({ queryKey: ["tv-results", tvSearchQuery], // 加入搜索词,区分不同请求 queryFn: fetchTvData, refetchOnWindowFocus: false, enabled: false, onSuccess, onError, });
2. 避免依赖异步状态更新传递参数
提取基础请求配置并修改fetchTvData,让它直接接收搜索词参数,彻底规避setState异步的问题:
// 提取基础配置,避免重复创建 const baseOptions = { method: "GET", url: "https://api.themoviedb.org/3/search/tv", params: { include_adult: "false", language: "en-US", page: "1", }, headers: { accept: "application/json", Authorization: "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmNzNkMDE0YzkzNDQ5OTQxNThkZjNlNWQ0MWVjZGY1NCIsInN1YiI6IjY0ZTQ2NzlhMWZlYWMxMDBjNDZjMWUyNCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.quqnVGOyEV10UKyr4PCb39i6JpkdKtvX4kDSoIq9gss", }, }; function fetchTvData(searchQuery: string): Promise<void | ShowData> { return axios.request({ ...baseOptions, params: { ...baseOptions.params, query: searchQuery, }, }); }
然后更新onSubmit和useQuery的queryFn,确保使用最新的搜索词:
function onSubmit(data: FormValues) { const searchTerm = data.tvSearch; setTvSearchQuery(searchTerm); refetch(); // 此时queryKey已更新,React Query会触发新请求 } // useQuery中的queryFn同步使用tvSearchQuery const { data, refetch } = useQuery({ queryKey: ["tv-results", tvSearchQuery], queryFn: () => fetchTvData(tvSearchQuery), // 其余配置不变 });
3. 修正TextField的type属性
你之前的TextField使用了非标准的type="tvSearch",这会导致浏览器解析异常,改成标准的type="text":
<TextField label="Enter a show or movie" type="text" {...register("tvSearch")} error={!!errors.tvSearch} helperText={errors.tvSearch?.message} />
完整修复代码
import { useQuery } from "react-query"; import axios from "axios"; import { Stack, TextField, Button } from "@mui/material"; import { useForm } from "react-hook-form"; import { useState } from "react"; interface FormValues { tvSearch: string; } interface Tv { id: number; } interface ShowData { data: Tv[]; } const baseOptions = { method: "GET", url: "https://api.themoviedb.org/3/search/tv", params: { include_adult: "false", language: "en-US", page: "1", }, headers: { accept: "application/json", Authorization: "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJmNzNkMDE0YzkzNDQ5OTQxNThkZjNlNWQ0MWVjZGY1NCIsInN1YiI6IjY0ZTQ2NzlhMWZlYWMxMDBjNDZjMWUyNCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.quqnVGOyEV10UKyr4PCb39i6JpkdKtvX4kDSoIq9gss", }, }; function ShowBox() { const [tvSearchQuery, setTvSearchQuery] = useState(""); const form = useForm<FormValues>({ defaultValues: { tvSearch: "", }, }); const { register, handleSubmit, formState } = form; const { errors } = formState; function fetchTvData(searchQuery: string): Promise<void | ShowData> { return axios.request({ ...baseOptions, params: { ...baseOptions.params, query: searchQuery, }, }); } function onSuccess(data: object) { console.log("success", data); } function onError(error: Error) { console.log("ERROR", error); } const { data, refetch } = useQuery({ queryKey: ["tv-results", tvSearchQuery], queryFn: () => fetchTvData(tvSearchQuery), refetchOnWindowFocus: false, enabled: false, onSuccess, onError, }); function onSubmit(data: FormValues) { const searchTerm = data.tvSearch; setTvSearchQuery(searchTerm); refetch(); } console.log(data?.data); return ( <form onSubmit={handleSubmit(onSubmit)} noValidate> <Stack spacing={2}> <TextField label="Enter a show or movie" type="text" {...register("tvSearch")} error={!!errors.tvSearch} helperText={errors.tvSearch?.message} /> <Button type="submit" variant="contained"> Search </Button> </Stack> </form> ); } export default ShowBox;
内容的提问来源于stack exchange,提问作者Brenden Baio
相关产品推荐
相关产品推荐

