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

使用Rollup打包RTKQ库时出现react-redux Provider上下文缺失错误

问题排查:RTKQ库打包后出现「could not find react-redux context value」错误

错误信息

运行时抛出错误:

could not find react-redux context value; please ensure the component is wrapped in a <Provider>

该错误仅在库通过Rollup打包后出现,源码直接使用或未打包发布时正常工作。

涉事库情况

基于RTKQ封装的API集合,导出apiSlice供消费者集成到自身Redux Store中。

Rollup配置

const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const peerDepsExternal = require('rollup-plugin-peer-deps-external');
const babel = require('@rollup/plugin-babel');

const packageJson = require('./package.json');

module.exports = {
  input: 'src/index.js',
  external: [/@babel\/runtime/],
  output: [
    {
      file: packageJson.module,
      format: 'es',
      sourcemap: true,
    }
  ],
  plugins: [
    peerDepsExternal(),
    resolve(),
    babel({
      exclude: 'node_modules/**',
      babelHelpers: 'runtime',
    }),
    commonjs({ 
      strictRequires: true,
    }),
  ],
};

package.json

{
  "name": "some-name",
  "version": "1.1.2",
  "main": "dist/esm/index.js",
  "files": [
    "dist"
  ],
  "dependencies": {
    "@babel/runtime": "^7.20.6",
    "@faker-js/faker": "^7.6.0",
    "@reduxjs/toolkit": "^1.8.0",
    "luxon": "^3.1.0",
    "np": "^7.6.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-redux": "^8.0.2",
    "reactjs-localstorage": "^1.0.1"
  },
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint --fix .",
    "prepare": "npm run co:login",
    "prepublishOnly": "npm run build && np --no-publish --no-yarn --no-cleanup",
    "build": "rollup --config",
    "start": "webpack serve",
    "devBuild": "webpack --mode=development",
    "test": "echo \"No tests yet\""
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@babel/core": "^7.20.5",
    "@babel/plugin-transform-runtime": "^7.19.6",
    "@babel/preset-env": "^7.20.2",
    "@rollup/plugin-babel": "^6.0.3",
    "@rollup/plugin-commonjs": "^24.0.0",
    "@rollup/plugin-node-resolve": "^15.0.1",
    "eslint": "^8.17.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.30.0",
    "eslint-plugin-storybook": "^0.5.12",
    "rollup": "^3.7.5",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "webpack": "^5.70.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  }
}

.babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": ["@babel/plugin-transform-runtime"]
}

库核心逻辑:通过RTKQ的createApi()创建apiSlice,导出该切片及所有定义的API端点。

消费者情况

消费者为使用Storybook测试的组件库,其preview.js核心代码如下:

import {
  createTheme,
  CssBaseline,
  responsiveFontSizes,
  ThemeProvider,
} from '@mui/material';
import { SnackbarProvider } from 'notistack';

import { MemoryRouter } from 'react-router';

import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import { apiSlice} from '@my_company/the_lib_with_the_error';
import { useDispatch } from 'react-redux';
import { useEffect } from 'react';

import { initialize as initializeMSW, mswDecorator } from 'msw-storybook-addon';
import { ConfirmProvider } from 'material-ui-confirm';

// 测试用Store
const store = configureStore({
  reducer: {
    [apiSlice.reducerPath]: apiSlice.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(apiSlice.middleware),
  devTools: { trace: true },
});

let theme = createTheme({/** 主题配置 */});
theme = responsiveFontSizes(theme);

initializeMSW({ waitUntilReady: false });

const SetupApi = () => {
  const dispatch = useDispatch();
  useEffect(() => {
      dispatch(apiSlice.util.resetApiState());
  }, [dispatch]);
  
  return null;
}

const stdLayout = (Story) => {
  store.dispatch(apiSlice.util.resetApiState());

  return (
    <>
      <Provider store={store}>
        <ThemeProvider theme={theme}>
          <SnackbarProvider
            maxSnack={3}
            autoHideDuration={3000}
            anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
          >
            <ConfirmProvider>
              <>
                <CssBaseline />
                <SetupApi />
                <MemoryRouter initialEntries={['/']}>
                  <Story />
                </MemoryRouter>
              </>
            </ConfirmProvider>
          </SnackbarProvider>
        </ThemeProvider>
      </Provider>
    </>
  );
};

解决思路

1. 修复依赖重复问题(核心)

当前库的dependencies中包含react、react-redux、@reduxjs/toolkit,这会导致打包时这些依赖被打包进库文件,消费者安装后会出现两份依赖实例:一份来自库的打包产物,一份来自消费者自身的node_modules。React-Redux的上下文是基于实例的,两份实例无法共享上下文,从而抛出找不到Provider的错误。

解决方案:

  • 将react、react-dom、react-redux、@reduxjs/toolkit移到peerDependencies中,明确要求消费者提供这些依赖:
    "peerDependencies": {
      "@reduxjs/toolkit": "^1.8.0",
      "react": "^18.2.0",
      "react-dom": "^18.2.0",
      "react-redux": "^8.0.2"
    },
    "dependencies": {
      "@babel/runtime": "^7.20.6",
      "@faker-js/faker": "^7.6.0",
      "luxon": "^3.1.0",
      "reactjs-localstorage": "^1.0.1"
      // 移除上述四个peer依赖
    }
    
  • 将np移到devDependencies中(它是发布工具,不需要打包进产物)。

2. 确保Rollup正确排除Peer依赖

rollup-plugin-peer-deps-external插件会自动识别peerDependencies并将其排除在打包之外,无需手动在external字段添加。检查打包后的产物,确认没有包含react-redux或@reduxjs/toolkit的代码。

3. 清理消费者端依赖缓存

消费者执行以下操作,确保依赖实例唯一:

  • 删除node_modules文件夹
  • 删除package-lock.json(或yarn.lock)
  • 重新执行npm install

4. 验证组件渲染时机

确保Storybook中所有使用RTKQ Hooks(如useQuery、useMutation)的组件,都在<Provider>的包裹范围内。避免在组件顶层(渲染前)调用需要上下文的方法,所有RTKQ相关逻辑应放到组件内部或useEffect中执行。

5. 检查Babel配置

当前Babel仅配置了@babel/preset-env,如果库中使用了React语法(如JSX),需添加@babel/preset-react:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime"]
}

并在devDependencies中安装该预设:

npm install @babel/preset-react --save-dev

内容的提问来源于stack exchange,提问作者zetain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 23:15:29