You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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测试场景。

实现步骤

  1. 安装依赖
npm install msw @types/msw redux-mock-store @types/redux-mock-store -D
  1. 定义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))
  })
]
  1. 配置Mock服务,新建mocks/server.ts
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

export const server = setupServer(...handlers)
  1. 修改测试全局配置(如jest.setup.ts)
import { server } from './mocks/server'

// 所有测试启动前开启Mock服务
beforeAll(() => server.listen())
// 每个测试用例执行后重置Mock规则,避免用例间相互影响
afterEach(() => server.resetHandlers())
// 所有测试结束后关闭Mock服务
afterAll(() => server.close())
  1. 编写测试用例示例
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 14:45:06