如何在不同文件的redux-observable epic之间共享变量?
有三种常见的实现方案,可按你的业务场景选择:
方案1:抽离共享变量到公共模块(适合纯客户端场景)
把共享变量单独放到一个公共模块文件,两个epic分别引入该模块读写即可:
- 新建
shared/index.js存储共享变量
// shared/index.js export let sharedVariable = null // 封装赋值方法避免直接篡改 export const setSharedVariable = (val) => { if (!sharedVariable) sharedVariable = val }
epics1.js中引入赋值
import { setSharedVariable } from './shared' import { A } from 'xxx' // 其他引入省略 const epic1 = (action$, state$) => { return action$.pipe( filter(action1.match), tap(() => setSharedVariable(new A())), mergeMap((action) => { // 原有逻辑 }) ) } export default epic1
epics2.js中引入读取
import { sharedVariable } from './shared' // 其他引入省略 const epic2 = (action$, state$) => { return action$.pipe( filter(action2.match), map(() => sharedVariable ? action3() : action4()) ) } export default epic2
注意:该方案在Next.js SSR/SSG场景下会出现状态污染,Node.js服务端的模块是单例缓存的,多个用户请求会共享同一个变量,因此仅适合纯客户端运行的场景。
方案2:存储到Redux Store(最推荐,适配全场景)
符合Redux单向数据流设计,状态可追溯,天然支持SSR下每个请求独立store的特性,不会出现状态污染:
- 新增shared状态切片
// store/slices/sharedSlice.js import { createSlice } from '@reduxjs/toolkit' const initialState = { aInstance: null } const sharedSlice = createSlice({ name: 'shared', initialState, reducers: { setAInstance: (state, action) => { if (!state.aInstance) state.aInstance = action.payload } } }) export const { setAInstance } = sharedSlice.actions export const selectAInstance = state => state.shared.aInstance export default sharedSlice.reducer
- 修改
epics1.js赋值逻辑
import { setAInstance } from 'store/slices/sharedSlice' import { A } from 'xxx' const epic1 = (action$, state$) => { return action$.pipe( filter(action1.match), mergeMap(() => { const instance = new A() // 先派发action把实例存入store return of(setAInstance(instance)).pipe( // 后续原有逻辑 ) }) ) }
- 修改
epics2.js读取逻辑
import { selectAInstance } from 'store/slices/sharedSlice' const epic2 = (action$, state$) => { return action$.pipe( filter(action2.match), map(() => { const instance = selectAInstance(state$.value) return instance ? action3() : action4() }) ) }
方案3:使用redux-observable依赖注入(适合需要解耦测试的场景)
利用redux-observable内置的依赖注入能力,把共享变量作为全局依赖传入所有epic,方便单元测试时替换mock依赖:
- 创建epic中间件时传入依赖
// store/epic.js import { createEpicMiddleware, combineEpics } from 'redux-observable' import epic1 from './epics1' import epic2 from './epics2' const rootEpic = combineEpics(epic1, epic2) export const epicMiddleware = createEpicMiddleware({ dependencies: { // 用对象包裹方便引用修改,直接传基础类型无法修改 shared: { value: null } } }) epicMiddleware.run(rootEpic)
epics1.js从第三个参数拿依赖赋值
const epic1 = (action$, state$, deps) => { return action$.pipe( filter(action1.match), tap(() => { if (!deps.shared.value) deps.shared.value = new A() }), mergeMap((action) => { // 原有逻辑 }) ) }
epics2.js从第三个参数拿依赖读取
const epic2 = (action$, state$, deps) => { return action$.pipe( filter(action2.match), map(() => deps.shared.value ? action3() : action4()) ) }
注意:该方案同样存在SSR下状态污染问题,仅适合纯客户端场景使用。
内容的提问来源于stack exchange,提问作者HsnVahedi
相关产品推荐
相关产品推荐

