useCombinedReducer.tsx 1.3 KB

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