useCombinedReducer.tsx 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. import {Reducer, ReducerAction, useReducer} from 'react';
  2. type ReducersObject<S = any, A = any> = {
  3. [K in keyof S]: Reducer<S, A>;
  4. };
  5. type ReducersState<M> = M extends ReducersObject
  6. ? {
  7. [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never;
  8. }
  9. : never;
  10. type ReducerFromReducersObject<M> = M extends {
  11. [P in keyof M]: infer R;
  12. }
  13. ? R extends Reducer<any, any>
  14. ? R
  15. : never
  16. : never;
  17. type AllReducerAction<M> = M extends ReducersObject
  18. ? ReducerAction<ReducerFromReducersObject<M>>
  19. : never;
  20. type CombinedState<S> = {} & S;
  21. export type CombinedReducer<M extends ReducersObject> = Reducer<
  22. CombinedState<ReducersState<M>>,
  23. AllReducerAction<M>
  24. >;
  25. export function makeCombinedReducers<M extends ReducersObject>(
  26. reducers: M
  27. ): CombinedReducer<M> {
  28. const keys: (keyof M)[] = Object.keys(reducers);
  29. return (state, action) => {
  30. const nextState = {} as ReducersState<M>;
  31. for (const key of keys) {
  32. nextState[key] = reducers[key](state[key], action);
  33. }
  34. return nextState;
  35. };
  36. }
  37. export function useCombinedReducer<M extends ReducersObject>(
  38. reducers: M,
  39. initialState: CombinedState<ReducersState<M>>
  40. ): [CombinedState<ReducersState<M>>, React.Dispatch<AllReducerAction<M>>] {
  41. return useReducer(makeCombinedReducers(reducers), initialState);
  42. }