RTK Query文档示例执行报错:Actions must be plain objects
错误日志
react-dom.development.js:22839 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
at Object.performAction (:1:31530)
at $ (:1:33343)
at Object.e (:1:37236)
at dispatch (:1:55003)
at buildHooks.ts:768:27
at commitHookEffectListMount (react-dom.development.js:23150:26)
at commitPassiveMountOnFiber (react-dom.development.js:24926:13)
at commitPassiveMountEffects_complete (react-dom.development.js:24891:9)
at commitPassiveMountEffects_begin (react-dom.development.js:24878:7)
at commitPassiveMountEffects (react-dom.development.js:24866:3)
相关代码片段
API端点定义
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/dist/query/react'; export const widgetConfigApi = createApi( { reducerPath: 'widgetconfig', baseQuery: fetchBaseQuery( { baseUrl: '/' } ), endpoints: ( builder ) => ( { widgetConfig: builder.query<any[], void>( { query: () => 'widgetconfig.json', } ), } ), } ); export const { useWidgetConfigQuery } = widgetConfigApi;
Store配置
const store = configureStore( { reducer: { widgets, [ widgetConfigApi.reducerPath ]: widgetConfigApi.reducer }, middleware: ( getDefaultMiddleware ) => getDefaultMiddleware().concat( widgetConfigApi.middleware ), enhancers: composeEnhancers, } );
组件使用Hook
import { Layout, theme } from 'antd'; import React, { useEffect, useState, lazy } from 'react'; import { v4 } from 'uuid'; import './assets/styles/main.css'; import lineChart from './components/mock/chart-data/lineChart'; import { sideMenuItems } from './components/mock/side-menu/items'; import SideMenu from './components/side-menu/SideMenu'; import { Widget } from '@babilon/babilon-ui-components'; import { useWidgetConfigQuery } from './redux/api/widgetApi'; function App () { const { Header, Content, Footer } = Layout; const { token: { colorBgLayout }, } = theme.useToken(); const [ widgetConfig, setWidgetConfig ] = useState( [] ); const { data, isLoading } = useWidgetConfigQuery(); useEffect( () => { console.log( data ); !isLoading && setWidgetConfig( data ); }, [] ); return ( <Layout style={ { height: '100vh' } }> <SideMenu items={ sideMenuItems } /> <Layout style={ { overflow: 'auto', position: 'relative' } }> <Header style={ { padding: 0, background: colorBgLayout } } /> <Content style={ { margin: '0 16px' } }> { widgetConfig.map( ( config: any ) => { const X = lazy( () => import( config.path ) ); const Y = lazy( () => import( config?.drawer.path ) ); return ( <React.Suspense key={ config.id }> <Widget styles={ { height: 350 } } className="h-350" { ...structuredClone( config ) } uuid={ v4() }> <X content series={ lineChart.series } /> </Widget> </React.Suspense> ); } ) } </Content> <Footer style={ { textAlign: 'center' } }>Ant Design ©2018 Created by Ant UED</Footer> </Layout> </Layout> ); } export default App;
环境与补充信息
- 项目基于Vite构建
- ApiProvider可正常工作,但默认Provider触发错误
- 在全新的Vite+React和Create React App环境中均能复现该问题
问题根源在于Store配置里的enhancers参数设置错误:你直接将composeEnhancers作为值传入,但configureStore要求enhancers是一个接收默认增强器数组的函数,或是增强器数组本身。
修复后的Store配置
// 假设你之前的composeEnhancers是通过Redux DevTools定义的 import { composeWithDevTools } from '@redux-devtools/extension'; const composeEnhancers = composeWithDevTools({}); const store = configureStore({ reducer: { widgets, [widgetConfigApi.reducerPath]: widgetConfigApi.reducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(widgetConfigApi.middleware), // 正确写法:将默认增强器传入composeEnhancers enhancers: (defaultEnhancers) => composeEnhancers(...defaultEnhancers), });
额外说明
- 若不需要自定义增强器,可直接删除
enhancers字段,configureStore会自动使用默认配置 - 必须保证RTK Query的中间件能被增强器正确包裹,否则无法处理异步action
- 原错误是因为直接传递
composeEnhancers函数而非调用它并传入默认增强器,导致Store无法识别RTK Query的异步action
内容的提问来源于stack exchange,提问作者Andy95

