我正在 React 应用程序中使用 Redux Toolkit 和 RTK Query,并且我想在用户单击新按钮时取消或终止正在进行的 API 请求。我想确保如果在前一个请求完成之前触发新请求,则前一个请求将被中止或阻止完成。
问题:
我想确保,如果用户单击按钮加载新部分,则先前单击按钮的任何正在进行的请求都将被取消,并且不会干扰新请求。但是,我遇到了以下问题:
The ongoing request is not being killed.
The new request is being initiated regardless of the previous one.
预期行为:
我希望当用户单击新按钮加载新部分(例如“帖子”、“评论”)时,任何正在进行的请求(例如单击的上一个按钮)都应中止或阻止完成。新请求应立即发送,并且不应收到先前请求的响应。 StackBlitz 链接:https://stackblitz.com/edit/vitejs-vite-54uhcypb?file=src%2FApp.tsx,src%2Fservice.tsx&terminal=dev
(我知道我可以在需要时禁用按钮,但我不想要/喜欢这种方法)
任何人都可以建议如何正确实现这一点或如何在 RTK 查询中有效地终止请求?
重构 adminAPI 以包含通用端点 在 adminAPI 中添加单个动态端点以动态选择资源。 fetchResource 端点根据调用期间传入的资源类型动态构建查询 URL。
useLazyFetchResourceQuery 挂钩用于在单击按钮时手动触发查询。
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const adminAPI = createApi({
reducerPath: 'dashboardApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://jsonplaceholder.typicode.com',
}),
endpoints: (builder) => ({
fetchResource: builder.query({
query: ({ resourceType, params }) => {
// Add query parameters dynamically
const queryParams = params
? '?' +
Object.entries(params)
.map(([key, value]) => `${key}=${value}`)
.join('&')
: '';
return `/${resourceType}${queryParams}`;
},
}),
}),
});
export const { useLazyFetchResourceQuery } = adminAPI;
用单个动态查询替换当前的多个挂钩,并包含基于资源类型及其关联查询参数处理独特功能的逻辑。
import './App.css';
import React, { useState } from 'react';
import { useLazyFetchResourceQuery } from './service';
function App() {
const [resourceType, setResourceType] = useState(null); // Track current resource type
const [queryParams, setQueryParams] = useState(null); // Track any dynamic params
const [fetchResource, { data, isFetching, isError }] = useLazyFetchResourceQuery();
const handleFetchResource = (type) => {
setResourceType(type);
// Customize parameters or behavior for each resource
switch (type) {
case 'posts':
setQueryParams({ _limit: 10, _page: 1 }); // Example: Pagination for posts
break;
case 'comments':
setQueryParams({ _limit: 20 }); // Comments have a custom limit
break;
case 'albums':
setQueryParams(null); // No special params for albums
break;
case 'photos':
setQueryParams({ _limit: 5 }); // Load fewer photos
break;
case 'todos':
setQueryParams(null); // Name or filter for Todos (if needed)
break;
case 'users':
setQueryParams(null); // Users may not require parameters
break;
default:
setQueryParams(null);
}
// Trigger the dynamic query with both resource type and its specific params
fetchResource({ resourceType: type, params: queryParams });
};
return (
<>
<button onClick={() => handleFetchResource('posts')}>Load Posts</button>
<button onClick={() => handleFetchResource('comments')}>Load Comments</button>
<button onClick={() => handleFetchResource('albums')}>Load Albums</button>
<button onClick={() => handleFetchResource('photos')}>Load Photos</button>
<button onClick={() => handleFetchResource('todos')}>Load Todos</button>
<button onClick={() => handleFetchResource('users')}>Load Users</button>
<br />
{/* Show loading state */}
{isFetching && <p>Loading {resourceType}...</p>}
{/* Handle error state */}
{isError && <p style={{ color: 'red' }}>Error loading {resourceType} data.</p>}
{/* Show data dynamically with resource-specific logic */}
{data && (
<div>
<h3>{resourceType.charAt(0).toUpperCase() + resourceType.slice(1)}</h3>
<ul>
{/* Customize how to show data based on the resource */}
{Array.isArray(data) &&
data.map((item, index) => {
switch (resourceType) {
case 'posts':
return <li key={index}><strong>{item.title}</strong> - {item.body}</li>;
case 'comments':
return <li key={index}>"{item.body}" by User {item.email}</li>;
case 'albums':
return <li key={index}>{item.title}</li>;
case 'photos':
return <li key={index}><img src={item.thumbnailUrl} alt={item.title} /> {item.title}</li>;
case 'todos':
return <li key={index}>{item.title} - {item.completed ? '✅' : '❌'}</li>;
case 'users':
return (
<li key={index}>
{item.name} - {item.email}
</li>
);
}
})}
</ul>
</div>
)}
</>
);
}
export default App;