如何基于Redux Toolkit轮询场景管理查询加载状态?
解决RTK Query轮询时区分加载状态的问题
我有一个基于Redux Toolkit (RTK) Query的React应用,调用REST API时需要显示加载状态,同时每15秒轮询一次接口获取更新数据——因为响应里部分字段会频繁变化,但渲染加载区域的UI数据不会变。
当前用isFetching控制加载状态,问题是轮询时isFetching也会变为true,导致轮询过程中也显示加载动画。我希望仅在初始加载、或者请求参数(比如orgId)变更时显示加载状态,轮询时不触发该加载状态。
尝试过用isLoading替代,但参数变更触发重新请求时,isLoading不会生效,无法满足需求。
最小复现代码
FooComponent.tsx
import { useGetFooDataQuery } from '../app/services/foo'; import { CircularProgress } from '@mui/material'; const POLLING_INTERVAL = 20000; const FooComponent = ({ orgId }: { orgId: string }) => { const { data: foo, isLoading: isLoading, isFetching: isFetching, } = useGetFooDataQuery( { org_id: orgId, }, { pollingInterval: POLLING_INTERVAL, refetchOnMountOrArgChange: true, }, ); const { data_that_only_changes_when_org_id_changes, polled_data_prone_to_change } = foo ?? {}; return ( <div> <div>{isFetching ? <CircularProgress /> : data_that_only_changes_when_org_id_changes}</div> {/* 期望仅初始加载或org_id变更时显示加载 */} <div>{isFetching ? <CircularProgress /> : polled_data_prone_to_change}</div> {/* 每次请求(含轮询)都显示加载 */} </div> ); }; export default FooComponent;
foo.ts
import { api } from './api'; type GetFooDataQueryParams = { org_id: string; }; type FooData = { data_that_only_changes_when_org_id_changes: number; polled_data_prone_to_change: number; }; export const fooApi = api.injectEndpoints({ endpoints: (builder) => ({ getFooData: builder.query<FooData, GetFooDataQueryParams>({ query: (args) => { const body = { org_id: args.org_id, }; return { url: 'data/foo', method: 'GET', body: body, }; }, }), }), }); export const { useGetFooDataQuery } = fooApi;
api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { RootState } from '../store'; import { getApiURL } from '../../utilities/url'; const baseQuery = fetchBaseQuery({ baseUrl: getApiURL([]), prepareHeaders: (headers, { getState }) => { const token = (getState() as RootState).auth.token; if (token) { headers.set('Authorization', `Bearer ${token}`); } headers.set('Accept', 'application/json'); headers.set('Content-Type', 'application/json'); return headers; }, }); export const api = createApi({ reducerPath: 'splitApi', baseQuery: baseQuery, endpoints: () => ({}), }); export const enhancedApi = api.enhanceEndpoints({ endpoints: () => ({ getPost: () => 'test', }), });
临时解决方案
通过追踪请求触发原因(参数变更/轮询),控制加载状态:
import { useEffect, useState } from 'react'; import { useGetFooDataQuery } from '../services/fooService'; import { CircularProgress } from '@mui/material'; const POLLING_INTERVAL = 20000; const FooComponent = ({ orgId }: { orgId: string }) => { const [mountOrQueryArgChange, setMountOrQueryArgChange] = useState<boolean>(false); const { data: foo, isFetching: isFetching } = useGetFooDataQuery( { org_id: orgId, }, { pollingInterval: POLLING_INTERVAL, refetchOnMountOrArgChange: true, }, ); useEffect(() => { setMountOrQueryArgChange(true); }, [orgId]); useEffect(() => { if (!isFetching) setMountOrQueryArgChange(false); }, [isFetching]); const { data_that_only_changes_when_org_id_changes, polled_data_prone_to_change } = foo ?? {}; return ( <div> {/* 仅初始加载或org_id变更触发请求时显示加载 */} <div> {mountOrQueryArgChange && isFetching ? <CircularProgress /> : data_that_only_changes_when_org_id_changes} </div> {/* 每次请求(含轮询)都显示加载 */} <div>{isFetching ? <CircularProgress /> : polled_data_prone_to_change}</div> </div> ); }; export default FooComponent;
更优解决方案:拆分查询
将静态数据和动态数据拆分为两个独立的RTK Query端点,分别控制加载逻辑和轮询策略,无需额外状态追踪:
1. 修改API端点(foo.ts)
import { api } from './api'; type GetFooDataQueryParams = { org_id: string; }; type FooData = { data_that_only_changes_when_org_id_changes: number; polled_data_prone_to_change: number; }; export const fooApi = api.injectEndpoints({ endpoints: (builder) => ({ // 静态数据:仅在初始加载/参数变更时请求,不轮询 getStaticFooData: builder.query<{data_that_only_changes_when_org_id_changes: number}, GetFooDataQueryParams>({ query: (args) => ({ url: 'data/foo', method: 'GET', body: { org_id: args.org_id } }), transformResponse: (response: FooData) => ({ data_that_only_changes_when_org_id_changes: response.data_that_only_changes_when_org_id_changes }) }), // 动态数据:启用轮询,实时获取更新 getPolledFooData: builder.query<{polled_data_prone_to_change: number}, GetFooDataQueryParams>({ query: (args) => ({ url: 'data/foo', method: 'GET', body: { org_id: args.org_id } }), transformResponse: (response: FooData) => ({ polled_data_prone_to_change: response.polled_data_prone_to_change }) }), }), }); export const { useGetStaticFooDataQuery, useGetPolledFooDataQuery } = fooApi;
2. 修改组件逻辑(FooComponent.tsx)
import { useGetStaticFooDataQuery, useGetPolledFooDataQuery } from '../app/services/foo'; import { CircularProgress } from '@mui/material'; const POLLING_INTERVAL = 20000; const FooComponent = ({ orgId }: { orgId: string }) => { // 静态数据:用isLoading控制加载(仅初始/参数变更时显示) const { data: staticData, isLoading: isStaticLoading } = useGetStaticFooDataQuery({ org_id: orgId }); // 动态数据:用isFetching控制加载(所有请求含轮询都显示) const { data: polledData, isFetching: isPolledFetching } = useGetPolledFooDataQuery( { org_id: orgId }, { pollingInterval: POLLING_INTERVAL } ); return ( <div> <div> {isStaticLoading ? <CircularProgress /> : staticData?.data_that_only_changes_when_org_id_changes} </div> <div> {isPolledFetching ? <CircularProgress /> : polledData?.polled_data_prone_to_change} </div> </div> ); }; export default FooComponent;
这种方案逻辑更清晰,完全利用RTK Query自身的状态管理能力,避免了手动追踪请求触发原因的复杂度。
内容的提问来源于stack exchange,提问作者GNG
相关产品推荐
相关产品推荐

