如何在同一组件中使用RTK Query两个端点的查询结果?
问题描述
我有一个cardList组件,通过第一个请求获取电影列表并展示,该功能运行正常。相关RTK Query代码如下:
export const movieApi = createApi({ reducerPath: "movieApi", tagTypes: ["Movies"], baseQuery: fetchBaseQuery({ baseUrl: "https://api.themoviedb.org/3/" }), endpoints: (builder) => ({ getMovies: builder.query<MoviesArray, number | void>({ query: (page = 1) => ({ url: "movie/top_rated?api_key=3046e4e04c3ba6250edd1d3c6eb2d4ad&language=en-US&page=" + page, method: "GET", }) }), searchMovies: builder.mutation<MoviesArray, string | void>({ query: (searchValue) => ({ url: `search/movie?api_key=3046e4e04c3ba6250edd1d3c6eb2d4ad&language=en-US&query=${searchValue}&page=1&include_adult=true`, method: "GET", }), }), }) }); export const { useGetMoviesQuery, useSearchMoviesMutation } = movieApi;
现在的问题是无法在同一组件中展示搜索输入的结果:能获取到新的搜索结果数组,但不知道如何替换原有结果。我不想新增路由和SearchMovie组件,希望有更简洁的解决方案。之前尝试过providesTags和invalidatesTags但没搞清楚,觉得在Redux/Saga或普通RTK里通过action和state实现这个功能要简单得多。
解决方案
1. 将搜索接口从Mutation改为Query
RTK Query中,GET请求应该用query而非mutation,Mutation主要用于会修改服务器数据的请求(POST/PUT/DELETE等)。把searchMovies改成query,既符合规范,也更容易在组件中获取和管理结果:
export const movieApi = createApi({ reducerPath: "movieApi", tagTypes: ["Movies"], baseQuery: fetchBaseQuery({ baseUrl: "https://api.themoviedb.org/3/" }), endpoints: (builder) => ({ getMovies: builder.query<MoviesArray, number | void>({ query: (page = 1) => ({ url: `movie/top_rated?api_key=3046e4e04c3ba6250edd1d3c6eb2d4ad&language=en-US&page=${page}`, method: "GET", }) }), // 修改为query类型 searchMovies: builder.query<MoviesArray, string>({ query: (searchValue) => ({ url: `search/movie?api_key=3046e4e04c3ba6250edd1d3c6eb2d4ad&language=en-US&query=${searchValue}&page=1&include_adult=true`, method: "GET", }), }), }) }); export const { useGetMoviesQuery, useSearchMoviesQuery } = movieApi;
2. 在组件中维护搜索状态,切换数据源
在cardList组件里,用一个状态变量记录用户的搜索输入,根据这个变量决定展示默认电影列表还是搜索结果:
import { useState } from 'react'; import { useGetMoviesQuery, useSearchMoviesQuery } from './movieApi'; const CardList = () => { const [searchTerm, setSearchTerm] = useState(''); // 获取默认热门电影列表 const { data: defaultMovies, isLoading: defaultLoading } = useGetMoviesQuery(); // 根据搜索词获取结果,搜索词为空时跳过请求 const { data: searchMovies, isLoading: searchLoading } = useSearchMoviesQuery(searchTerm, { skip: !searchTerm, }); // 决定当前展示哪组数据 const displayMovies = searchTerm ? searchMovies : defaultMovies; const isLoading = searchTerm ? searchLoading : defaultLoading; if (isLoading) return <div>加载中...</div>; if (!displayMovies) return <div>无数据</div>; return ( <div> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="搜索电影..." /> <div className="card-list"> {displayMovies.results.map(movie => ( <div key={movie.id} className="movie-card"> <h3>{movie.title}</h3> {/* 渲染其他电影信息 */} </div> ))} </div> </div> ); }; export default CardList;
3. 可选:用useLazyQuery手动触发搜索
如果希望用户点击搜索按钮才发起请求(而非输入时实时请求),可以用useLazyQuery手动控制请求时机:
import { useState } from 'react'; import { useGetMoviesQuery, useLazyQuery } from './movieApi'; const CardList = () => { const [inputValue, setInputValue] = useState(''); const { data: defaultMovies, isLoading: defaultLoading } = useGetMoviesQuery(); // 懒加载搜索query,手动触发请求 const [triggerSearch, { data: searchMovies, isLoading: searchLoading }] = useLazyQuery(movieApi.endpoints.searchMovies); const handleSearch = () => { if (inputValue.trim()) { triggerSearch(inputValue.trim()); } }; const displayMovies = searchMovies ? searchMovies : defaultMovies; const isLoading = searchLoading || defaultLoading; if (isLoading) return <div>加载中...</div>; if (!displayMovies) return <div>无数据</div>; return ( <div> <input type="text" value={inputValue} onChange={(e) => setInputValue(e.target.value)} placeholder="输入电影名..." /> <button onClick={handleSearch}>搜索</button> <div className="card-list"> {displayMovies.results.map(movie => ( <div key={movie.id} className="movie-card"> <h3>{movie.title}</h3> {/* 渲染其他电影信息 */} </div> ))} </div> </div> ); };
关于providesTags/invalidatesTags的说明
这两个API主要用于缓存更新,比如当你修改了服务器数据后,让相关的query自动重新获取最新数据。而你的场景是在两个独立数据源(默认列表和搜索结果)之间切换,不需要缓存失效,直接通过状态变量控制展示哪组数据即可,没必要用这两个API。
内容的提问来源于stack exchange,提问作者ctacbarada
相关产品推荐
相关产品推荐

