11import { useContext } from 'react' ;
2+ import useForceUpdate from 'use-force-update' ;
23import { Reducers , State } from '../default' ;
3- import Dispatcher , { ExtractArguments } from '../types/dispatcher' ;
4+ import Dispatcher , {
5+ ExtractArguments ,
6+ PropertyDispatcher ,
7+ } from '../types/dispatcher' ;
48import Dispatchers from '../types/dispatchers' ;
59import Reducer , { PropertyReducer } from '../types/reducer' ;
610import Context from './context' ;
@@ -14,9 +18,16 @@ import REACT_HOOKS_ERROR from './utils/react-hooks-error';
1418export type UseDispatch <
1519 G extends { } = State ,
1620 R extends { } = Reducers ,
21+ P extends keyof G = keyof G ,
1722 K extends keyof R = keyof R ,
18- A extends any [ ] = any [ ]
19- > = Dispatcher < G , A > | Dispatcher < G , ExtractArguments < R [ K ] > > | Dispatchers < G , R > ;
23+ A extends any [ ] = any [ ] ,
24+ > =
25+ | Dispatcher < G , A >
26+ | Dispatcher < G , ExtractArguments < R [ K ] > >
27+ | Dispatchers < G , R >
28+ | PropertyDispatcher < G , P , A > ;
29+
30+ type VoidFunction = ( ) => void ;
2031
2132
2233
@@ -42,13 +53,13 @@ export default function _useDispatch<
4253export default function _useDispatch <
4354 G extends { } = State ,
4455 R extends { } = Reducers ,
45- A extends any [ ] = any [ ] ,
4656 P extends keyof G = keyof G ,
57+ A extends any [ ] = any [ ] ,
4758> (
4859 overrideGlobalStateManager : GlobalStateManager < G , R > | null ,
49- reducer : PropertyReducer < G , A , P > ,
60+ reducer : PropertyReducer < G , P , A > ,
5061 property : P ,
51- ) : Dispatcher < G , A > ;
62+ ) : PropertyDispatcher < G , P , A > ;
5263
5364// useDispatch('name')
5465export default function _useDispatch <
@@ -78,20 +89,22 @@ export default function _useDispatch<
7889export default function _useDispatch <
7990 G extends { } = State ,
8091 R extends { } = Reducers ,
92+ P extends keyof G = keyof G ,
8193 K extends keyof R = keyof R ,
8294 A extends any [ ] = any [ ] ,
83- P extends keyof G = keyof G ,
8495> (
8596 overrideGlobalStateManager : GlobalStateManager < G , R > | null ,
86- reducer ?: K | Reducer < G , R , A > | PropertyReducer < G , A , P > ,
97+ reducer ?: K | Reducer < G , R , A > | PropertyReducer < G , P , A > ,
8798 property ?: P ,
88- ) : UseDispatch < G , R , K , A > {
99+ ) : UseDispatch < G , R , P , K , A > {
89100
90101 // Require hooks.
91102 if ( ! useContext ) {
92103 throw REACT_HOOKS_ERROR ;
93104 }
94105
106+ const forceUpdate : VoidFunction = useForceUpdate ( ) ;
107+
95108 // Get the global state manager.
96109 const globalStateManager : GlobalStateManager < G , R > =
97110 overrideGlobalStateManager ||
@@ -109,6 +122,8 @@ export default function _useDispatch<
109122 // If this reducer mutates a specific property, create a dispatcher that
110123 // targets that property.
111124 if ( isPropertyReducer ( reducer , property ) ) {
125+
126+ // Create a middleware reducer specifically for handling this property.
112127 const newReducer : Reducer < G , R , A , Partial < G > > = (
113128 global : G ,
114129 _dispatch : Dispatchers < G , R > ,
@@ -118,7 +133,70 @@ export default function _useDispatch<
118133 newGlobalState [ property ] = reducer ( global [ property ] , ...args ) ;
119134 return newGlobalState ;
120135 } ;
121- return globalStateManager . createDispatcher ( newReducer ) ;
136+
137+ // Pre-emptively set dispatcher to type PropertyDispatcher so that we can
138+ // add the tuple to it.
139+ const propertyDispatcher : PropertyDispatcher < G , P , A > =
140+ (
141+ globalStateManager . createDispatcher ( newReducer , reducer . name )
142+ ) as PropertyDispatcher < G , P , A > ;
143+
144+ // [0] is the property value, with subscription.
145+ Object . defineProperty ( propertyDispatcher , 0 , {
146+ configurable : true ,
147+ enumerable : true ,
148+ get ( ) : G [ P ] {
149+ globalStateManager . addPropertyListener ( property , forceUpdate ) ;
150+ return globalStateManager . state [ property ] ;
151+ } ,
152+ } ) ;
153+
154+ // [1] is the dispatcher itself.
155+ propertyDispatcher [ 1 ] = propertyDispatcher ;
156+
157+ // Iterators must have a slice method.
158+ const propertyDispatcherSlice = (
159+ start ?: number ,
160+ end ?: number
161+ ) : ( G [ P ] | Dispatcher < G , A > ) [ ] => {
162+ const values : ( G [ P ] | Dispatcher < G , A > ) [ ] =
163+ [ propertyDispatcher [ 0 ] , propertyDispatcher [ 1 ] ] ;
164+ return values . slice . apply ( values , [ start , end ] ) ;
165+ } ;
166+ propertyDispatcher . slice = propertyDispatcherSlice ;
167+
168+ // Iterators must have a Symbol.iterator property.
169+ const propertyDispatcherIterator =
170+ ( ) : IterableIterator < G [ P ] | Dispatcher < G , A > > => {
171+ let index : number = 0 ;
172+ const propertyDispatcherIteratorNext =
173+ ( ) : IteratorResult < Dispatcher < G , A > | G [ P ] > => {
174+
175+ // IteratorResult
176+ if ( index < 2 ) {
177+ return {
178+ done : false ,
179+ value : propertyDispatcher [ index ++ ] ,
180+ } ;
181+ }
182+
183+ // Done.
184+ index = 0 ;
185+ return {
186+ done : true ,
187+ value : undefined ,
188+ } ;
189+ } ;
190+
191+ // IterableIterator
192+ return {
193+ [ Symbol . iterator ] : propertyDispatcher [ Symbol . iterator ] ,
194+ next : propertyDispatcherIteratorNext ,
195+ } ;
196+ } ;
197+ propertyDispatcher [ Symbol . iterator ] = propertyDispatcherIterator ;
198+
199+ return propertyDispatcher ;
122200 }
123201
124202 // If this reducer does not mutate a specific property, create it as is.
0 commit comments