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

如何优化React Query中批量Movie组件的Mutation初始化逻辑

优化React Query Mutation重复初始化的方案

针对你提到的每个Movie组件独立初始化mutation导致的资源浪费,以及多页面存在多种电影操作的场景,这里有几个实用的优化方案:

1. 抽离自定义Hook复用Mutation逻辑

把各类电影操作的Mutation封装成独立的自定义Hook,不管是在Movie组件还是其他页面组件里,都能直接调用,既避免重复代码,也统一了操作逻辑。

比如针对「添加至观影清单」的操作,封装一个Hook:

// hooks/useAddToWatchlist.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { addToWatchlistApi } from '../api/movies';

export function useAddToWatchlist() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: (movie: TMovie) => addToWatchlistApi(movie),
    // 统一配置回调,比如操作成功后刷新缓存
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['watchlist'] });
    },
  });
}

然后在Movie组件里直接用:

// Movie Component
import { useAddToWatchlist } from '../hooks/useAddToWatchlist';

function Movie({ movie }: { movie: TMovie }) {
  const { mutate } = useAddToWatchlist();

  return (
    <div onClick={() => mutate(movie)}>
      <img src={movie.posterPath} />
    </div>
  );
}

如果有其他操作(比如标记已看、移除清单),同理封装对应的useMarkAsWatched、useRemoveFromWatchlist等Hook,多页面直接导入即可。

2. 父组件统一初始化Mutation,传递mutate函数

如果只是单页面内的批量组件(比如100个Movie),可以在父组件MoviesPage里只初始化一次Mutation,然后把mutate函数通过props传给每个子组件,这样整个页面只会有一个Mutation实例,大幅减少资源占用。

修改后的代码:

// MoviesPage.tsx
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { addToWatchlistApi } from '../api/movies';
import Movie from './Movie';

export default async function MoviesPage() {
  const movies = await getMovies();
  const queryClient = useQueryClient();
  
  const { mutate } = useMutation({
    mutationFn: (movie: TMovie) => addToWatchlistApi(movie),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['watchlist'] });
    },
  });

  return (
    <div>
      {movies.map(movie => (
        <Movie key={movie.id} movie={movie} handleAddToWatchlist={mutate} />
      ))}
    </div>
  );
}

// Movie Component
function Movie({ movie, handleAddToWatchlist }: { movie: TMovie; handleAddToWatchlist: (movie: TMovie) => void }) {
  return (
    <div onClick={() => handleAddToWatchlist(movie)}>
      <img src={movie.posterPath} />
    </div>
  );
}

如果需要同时支持多种操作,父组件可以初始化多个Mutation,把对应的mutate函数都传给子组件即可。

3. 用工厂函数批量生成操作Hook

如果有大量结构相似的电影操作(比如添加/删除/更新不同类型的清单),可以写一个工厂函数来批量生成对应的useMutation Hook,避免重复编写相似代码:

// hooks/useMovieOperation.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { addToWatchlistApi, markAsWatchedApi, removeFromWatchlistApi } from '../api/movies';

type MovieOperationType = 'addToWatchlist' | 'markAsWatched' | 'removeFromWatchlist';

export function createMovieOperationHook(operationType: MovieOperationType) {
  // 根据操作类型匹配对应的API
  const getApiFn = () => {
    switch (operationType) {
      case 'addToWatchlist':
        return (movie: TMovie) => addToWatchlistApi(movie);
      case 'markAsWatched':
        return (movie: TMovie) => markAsWatchedApi(movie);
      case 'removeFromWatchlist':
        return (movieId: number) => removeFromWatchlistApi(movieId);
      default:
        throw new Error('Invalid operation type');
    }
  };

  // 返回对应的Hook
  return function useMovieOperation() {
    const queryClient = useQueryClient();
    
    return useMutation({
      mutationFn: getApiFn(),
      onSuccess: () => {
        // 根据操作类型刷新对应的缓存
        queryClient.invalidateQueries({ queryKey: [operationType] });
      },
    });
  };
}

// 生成具体的Hook
export const useAddToWatchlist = createMovieOperationHook('addToWatchlist');
export const useMarkAsWatched = createMovieOperationHook('markAsWatched');

之后在组件里的用法和方案1完全一致,新增操作时只需要在工厂函数里加个case就行,维护成本很低。


内容的提问来源于stack exchange,提问作者123 456

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 05:42:40