11import { Cmd , Dispatch } from "./Cmd" ;
22import { dispatchMiddleware , LoggerService } from "./Init" ;
3- import { MessageBase , Nullable } from "./ElmUtilities" ;
3+ import { MessageBase , Nullable , UpdateMap } from "./ElmUtilities" ;
4+ import { UpdateFunction , UpdateReturnType } from "./ElmComponent" ;
45import { useCallback , useState } from "react" ;
5- import { UpdateFunction } from "./ElmComponent" ;
66
7- export function useElmish < TProps , TModel , TMsg extends MessageBase > ( props : TProps , init : ( props : TProps ) => [ TModel , Cmd < TMsg > ] , update : UpdateFunction < TProps , TModel , TMsg > , name : string ) : [ TModel , Dispatch < TMsg > ] {
7+ interface UseElmishOptions < TProps , TModel , TMessage extends MessageBase > {
8+ props : TProps ,
9+ init : ( props : TProps ) => [ TModel , Cmd < TMessage > ] ,
10+ update : UpdateFunction < TProps , TModel , TMessage > | UpdateMap < TProps , TModel , TMessage > ,
11+ name : string ,
12+ }
13+
14+ /**
15+ * Hook to use the Elm architecture pattern in a function component.
16+ * @param {UseElmishOptions } options The options passed the the hook.
17+ * @returns A tuple containing the current model and the dispatcher.
18+ * @example
19+ * const [model, dispatch] = useElmish({ props, init, update, name: "MyComponent" });
20+ */
21+ export function useElmish < TProps , TModel , TMessage extends MessageBase > ( { props, init, update, name } : UseElmishOptions < TProps , TModel , TMessage > ) : [ TModel , Dispatch < TMessage > ] {
822 let reentered = false ;
9- const buffer : TMsg [ ] = [ ] ;
23+ const buffer : TMessage [ ] = [ ] ;
1024 let currentModel : Partial < TModel > = { } ;
1125
1226 const [ model , setModel ] = useState < Nullable < TModel > > ( null ) ;
1327 let initializedModel = model ;
1428
15- const execCmd = useCallback ( ( cmd : Cmd < TMsg > ) : void => {
29+ const execCmd = useCallback ( ( cmd : Cmd < TMessage > ) : void => {
1630 cmd . forEach ( call => {
1731 try {
1832 call ( dispatch ) ;
@@ -22,7 +36,7 @@ export function useElmish<TProps, TModel, TMsg extends MessageBase> (props: TPro
2236 } ) ;
2337 } , [ ] ) ;
2438
25- const dispatch = useCallback ( ( msg : TMsg ) : void => {
39+ const dispatch = useCallback ( ( msg : TMessage ) : void => {
2640 if ( ! initializedModel ) {
2741 return ;
2842 }
@@ -38,15 +52,15 @@ export function useElmish<TProps, TModel, TMsg extends MessageBase> (props: TPro
3852 } else {
3953 reentered = true ;
4054
41- let nextMsg : TMsg | undefined = msg ;
55+ let nextMsg : TMessage | undefined = msg ;
4256 let modified = false ;
4357
4458 while ( nextMsg ) {
4559 LoggerService ?. info ( "Elm" , "message from" , name , nextMsg . name ) ;
4660 LoggerService ?. debug ( "Elm" , "message from" , name , nextMsg ) ;
4761
4862 try {
49- const [ newModel , cmd ] = update ( { ...initializedModel , ...currentModel } , nextMsg , props ) ;
63+ const [ newModel , cmd ] = callUpdate ( update , nextMsg , { ...initializedModel , ...currentModel } , props ) ;
5064
5165 if ( modelHasChanged ( newModel ) ) {
5266 currentModel = { ...currentModel , ...newModel } ;
@@ -87,4 +101,18 @@ export function useElmish<TProps, TModel, TMsg extends MessageBase> (props: TPro
87101 }
88102
89103 return [ initializedModel , dispatch ] ;
104+ }
105+
106+ export function callUpdate < TProps , TModel , TMessage extends MessageBase > ( update : UpdateFunction < TProps , TModel , TMessage > | UpdateMap < TProps , TModel , TMessage > , msg : TMessage , model : TModel , props : TProps ) : UpdateReturnType < TModel , TMessage > {
107+ if ( typeof update === "function" ) {
108+ return update ( model , msg , props ) ;
109+ }
110+
111+ return callUpdateMap ( update , msg , model , props ) ;
112+ }
113+
114+ export function callUpdateMap < TProps , TModel , TMessage extends MessageBase > ( updateMap : UpdateMap < TProps , TModel , TMessage > , msg : TMessage , model : TModel , props : TProps ) : UpdateReturnType < TModel , TMessage > {
115+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
116+ // @ts -expect-error -- We know that nextMsg fits
117+ return updateMap [ msg . name as TMessage [ "name" ] ] ( msg , model , props ) ;
90118}
0 commit comments