代码之家  ›  专栏  ›  技术社区  ›  Gülü Əhmədova

如何使用RTK查询在React中阻止或终止正在进行的请求?

  •  1
  • Gülü Əhmədova  · 技术社区  · 7 月前

    我正在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查询中有效地终止请求吗?

    1 回复  |  直到 7 月前
        1
  •  1
  •   Szymon Roziewski    7 月前

    重构adminAPI以包含通用端点 在adminAPI中添加一个动态端点以动态选择资源。 fetchResource端点根据调用期间传入的resourceType动态构建查询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;
    

    将当前的多个钩子替换为单个动态查询,并包含基于resourceType及其相关查询参数处理独特功能的逻辑。

    import './App.css';
    import React, { useState } from 'react';
    import { useLazyFetchResourceQuery } from './service';
    
    function App() {
      const [resourceType, setResourceType] = useState(null); 
      const [queryParams, setQueryParams] = useState(null); 
    
      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 }); 
            break;
          case 'comments':
            setQueryParams({ _limit: 20 });
            break;
          case 'albums':
            setQueryParams(null);
            break;
          case 'photos':
            setQueryParams({ _limit: 5 }); 
            break;
          case 'todos':
            setQueryParams(null);
            break;
          case 'users':
            setQueryParams(null); 
            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;
    

    请求会根据点击的按钮动态触发。 任何正在进行的请求都会自动取消,并通过内置的AbortController替换为新的请求。