React+TypeScript如何从Axios拦截器触发Redux Dispatch事件
React+TypeScript:在Axios拦截器中触发Redux Dispatch
问题背景
作为React+TypeScript新手,希望通过Axios拦截器统一管理API调用前后的Redux状态(比如加载状态),通过请求头参数custom_loader控制是否触发加载器,同时不需要将API响应存储到Redux中。之前尝试在拦截器中调用封装的ShowLoader函数时触发错误,原因是违反了React Hook的使用规则。
解决方案
核心思路
Axios拦截器不属于React组件树,无法使用useDispatch这类Hook。正确的做法是直接导入Redux Store实例,调用其全局的dispatch方法来触发action。
步骤1:修改Axios拦截器代码
更新src/Helpers/AxiosInterceptors.ts,直接导入Store并在拦截器中触发加载状态的action:
import { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestConfig, } from "axios"; import store from "../Redux/store"; import { showLoader, hideLoader } from "../Redux/Loader/slice"; const onRequest = ( config: InternalAxiosRequestConfig ): InternalAxiosRequestConfig => { // 根据请求头判断是否显示加载器 if (config.headers?.custom_loader === "SHOW_LOADER") { store.dispatch(showLoader()); } console.info(`[request] [${JSON.stringify(config)}]`); return config; }; const onRequestError = (error: AxiosError): Promise<AxiosError> => { // 请求失败时强制隐藏加载器 store.dispatch(hideLoader()); console.error(`[request error] [${JSON.stringify(error)}]`); return Promise.reject(error); }; const onResponse = (response: AxiosResponse): AxiosResponse => { // 请求成功后隐藏加载器 if (response.config.headers?.custom_loader === "SHOW_LOADER") { store.dispatch(hideLoader()); } console.info(`[response] [${JSON.stringify(response)}]`); return response; }; const onResponseError = (error: AxiosError): Promise<AxiosError> => { // 响应失败时隐藏加载器 store.dispatch(hideLoader()); console.error(`[response error] [${JSON.stringify(error)}]`); return Promise.reject(error); }; export function setupInterceptorsTo( axiosInstance: AxiosInstance ): AxiosInstance { axiosInstance.interceptors.request.use(onRequest, onRequestError); axiosInstance.interceptors.response.use(onResponse, onResponseError); return axiosInstance; }
步骤2:删除无用的DispatchActions.ts文件
由于直接使用Store的全局dispatch,之前封装的DispatchActions.ts文件已无存在必要,可直接删除。
步骤3:优化组件类型定义
避免使用any类型,改用Redux自动生成的RootState和AppDispatch提升类型安全性:
Employee.tsx
import { useEffect, useState } from "react"; import { IEmployeeList } from "../../Models/Employee"; import { EmployeeList } from "./EmployeeList"; import { EmployeeService } from "./EmployeeServices"; import Button from "react-bootstrap/Button"; import { useDispatch, useSelector } from "react-redux"; import { trackRefreshCount } from "../../Redux/RefreshCount/slice"; import type { RootState, AppDispatch } from "../../Redux/store"; export const Employee: React.FC = () => { const getEmployeesList = EmployeeService(); const [empTblList, setEmpTblList] = useState<IEmployeeList[]>([]); const dispatch = useDispatch<AppDispatch>(); const { loader, trackRefreshCountSlice } = useSelector( (state: RootState) => state ); useEffect(() => { SetEmployeeList(); }, []); const SetEmployeeList = async () => { const data = await getEmployeesList(""); setEmpTblList(data); }; return ( <div> <Button disabled={loader.isLoading} variant="primary" onClick={() => { dispatch(trackRefreshCount()); SetEmployeeList(); }} > Refresh ({trackRefreshCountSlice.refreshCount}) </Button> <EmployeeList EmpTableList={empTblList} /> </div> ); };
EmployeeList.tsx
import { IEmployeeList } from "../../Models/Employee"; import Table from "react-bootstrap/Table"; import { useSelector } from "react-redux"; import SkeletonTable from "../../Component/Skeleton/SkeletonTable"; import type { RootState } from "../../Redux/store"; export const EmployeeList = (props: { EmpTableList: IEmployeeList[] }) => { const { EmpTableList } = props; const { loader } = useSelector((state: RootState) => state); return ( <div className="table-container"> <Table responsive> <thead> <tr> <th>Name</th> <th>Active</th> <th>Birthday</th> <th></th> </tr> </thead> <tbody> {loader.isLoading ? ( <SkeletonTable Column={3} Row={10} /> ) : ( EmpTableList.map((d: IEmployeeList, index: number) => ( <tr key={index}> <td>{d.name}</td> <td>{d.isActive ? "Active" : "Inactive"}</td> <td>{d.birthday}</td> <td> </td> </tr> )) )} </tbody> </Table> </div> ); };
关于Thunk的补充说明
你提到不想存储API响应到Redux所以未使用Thunk,但Thunk的核心是允许编写异步逻辑触发action,完全不需要将响应存入Redux。示例如下:
// 示例Thunk函数(不存储响应到Redux) import { createAsyncThunk } from "@reduxjs/toolkit"; import API from "../Helpers/HttpClient"; import { showLoader, hideLoader } from "./Loader/slice"; export const fetchEmployees = createAsyncThunk( "employees/fetch", async (_, { dispatch }) => { dispatch(showLoader()); try { const response = await API.get("uow", { headers: { custom_loader: "SHOW_LOADER" }, }); return response.data; // 返回数据给组件处理,不存入Redux } finally { dispatch(hideLoader()); } } );
在组件中使用:
const SetEmployeeList = async () => { const data = await dispatch(fetchEmployees()).unwrap(); setEmpTblList(data); };
这种方式也能实现加载状态管理,若你倾向于统一通过拦截器管理所有API的加载状态,全局Store dispatch方案更合适。
内容的提问来源于stack exchange,提问作者Mrunalsinh Jadeja
相关产品推荐
相关产品推荐

