如何在single-spa中使用RxJs共享UI状态?求React场景示例
React + single-spa + RxJS 状态共享完整示例
我刚好在React + single-spa的项目里实践过用RxJS做跨应用状态共享,给你整理了一个覆盖所有场景的基础示例,核心思路是用全局可导入的RxJS状态服务,借助BehaviorSubject(它能保存当前状态值,新订阅者也能拿到最新状态)实现双向/多向的状态同步。
1. 创建共享状态服务
先新建一个shared-state.js(这个文件要放在所有应用都能访问到的位置,比如作为single-spa的共享模块,或者打包成独立UMD包供全局导入):
import { BehaviorSubject } from 'rxjs'; // 定义独立的状态流,按业务域拆分更清晰 const userSubject = new BehaviorSubject(null); const themeSubject = new BehaviorSubject('light'); // 暴露状态操作API export const sharedState = { // 用户信息状态操作 setUser: (user) => userSubject.next(user), getUser: () => userSubject.getValue(), subscribeToUser: (callback) => userSubject.subscribe(callback), // 主题状态操作 setTheme: (theme) => themeSubject.next(theme), getTheme: () => themeSubject.getValue(), subscribeToTheme: (callback) => themeSubject.subscribe(callback), // 通用订阅清理方法,方便React组件卸载时清理 unsubscribe: (subscription) => subscription.unsubscribe() };
2. 根应用 → 微应用 共享状态
根应用更新状态
比如根应用导航栏的主题切换按钮,点击时更新共享状态:
// 根应用 Navbar 组件 import { sharedState } from './shared-state'; function Navbar() { const toggleTheme = () => { const currentTheme = sharedState.getTheme(); sharedState.setTheme(currentTheme === 'light' ? 'dark' : 'light'); }; return ( <nav className="navbar"> <button onClick={toggleTheme}>切换主题</button> </nav> ); }
微应用订阅状态
微应用的任意组件可以订阅主题状态,实时同步UI:
// 微应用 Profile 组件 import { useEffect, useState } from 'react'; import { sharedState } from '../shared-state'; // 按实际路径导入 function Profile() { // 初始化时拿到当前状态 const [theme, setTheme] = useState(sharedState.getTheme()); useEffect(() => { // 订阅主题变化,更新本地状态 const subscription = sharedState.subscribeToTheme((newTheme) => { setTheme(newTheme); }); // 组件卸载时清理订阅,避免内存泄漏 return () => sharedState.unsubscribe(subscription); }, []); return ( <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff', padding: '1rem' }}> <h2>用户资料页</h2> <p>当前主题:{theme}</p> </div> ); }
3. 微应用 → 根应用 共享状态
微应用更新状态
比如微应用的登录表单,登录成功后更新共享的用户信息:
// 微应用 Login 组件 import { useState } from 'react'; import { sharedState } from '../shared-state'; function Login() { const [username, setUsername] = useState(''); const handleLogin = () => { // 模拟登录接口返回的用户信息 const userInfo = { id: 1001, name: username, avatar: 'default-avatar.png' }; sharedState.setUser(userInfo); }; return ( <div className="login-form"> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="输入用户名" /> <button onClick={handleLogin}>登录</button> </div> ); }
根应用订阅状态
根应用导航栏订阅用户状态,动态显示登录信息:
// 根应用 Navbar 组件 import { useEffect, useState } from 'react'; import { sharedState } from './shared-state'; function Navbar() { const [user, setUser] = useState(sharedState.getUser()); useEffect(() => { const subscription = sharedState.subscribeToUser((newUser) => { setUser(newUser); }); return () => sharedState.unsubscribe(subscription); }, []); return ( <nav className="navbar"> {user ? ( <span className="user-info">欢迎回来,{user.name}</span> ) : ( <span className="unlogin">未登录</span> )} <button onClick={toggleTheme}>切换主题</button> </nav> ); }
4. 微应用之间 共享状态
逻辑和上面一致,比如微应用A修改用户信息,微应用B实时同步:
微应用A(订单应用)更新状态
// 微应用A UserSettings 组件 import { sharedState } from '../shared-state'; function UserSettings() { const updateUserName = () => { const currentUser = sharedState.getUser(); // 更新用户名,保留其他字段 sharedState.setUser({ ...currentUser, name: '新昵称' }); }; return ( <button onClick={updateUserName}>修改昵称</button> ); }
微应用B(消息应用)订阅状态
// 微应用B MessageCenter 组件 import { useEffect, useState } from 'react'; import { sharedState } from '../shared-state'; function MessageCenter() { const [user, setUser] = useState(sharedState.getUser()); useEffect(() => { const subscription = sharedState.subscribeToUser((newUser) => { setUser(newUser); }); return () => sharedState.unsubscribe(subscription); }, []); return ( <div className="message-center"> <p>您好,{user?.name},您有3条未读消息</p> </div> ); }
关键注意事项
- 确保所有应用能正确导入
shared-state.js:如果用single-spa的模块联邦,要配置好依赖共享;如果全局导入,要保证该文件在所有应用加载前完成加载。 - React组件中订阅时,一定要在
useEffect的清理函数里取消订阅,避免内存泄漏。 - 复杂场景下建议拆分多个
BehaviorSubject,每个对应一个独立业务域,避免状态混乱。
内容的提问来源于stack exchange,提问作者Saswata Arabinda
相关产品推荐
相关产品推荐

