React Redux extraReducers action.payload unknown问题解决及测试mock方案
类型问题修复
你遇到的action.payload为unknown类型的问题,是因为没有给createAsyncThunk显式指定泛型参数,TS 无法自动推断请求成功/失败场景的返回值类型。同时你之前在rejected回调中错误从action.error取值,调用rejectWithValue后自定义错误内容会存放在action.payload中。
修改后的完整代码如下:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' import axios from 'axios' // 你的自定义类型,根据实际业务调整 type CampaignChart = { id: number name: string status: string } const BASE_URL = '你的接口地址前缀' // 定义请求失败时返回的错误结构 type FetchCampaignsError = { error: string } export type initialStateType = { campaignStatus: 'idle' | 'loading' | 'succeeded' | 'failed'; campaignList: CampaignChart[]; responseError: string | null; } const initialState: initialStateType = { campaignStatus: 'idle', campaignList: [], responseError: null, } // 给createAsyncThunk显式传入泛型参数 // 第一个参数:请求成功返回的payload类型 // 第二个参数:thunk的入参类型,无入参则为void // 第三个参数:rejectWithValue返回的错误类型 export const fetchCampaigns = createAsyncThunk< CampaignChart[], void, { rejectValue: FetchCampaignsError } >('campaigns/fetchCampaigns', async (_, thunkAPI) => { try { const response = await axios.get<CampaignChart[]>(BASE_URL + '/campaigns'); return response.data } catch(error: unknown){ // 对错误做类型窄化,符合TS类型检查要求 if (error instanceof Error) { return thunkAPI.rejectWithValue({ error: error.message }) } return thunkAPI.rejectWithValue({ error: '未知错误' }) } }) export const campaignSlice = createSlice({ name: 'campaign', initialState, reducers: { }, extraReducers(builder) { builder .addCase(fetchCampaigns.pending, (state) => { state.campaignStatus = 'loading' state.responseError = null }) .addCase(fetchCampaigns.fulfilled, (state, action) => { state.campaignStatus = 'succeeded' // 此时action.payload自动被推断为CampaignChart[]类型 state.campaignList = state.campaignList.concat(action.payload) }) .addCase(fetchCampaigns.rejected, (state, action) => { state.campaignStatus = 'failed' // 此时action.payload自动被推断为FetchCampaignsError类型 state.responseError = action.payload?.error ?? '未知错误' }) } });
接口Mock与单元测试实现
推荐使用 Mock Service Worker(msw)做接口Mock,该方案完全模拟真实请求逻辑,无需修改业务代码,适配react-testing-library测试场景。
实现步骤
- 安装依赖
npm install msw @types/msw redux-mock-store @types/redux-mock-store -D
- 定义Mock接口处理器,新建
mocks/handlers.ts
import { rest } from 'msw' import { BASE_URL } from '你的配置文件路径' import { CampaignChart } from '你的类型定义路径' // 模拟返回的测试数据,可根据业务场景自定义 export const mockCampaignList: CampaignChart[] = [ { id: 1, name: '活动1', status: 'active' }, { id: 2, name: '活动2', status: 'paused' } ] export const handlers = [ // 匹配GET活动列表接口,返回成功数据 rest.get(`${BASE_URL}/campaigns`, (req, res, ctx) => { return res(ctx.status(200), ctx.json(mockCampaignList)) }) ]
- 配置Mock服务,新建
mocks/server.ts
import { setupServer } from 'msw/node' import { handlers } from './handlers' export const server = setupServer(...handlers)
- 修改测试全局配置(如
jest.setup.ts)
import { server } from './mocks/server' // 所有测试启动前开启Mock服务 beforeAll(() => server.listen()) // 每个测试用例执行后重置Mock规则,避免用例间相互影响 afterEach(() => server.resetHandlers()) // 所有测试结束后关闭Mock服务 afterAll(() => server.close())
- 编写测试用例示例
import { render, screen, waitFor } from '@testing-library/react' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' import thunk from 'redux-thunk' import CampaignList from '你的组件路径' import { mockCampaignList } from 'mocks/handlers' import { BASE_URL } from '你的配置文件路径' import { rest } from 'msw' import { server } from 'mocks/server' const middlewares = [thunk] const mockStore = configureStore(middlewares) describe('活动列表组件', () => { it('请求成功时正常渲染活动列表', async () => { const store = mockStore({ campaign: { campaignStatus: 'idle', campaignList: [], responseError: null } }) render( <Provider store={store}> <CampaignList /> </Provider> ) // 验证加载状态 expect(screen.getByText(/加载中/i)).toBeInTheDocument() // 等待请求完成,验证列表渲染 await waitFor(() => { mockCampaignList.forEach(item => { expect(screen.getByText(item.name)).toBeInTheDocument() }) }) }) it('请求失败时正常展示错误提示', async () => { // 临时覆盖Mock规则,返回错误 server.use( rest.get(`${BASE_URL}/campaigns`, (req, res, ctx) => { return res(ctx.status(500), ctx.json({ message: '服务异常' })) }) ) const store = mockStore({ campaign: { campaignStatus: 'idle', campaignList: [], responseError: null } }) render( <Provider store={store}> <CampaignList /> </Provider> ) // 验证错误提示展示 await waitFor(() => { expect(screen.getByText(/服务异常/i)).toBeInTheDocument() }) }) })
内容的提问来源于stack exchange,提问作者nonpointed
相关产品推荐
相关产品推荐

