如何基于RTK Query请求类型优雅控制toast提示展示
针对RTK Query端点自定义Toast规则的实现方案
最简洁优雅的实现方式是利用RTK Query原生支持的extraOptions端点配置字段,不需要在中间件里硬编码接口名单,配置和接口定义绑定,维护成本极低。
实现步骤
1. 在端点定义处配置个性化规则
RTK Query的每个endpoint都支持传入extraOptions字段,该字段会原封不动挂载到对应请求生成的action的meta.arg属性上,你可以在这里直接标记不需要提示的接口,或者给特定接口配置自定义提示文案、关闭时长等规则:
// api服务定义文件示例 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export const appApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: '/api' }), endpoints: (builder) => ({ // 不需要展示任何toast的后台静默拉取接口 getBackgroundConfig: builder.query({ query: () => '/system/config', extraOptions: { disableToast: true } // 标记关闭toast }), // 需要自定义成功/失败提示的操作类接口 submitForm: builder.mutation({ query: (formData) => ({ url: '/form/submit', method: 'POST', body: formData }), extraOptions: { toastConfig: { successMsg: '表单提交成功', errorMsg: '提交失败,请检查网络后重试' } } }), // 不需要额外配置的接口,自动走全局默认toast规则 deleteItem: builder.mutation({ query: (id) => ({url: `/item/${id}`, method: 'DELETE'}) }) }) })
2. 改造中间件读取配置
在中间件执行toast逻辑前,先读取当前请求携带的extraOptions配置,优先处理跳过规则,再合并自定义配置覆盖默认逻辑:
import { isFulfilled, isPending, isRejectedWithValue, Middleware, } from "@reduxjs/toolkit"; import { toast } from "react-toastify"; const queryStatus: Middleware = () => (next) => (action) => { const TOAST_QUERY_ID = "query-status"; // 非RTK Query请求直接放行 if (!isPending(action) && !isFulfilled(action) && !isRejectedWithValue(action)) { return next(action); } // 读取当前端点的自定义配置 const { disableToast = false, toastConfig = {} } = action.meta.arg.extraOptions || {}; // 标记关闭toast的请求直接放行,不做提示处理 if (disableToast) { return next(action); } // 全局默认toast配置 const defaultConfig = { loadingMsg: "请求处理中", successMsg: "操作成功", errorMsg: "请求出错,请稍后重试", successAutoClose: 2000, errorAutoClose: 3000, }; // 合并端点自定义配置 const finalConfig = { ...defaultConfig, ...toastConfig }; if (isPending(action)) { toast(finalConfig.loadingMsg, { isLoading: true, toastId: TOAST_QUERY_ID, type: toast.TYPE.DEFAULT, }); } else if (isFulfilled(action)) { const isMutation = action.meta.arg.type === "mutation"; toast.update(TOAST_QUERY_ID, { render: isMutation ? finalConfig.successMsg : "", type: toast.TYPE.SUCCESS, isLoading: false, autoClose: isMutation ? finalConfig.successAutoClose : 100, }); } else if (isRejectedWithValue(action)) { toast.update(TOAST_QUERY_ID, { render: finalConfig.errorMsg, type: toast.TYPE.ERROR, isLoading: false, autoClose: finalConfig.errorAutoClose, }); } return next(action); }; export default queryStatus;
方案优势
- 配置和接口定义强绑定,新增/修改接口规则时不需要翻找中间件代码,不会出现硬编码名单漏改、错改的问题
- 基于官方原生扩展能力实现,没有hack逻辑,版本兼容性好
- 扩展性强,后续如果需要给特定接口加toast持久显示、自定义交互、操作跳转等逻辑,只需要在
extraOptions里新增对应字段,在中间件里统一处理即可 - 完全兼容你之前实现的query/mutation差异化逻辑,不需要重构原有代码
内容的提问来源于stack exchange,提问作者Osmanys Fuentes-Lombá
相关产品推荐
相关产品推荐

