Files
sentinel/static/app/utils/useCombinedReducer.tsx
2024-02-15 17:01:50 +00:00

54 lines
1.3 KiB
TypeScript

import type {Reducer, ReducerAction} from 'react';
import {useReducer} from 'react';
type ReducersObject<S = any, A = any> = {
[K in keyof S]: Reducer<S, A>;
};
type ReducersState<M> = M extends ReducersObject
? {
[P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never;
}
: never;
type ReducerFromReducersObject<M> = M extends {
[P in keyof M]: infer R;
}
? R extends Reducer<any, any>
? R
: never
: never;
type AllReducerAction<M> = M extends ReducersObject
? ReducerAction<ReducerFromReducersObject<M>>
: never;
type CombinedState<S> = {} & S;
export type CombinedReducer<M extends ReducersObject> = Reducer<
CombinedState<ReducersState<M>>,
AllReducerAction<M>
>;
export function makeCombinedReducers<M extends ReducersObject>(
reducers: M
): CombinedReducer<M> {
const keys: (keyof M)[] = Object.keys(reducers);
return (state, action) => {
const nextState = {} as ReducersState<M>;
for (const key of keys) {
nextState[key] = reducers[key](state[key], action);
}
return nextState;
};
}
export function useCombinedReducer<M extends ReducersObject>(
reducers: M,
initialState: CombinedState<ReducersState<M>>
): [CombinedState<ReducersState<M>>, React.Dispatch<AllReducerAction<M>>] {
return useReducer(makeCombinedReducers(reducers), initialState);
}