1- import { useCallback , useMemo , useRef , useState } from 'react' ;
1+ import type React from 'react' ;
2+ import {
3+ type ReducerAction ,
4+ type ReducerState ,
5+ useCallback ,
6+ useMemo ,
7+ useRef ,
8+ useState ,
9+ } from 'react' ;
210
311/**
412 * A hook that wraps a reducer to provide an observer pattern for the state.
@@ -10,20 +18,30 @@ import {useCallback, useMemo, useRef, useState} from 'react';
1018 */
1119
1220type ArgumentTypes < F extends Function > = F extends ( ...args : infer A ) => any ? A : never ;
13- interface Middlewares < S , A > {
14- [ 'before action' ] : ( S : Readonly < S > , A : A ) => void ;
15- [ 'before next state' ] : ( P : Readonly < S > , S : Readonly < S > , A : A ) => void ;
21+
22+ export interface DispatchingReducerMiddleware < R extends React . Reducer < any , any > > {
23+ [ 'before action' ] : ( S : Readonly < ReducerState < R > > , A : React . ReducerAction < R > ) => void ;
24+ [ 'before next state' ] : (
25+ P : Readonly < React . ReducerState < R > > ,
26+ S : Readonly < React . ReducerState < R > > ,
27+ A : React . ReducerAction < R >
28+ ) => void ;
1629}
1730
18- type MiddlewaresEvent < S , A > = { [ K in keyof Middlewares < S , A > ] : Set < Middlewares < S , A > [ K ] > } ;
31+ type MiddlewaresEvent < R extends React . Reducer < any , any > > = {
32+ [ K in keyof DispatchingReducerMiddleware < R > ] : Set < DispatchingReducerMiddleware < R > [ K ] > ;
33+ } ;
1934
20- class Emitter < S , A > {
21- listeners : MiddlewaresEvent < S , A > = {
22- 'before action' : new Set < Middlewares < S , A > [ 'before action' ] > ( ) ,
23- 'before next state' : new Set < Middlewares < S , A > [ 'before next state' ] > ( ) ,
35+ class Emitter < R extends React . Reducer < any , any > > {
36+ listeners : MiddlewaresEvent < R > = {
37+ 'before action' : new Set < DispatchingReducerMiddleware < R > [ 'before action' ] > ( ) ,
38+ 'before next state' : new Set < DispatchingReducerMiddleware < R > [ 'before next state' ] > ( ) ,
2439 } ;
2540
26- on ( key : keyof Middlewares < S , A > , fn : Middlewares < S , A > [ keyof Middlewares < S , A > ] ) {
41+ on (
42+ key : keyof DispatchingReducerMiddleware < R > ,
43+ fn : DispatchingReducerMiddleware < R > [ keyof DispatchingReducerMiddleware < R > ]
44+ ) {
2745 const store = this . listeners [ key ] ;
2846 if ( ! store ) {
2947 throw new Error ( `Unsupported reducer middleware: ${ key } ` ) ;
@@ -33,9 +51,9 @@ class Emitter<S, A> {
3351 store . add ( fn ) ;
3452 }
3553
36- removeListener (
37- key : keyof Middlewares < S , A > ,
38- listener : Middlewares < S , A > [ keyof Middlewares < S , A > ]
54+ off (
55+ key : keyof DispatchingReducerMiddleware < R > ,
56+ listener : DispatchingReducerMiddleware < R > [ keyof DispatchingReducerMiddleware < R > ]
3957 ) {
4058 const store = this . listeners [ key ] ;
4159 if ( ! store ) {
@@ -47,8 +65,8 @@ class Emitter<S, A> {
4765 }
4866
4967 emit (
50- key : keyof Middlewares < S , A > ,
51- ...args : ArgumentTypes < Middlewares < S , A > [ typeof key ] >
68+ key : keyof DispatchingReducerMiddleware < R > ,
69+ ...args : ArgumentTypes < DispatchingReducerMiddleware < R > [ typeof key ] >
5270 ) {
5371 const store = this . listeners [ key ] ;
5472 if ( ! store ) {
@@ -59,13 +77,15 @@ class Emitter<S, A> {
5977 }
6078}
6179
62- export function useDispatchingReducer < S , A > (
63- reducer : React . Reducer < S , A > ,
64- initialState : S ,
65- initializer ?: ( arg : S ) => S
66- ) : [ S , React . Dispatch < A > , Emitter < S , A > ] {
67- const emitter = useMemo ( ( ) => new Emitter < S , A > ( ) , [ ] ) ;
68- const [ state , setState ] = useState ( initialState ?? ( initializer ?.( initialState ) as S ) ) ;
80+ export function useDispatchingReducer < R extends React . Reducer < any , any > > (
81+ reducer : R ,
82+ initialState : ReducerState < R > ,
83+ initializer ?: ( arg : ReducerState < R > ) => ReducerState < R >
84+ ) : [ ReducerState < R > , React . Dispatch < ReducerAction < R > > , Emitter < R > ] {
85+ const emitter = useMemo ( ( ) => new Emitter < R > ( ) , [ ] ) ;
86+ const [ state , setState ] = useState (
87+ initialState ?? ( initializer ?.( initialState ) as ReducerState < R > )
88+ ) ;
6989
7090 const reducerRef = useRef ( reducer ) ;
7191 reducerRef . current = reducer ;
@@ -75,7 +95,7 @@ export function useDispatchingReducer<S, A>(
7595 stateRef . current = state ;
7696
7797 const wrappedDispatch = useCallback (
78- ( action : A ) => {
98+ ( action : ReducerAction < R > ) => {
7999 // @TODO it is possible for a dispatched action to throw an error
80100 // and break the reducer. We should probably catch it, I'm just not sure
81101 // what would be the best mechanism to handle it. If we opt to rethrow,
0 commit comments