You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一组件中使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 12:18:15