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

如何通过Vite Plugin Federation共享Redux Toolkit Store?问题排查

微前端共享Redux Toolkit Store问题排查与方案探讨

问题概述

尝试通过Vite Plugin Federation实现微前端架构下Redux Toolkit Store的跨应用共享,已搭建host、remote、store三个独立项目并完成配置,但运行remote项目时抛出错误:

TypeError: Cannot destructure property 'store' of 'useReduxContext$1(...)' as it is null.

相关配置与代码

host项目 vite.config.js

import federation from '@originjs/vite-plugin-federation'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
    plugins: [
        react(),
        federation({
            name: 'app',
            remotes: {
                remoteApp: 'http://localhost:5001/assets/remoteEntry.js',
                store: 'http://localhost:5002/assets/remoteEntry.js',
            },
            shared: ['react', 'react-dom'],
        }),
    ],
    build: {
        modulePreload: false,
        target: 'esnext',
        minify: false,
        cssCodeSplit: false,
    },
})

remote项目 vite.config.js

import federation from '@originjs/vite-plugin-federation'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
    plugins: [
        react(),
        federation({
            name: 'remote_app',
            filename: 'remoteEntry.js',
            remotes: {
                store: 'http://localhost:5002/assets/remoteEntry.js',
            },
            exposes: {
                './Button': './src/Button',
            },
            shared: ['react', 'react-dom'],
        }),
    ],
    build: {
        modulePreload: false,
        target: 'esnext',
        minify: false,
        cssCodeSplit: false,
    },
})

store项目 vite.config.js

import federation from '@originjs/vite-plugin-federation'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
    plugins: [
        react(),
        federation({
            name: 'store',
            filename: 'remoteEntry.js',
            exposes: {
                './store': './src/store',
            },
            shared: ['react', 'react-dom'],
        }),
    ],
    build: {
        modulePreload: false,
        target: 'esnext',
        minify: false,
        cssCodeSplit: false,
    },
})

store项目 src/store.js

import { configureStore, createSlice } from '@reduxjs/toolkit'
import { Provider, useDispatch, useSelector } from 'react-redux'

export const counterSlice = createSlice({
    name: 'counter',
    initialState: {
        count: 0,
    },
    reducers: {
        increment: state => {
            state.count += 1
        },
        clear: state => {
            state.count = 0
        },
    },
})

const { increment, clear } = counterSlice.actions

const store = configureStore({
    reducer: {
        counter: counterSlice.reducer,
    },
})

export function useStore() {
    const count = useSelector(state => state.counter.count)
    const dispatch = useDispatch()
    return {
        count,
        increment: () => dispatch(increment()),
        clear: () => dispatch(clear()),
    }
}

// eslint-disable-next-line react/prop-types
export function StoreProvider({ children }) {
    return <Provider store={store}>{children}</Provider>
}

remote项目 src/main.jsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import { StoreProvider } from 'store/store'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <StoreProvider>
            <App />
        </StoreProvider>
    </React.StrictMode>
)

remote项目 src/Button.jsx

import { useStore } from 'store/store'
export const Button = () => {
    const { count, increment } = useStore()
    return (
        <div>
            <button className='shared-btn' onClick={increment}>
                Click me: {count}
            </button>
        </div>
    )
}

export default Button

完整错误信息

TypeError: Cannot destructure property 'store' of 'useReduxContext$1(...)' as it is null.
    at useSelector (__federation_expose_Store-6073bd77.js:1385:7)
    at useStore (__federation_expose_Store-6073bd77.js:1786:17)
    at Button (__federation_expose_Button-064de5a1.js:103:32)
    at Xh (__federation_shared_react-dom.js:202:137)
    at Wk (__federation_shared_react-dom.js:325:339)
    at Vk (__federation_shared_react-dom.js:315:393)
    at Uk (__federation_shared_react-dom.js:315:322)
    at Jk (__federation_shared_react-dom.js:315:181)
    at Ok (__federation_shared_react-dom.js:306:88)
    at Hk (__federation_shared_react-dom.js:303:432)

错误原因分析

核心问题是Redux上下文未在跨应用共享时正确互通:

  • useSelector和useDispatch钩子依赖Redux的Provider上下文,但当前三个项目的react-redux和@reduxjs/toolkit未配置为共享依赖,导致每个项目使用各自的库实例
  • remote项目中引入的useStore来自store项目的远程模块,其依赖的react-redux实例与remote本地的实例不一致,上下文无法传递,最终触发useReduxContext返回null的错误

方案可行性与修复步骤

可行性判断

该方案完全可行,仅需修正依赖共享配置即可实现跨应用Redux Store共享。

修复步骤

  1. 共享状态管理依赖:在三个项目的vite.config.js的shared配置中添加react-redux和@reduxjs/toolkit,确保所有应用使用同一库实例:
shared: ['react', 'react-dom', 'react-redux', '@reduxjs/toolkit']
  1. 验证上下文覆盖:确保remote项目的StoreProvider包裹整个应用(当前代码已满足)
  2. 确认单例Store:store项目中已导出单例store,无需额外修改

替代状态管理器与微前端状态共享方案

如果不想使用Redux Toolkit,可选择更适合微前端的轻量方案:

  • Zustand:无需Provider,直接导出全局store实例,跨应用引入即可共享状态
  • Jotai:原子化状态管理,通过导出原子对象实现跨应用共享
  • 自定义全局状态容器:基于EventEmitter实现状态变更通知,适合简单场景,无需引入第三方库

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:14:51