React App Framework - 3. Redux

背景

接上篇, 我们来看看如何使用Redux来管理多个子应用的状态.

子应用状态管理

最初想过直接通过redux-compose通过key区分不同的app应用内容, 但是这样的隔离性较弱, 在全局或者其他子应用中都会存在无用的内容, 而且切换app的时候数据会存留在store中, 占用过多内存, 研究后发现可以自己开发各子应用隔离的store, 来实现各子系统的状态隔离, 同时加上全局store的事件订阅, 实现外层到内存的数据统一, 并且将外层的dispatch也附加到子应用的store中, 允许子应用调用全局的事件来修改状态, 实现子应用的状态隔离和全局的数据统一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// 子应用容器组件 - 加上redux
// sub-app.tsx

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const SubApp: React.FC<{
app: AllAppOptions;
menu: SubAppMenu;
translation: Translation;
AppIcon: React.FC;
reducers?: ReducersMapObject;
preloadedState?: any;
store?: Store<{}, Action>;
}> = ({ app, menu, children, reducers, preloadedState, store, translation, AppIcon }) => {
const globalStore = useStore<AppState, Action>();
const appStore = useMemo(
() =>
store ||
createStore(
createReducer(
combineReducers({
...reducers,
..._.mapValues(
globalStore.getState(),
(v) =>
(state: typeof v = v) =>
state,
),
}),
),
preloadedState,
composeEnhancers(
applyMiddleware(routerMiddleware(history), thunk.withExtraArgument(globalStore.dispatch)),
(createStore: any) =>
(...args: any[]) => {
const store = createStore(...args);
return {
...store,
globalDispatch: globalStore.dispatch,
};
},
),
),
[],
);
const unsubscribe = useMemo(
() =>
globalStore.subscribe(() => {
const globalState = globalStore.getState();
const state = appStore.getState();
_.find(globalState, (value, key) => state[key] !== value) && appStore.dispatch(changeGlobalState(globalState));
}),
[globalStore, appStore],
);
useEffect(() => unsubscribe, [unsubscribe]);
const [menuFoldState, setMenuFoldState] = useState(false);
const match = useMatch();
return (
<Provider store={appStore}>
<div className={classNames('sub-app flex-auto overflow-hidden flex-row position-relative', app)}>
<AppLeftMenu fold={menuFoldState} setFold={setMenuFoldState} menu={menu} app={app} match={match} AppIcon={AppIcon} />
<AppMain fold={menuFoldState} menu={menu} app={app} match={match}>
{children}
</AppMain>
</div>
</Provider>
);
};

useGlobalDispatch

同时开发一个useGlobalDispatch来简化全局dispatch的使用

1
2
3
4
5
6
7
8
9
import { AnyAction, Dispatch, Store } from 'redux';
import { useStore } from 'react-redux';

const useGlobalDispatch = (): Dispatch<AnyAction> => {
const store: Store<any, AnyAction> = useStore();
return (store as Store<any, AnyAction> & { globalDispatch: Dispatch<AnyAction> }).globalDispatch || store.dispatch;
};

export default useGlobalDispatch;

App

1
2
3
4
5
6
7
8
9
10
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(routerMiddleware(history), thunk)));

const App = () => {
return (
<Provider store={store}>
<Root history={history} />
</Provider>
);
};

小结

到此为止, 子应用的开发框架已经基本成型, 有独立的生命周期/动态加载和独立store, 不会互相影响也不会影响到基础框架, 后续的开发流程就与开发正常的react-app基本无异了

作者

Mosby

发布于

2019-09-15

许可协议

评论