react-elmish 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,66 +25,82 @@ An elmish component basically consists of the following parts:
25
25
  First import everything from `react-elmish` and declare the **Message** discriminated union type:
26
26
 
27
27
  ```ts
28
- import * as Elm from "react-elmish";
28
+ import { Cmd, createCmd, UpdateReturnType, UpdateMap } from "react-elmish";
29
29
 
30
30
  export type Message =
31
- | { name: "Increment" }
32
- | { name: "Decrement" }
33
- ;
31
+ | { name: "increment" }
32
+ | { name: "decrement" };
34
33
  ```
35
34
 
36
- You can also create some convenience functions to dispatch a message:
35
+ You can also create some convenience functions to create message objects:
37
36
 
38
37
  ```ts
39
38
  export const Msg = {
40
- increment: (): Message => ({ name: "Increment" }),
41
- decrement: (): Message => ({ name: "Decrement" }),
39
+ increment: (): Message => ({ name: "increment" }),
40
+ decrement: (): Message => ({ name: "decrement" }),
42
41
  };
43
42
  ```
44
43
 
45
- Now we can create a `cmd` object for our messages type:
46
-
47
- ```ts
48
- const cmd = Elm.createCmd<Message>();
49
- ```
50
-
51
44
  Next, declare the model:
52
45
 
53
46
  ```ts
54
- export type Model = Readonly<{
47
+ export interface Model {
55
48
  value: number,
56
- }>;
49
+ }
57
50
  ```
58
51
 
59
52
  The props are optional:
60
53
 
61
54
  ```ts
62
- export type Props = {
55
+ export interface Props {
63
56
  initialValue: number,
64
- };
57
+ }
58
+ ```
59
+
60
+ Now we create a `cmd` object for our messages type:
61
+
62
+ ```ts
63
+ const cmd = createCmd<Message>();
65
64
  ```
66
65
 
67
66
  To create the initial model we need an **init** function:
68
67
 
69
68
  ```ts
70
- export const init = (props: Props): [Model, Elm.Cmd<Message>] => {
71
- const model: Model = {
72
- value: props.initialValue,
73
- };
69
+ export function init (props: Props): [Model, Cmd<Message>] {
70
+ return [
71
+ {
72
+ value: props.initialValue,
73
+ },
74
+ cmd.none
75
+ ];
76
+ };
77
+ ```
74
78
 
75
- return [model, cmd.none];
79
+ To update the model based on a message we need an `UpdateMap` object:
80
+
81
+ ```ts
82
+ export const update: UpdateMap<Props, Model, Message> = {
83
+ increment (msg, model, props) {
84
+ return [{ value: model.value + 1 }];
85
+ },
86
+
87
+ decrement (msg, model, props) {
88
+ return [{ value: model.value - 1 }];
89
+ },
76
90
  };
77
91
  ```
78
92
 
79
- To update the model based on a message we need an **update** function:
93
+ **Note:** When using an `UpdateMap` it is recommended to use camelCase for message names ("increment" instead of "Increment").
94
+
95
+ Alternatively we can use an **update** function:
80
96
 
81
97
  ```ts
82
- export const update = (model: Model, msg: Msg, props: Props): Elm.UpdateReturnType<Model, Message> => {
98
+ export const update = (model: Model, msg: Msg, props: Props): UpdateReturnType<Model, Message> => {
83
99
  switch (msg.name) {
84
- case "Increment":
100
+ case "increment":
85
101
  return [{ value: model.value + 1 }];
86
102
 
87
- case "Decrement":
103
+ case "decrement":
88
104
  return [{ value: model.value - 1 }];
89
105
  }
90
106
  };
@@ -96,25 +112,49 @@ export const update = (model: Model, msg: Msg, props: Props): Elm.UpdateReturnTy
96
112
 
97
113
  To put all this together and to render our component, we need a React component.
98
114
 
99
- This can be a **class component**:
115
+ As a **function component**:
116
+
117
+ ```tsx
118
+ // Import everything from the App.ts
119
+ import { init, update, Msg, Props } from "../App";
120
+ // Import the useElmish hook
121
+ import { useElmish } from "react-elmish";
122
+
123
+ function App (props: Props): JSX.Element {
124
+ // Call the useElmish hook, it returns the current model and the dispatch function
125
+ const [model, dispatch] = useElmish({ props, init, update, name: "App" });
126
+
127
+ return (
128
+ <div>
129
+ {/* Display our current value */}
130
+ <p>{model.value}</p>
131
+
132
+ {/* dispatch messages */}
133
+ <button onClick={() => dispatch(Msg.increment())}>Increment</button>
134
+ <button onClick={() => dispatch(Msg.decrement())}>Decrement</button>
135
+ </div>
136
+ );
137
+ }
138
+ ```
139
+
140
+ As a **class component**:
100
141
 
101
142
  ```tsx
102
143
  // Import everything from the App.ts
103
- import * as Shared from "../App";
144
+ import { Model, Message, Props, init, update, Msg } as Shared from "../App";
104
145
  // Import the ElmComponent which extends the React.Component
105
146
  import { ElmComponent } from "react-elmish";
106
- // Don't forget to import react
107
147
  import React from "react";
108
148
 
109
149
  // Create an elmish class component
110
- class App extends ElmComponent<Shared.Model, Shared.Message, Shared.Props> {
150
+ class App extends ElmComponent<Model, Message, Props> {
111
151
  // Construct the component with the props and init function
112
- constructor(props: Shared.Props) {
113
- super(props, Shared.init, "App");
152
+ constructor(props: Props) {
153
+ super(props, init, "App");
114
154
  }
115
155
 
116
156
  // Assign our update function to the component
117
- update = Shared.update;
157
+ update = update;
118
158
 
119
159
  render(): React.ReactNode {
120
160
  // Access the model
@@ -126,38 +166,13 @@ class App extends ElmComponent<Shared.Model, Shared.Message, Shared.Props> {
126
166
  <p>{value}</p>
127
167
 
128
168
  {/* Dispatch messages */}
129
- <button onClick={() => this.dispatch(Shared.Msg.increment())}>Increment</button>
130
- <button onClick={() => this.dispatch(Shared.Msg.decrement())}>Decrement</button>
169
+ <button onClick={() => this.dispatch(Msg.increment())}>Increment</button>
170
+ <button onClick={() => this.dispatch(Msg.decrement())}>Decrement</button>
131
171
  </div>
132
172
  );
133
173
  }
134
174
  ```
135
175
 
136
- Or it can be a **functional component**:
137
-
138
- ```tsx
139
- // Import everything from the App.ts
140
- import * as Shared from "../App";
141
- // Import the useElmish hook
142
- import { useElmish } from "react-elmish";
143
-
144
- const App = (props: Shared.Props) => {
145
- // Call the useElmish hook, it returns the current model and the dispatch function
146
- const [model, dispatch] = useElmish(props, Shared.init, Shared.update, "App");
147
-
148
- return (
149
- <div>
150
- {/* Display our current value */}
151
- <p>{model.value}</p>
152
-
153
- {/* dispatch messages */}
154
- <button onClick={() => dispatch(Shared.Msg.increment())}>Increment</button>
155
- <button onClick={() => dispatch(Shared.Msg.decrement())}>Decrement</button>
156
- </div>
157
- );
158
- };
159
- ```
160
-
161
176
  You can use these components like any other React component.
162
177
 
163
178
  > **Note**: It is recommended to separate business logic and the view into separate modules. Here we put the `Messages`, `Model`, `Props`, `init`, and `update` functions into **App.ts**. The elmish React Component resides in a **Components** subfolder and is named **App.tsx**.
@@ -172,11 +187,11 @@ Messages can also have arguments. You can modify the example above and pass an o
172
187
 
173
188
  ```ts
174
189
  export type Message =
175
- | { name: "Increment", step?: number }
190
+ | { name: "increment", step?: number }
176
191
  ...
177
192
 
178
193
  export const Msg = {
179
- increment: (step?: number): Message => ({ name: "Increment", step }),
194
+ increment: (step?: number): Message => ({ name: "increment", step }),
180
195
  ...
181
196
  }
182
197
  ```
@@ -185,7 +200,7 @@ Then use this argument in the **update** function:
185
200
 
186
201
  ```ts
187
202
  ...
188
- case "Increment":
203
+ case "increment":
189
204
  return [{ value: model.value + (msg.step ?? 1)}]
190
205
  ...
191
206
  ```
@@ -273,10 +288,11 @@ You can handle errors easily with the following pattern.
273
288
  1. Add an error message:
274
289
 
275
290
  ```ts
291
+ import { ErrorMessage, errorMsg, handleError } from "react-elmish";
292
+
276
293
  export type Message =
277
294
  | ...
278
- | { name: "Error", error: Error }
279
- ;
295
+ | ErrorMessage;
280
296
  ```
281
297
 
282
298
  1. Optionally add the convenient function to the **Msg** object:
@@ -284,7 +300,7 @@ You can handle errors easily with the following pattern.
284
300
  ```ts
285
301
  export const Msg = {
286
302
  ...
287
- error: (error: Error): Message => ({ name: "Error", error }),
303
+ ...errorMsg,
288
304
  }
289
305
  ```
290
306
 
@@ -292,16 +308,16 @@ You can handle errors easily with the following pattern.
292
308
 
293
309
  ```ts
294
310
  ...
295
- case "Error":
296
- return Elm.handleError(msg.error);
311
+ case "error":
312
+ return handleError(msg.error);
297
313
  ...
298
314
  ```
299
315
 
300
316
  The **handleError** function then calls your error handling middleware.
301
317
 
302
- ## Dispatch commands in the update function
318
+ ## Dispatch commands in the update map or update function
303
319
 
304
- In addition to modifying the model, you can dispatch new commands in the **update** function.
320
+ In addition to modifying the model, you can dispatch new commands here.
305
321
 
306
322
  To do so, you can call one of the functions in the `cmd` object:
307
323
 
@@ -324,12 +340,12 @@ Let's assume you have a message to display the description of the last called me
324
340
  ```ts
325
341
  export type Message =
326
342
  ...
327
- | { name: "PrintLastMessage", message: string }
343
+ | { name: "printLastMessage", message: string }
328
344
  ...
329
345
 
330
346
  export const Msg = {
331
347
  ...
332
- printLastMessage: (message: string): Message => ({ name: "PrintLastMessage", message }),
348
+ printLastMessage: (message: string): Message => ({ name: "printLastMessage", message }),
333
349
  ...
334
350
  }
335
351
  ```
@@ -337,11 +353,11 @@ export const Msg = {
337
353
  In the **update** function you can dispatch that message like this:
338
354
 
339
355
  ```ts
340
- case "Increment":
356
+ case "increment":
341
357
  return [{ value: model.value + 1 }, cmd.ofMsg(Msg.printLastMessage("Incremented by one"))];
342
358
  ```
343
359
 
344
- This new message will immediately be dispatched after returning from the **update** function.
360
+ This new message will immediately be dispatched after returning from the **update**.
345
361
 
346
362
  ### Call an async function
347
363
 
@@ -359,16 +375,16 @@ you can define the following messages:
359
375
  ```ts
360
376
  export type Messages =
361
377
  ...
362
- | { name: "LoadSettings" },
363
- | { name: "SettingsLoaded", settings: Settings }
364
- | { name: "Error", error: Error }
378
+ | { name: "loadSettings" },
379
+ | { name: "settingsLoaded", settings: Settings }
380
+ | ErrorMessage
365
381
  ...
366
382
 
367
383
  export const Msg = {
368
384
  ...
369
- loadSettings: (): Message => ({ name: "LoadSettings" }),
370
- settingsLoaded: (settings: Settings): Message => ({ name: "SettingsLoaded", settings }),
371
- error: (error: Error): Message => ({ name: "Error", error }),
385
+ loadSettings: (): Message => ({ name: "loadSettings" }),
386
+ settingsLoaded: (settings: Settings): Message => ({ name: "settingsLoaded", settings }),
387
+ ...errorMsg,
372
388
  ...
373
389
  };
374
390
  ```
@@ -377,18 +393,18 @@ and handle the messages in the **update** function:
377
393
 
378
394
  ```ts
379
395
  ...
380
- case "LoadSettings":
396
+ case "loadSettings":
381
397
  // Create a command out of the async function with the provided arguments
382
398
  // If loadSettings resolves it dispatches "SettingsLoaded"
383
399
  // If it fails it dispatches "Error"
384
400
  // The return type of loadSettings must fit Msg.settingsLoaded
385
401
  return [{}, cmd.ofPromise.either(loadSettings, Msg.settingsLoaded, Msg.error, "firstArg", 123)];
386
402
 
387
- case "SettingsLoaded":
403
+ case "settingsLoaded":
388
404
  return [{ settings: msg.settings }];
389
405
 
390
- case "Error":
391
- return Elm.handleError(msg.error);
406
+ case "error":
407
+ return handleError(msg.error);
392
408
  ...
393
409
  ```
394
410
 
@@ -420,34 +436,141 @@ In a functional component you can use the **useEffect** hook as normal.
420
436
 
421
437
  If you have some business logic that you want to reuse in other components, you can do this by using different sources for messages.
422
438
 
439
+ ### With an `UpdateMap`
440
+
423
441
  Let's say you want to load some settings, you can write a module like this:
424
442
 
425
443
  ```ts LoadSettings.ts
426
- import * as Elm from "react-elmish";
444
+ import { createCmd, Cmd, ErrorMessage, UpdateMap, handleError } from "react-elmish";
445
+
446
+ export interface Settings {
447
+ // ...
448
+ }
449
+
450
+ export type Message =
451
+ | { name: "loadSettings" }
452
+ | { name: "settingsLoaded", settings: Settings }
453
+ | ErrorMessage;
454
+
455
+ export const Msg = {
456
+ loadSettings: (): Message => ({ name: "loadSettings" }),
457
+ settingsLoaded: (settings: Settings): Message => ({ name: "settingsLoaded", settings }),
458
+ error: (error: Error): Message => ({ name: "error", error }),
459
+ };
460
+
461
+ const cmd = createCmd<Message>();
462
+
463
+ export interface Model {
464
+ settings: Settings | null,
465
+ }
466
+
467
+ export function init (): Model {
468
+ return {
469
+ settings: null
470
+ };
471
+ }
472
+
473
+ export const update: UpdateMap<Props, Model, Message> = {
474
+ loadSettings () {
475
+ return [{}, cmd.ofPromise.either(loadSettings, Msg.settingsLoaded, Msg.error)];
476
+ }
477
+
478
+ settingsLoaded ({ settings }) {
479
+ return [{ settings }];
480
+ }
481
+
482
+ error ({ error }) {
483
+ return handleError(error);
484
+ }
485
+ };
486
+
487
+ async function loadSettings (): Promise<Settings> {
488
+ // Call some service (e.g. database or backend)
489
+ return {};
490
+ }
491
+ ```
492
+
493
+ > **Note**: This module has no **View**.
494
+
495
+ Now let's integrate the **LoadSettings** module in our component:
496
+
497
+ ```ts Composition.ts
498
+ // Import the LoadSettings module
499
+ import * as LoadSettings from "./LoadSettings";
500
+ import { createCmd, Cmd, } from "react-elmish";
501
+
502
+ // Here we define our local messages
503
+ type Message =
504
+ | { name: "myMessage" }
505
+ | LoadSettings.Message;
506
+
507
+ // And spread the Msg of LoadSettings object
508
+ export const Msg = {
509
+ myMessage: (): Message => ({ name: "myMessage" }),
510
+ ...LoadSettings.Msg,
511
+ };
512
+
513
+ const cmd = Elm.createCmd<Message>();
514
+
515
+ interface Props {}
516
+
517
+ // Extend the LoadSettings model
518
+ interface Model extends LoadSettings.Model {
519
+ // ...
520
+ }
521
+
522
+ export const init = (): [Model, Cmd<Message>] => {
523
+ // Return the model and dispatch the LoadSettings message
524
+ return [
525
+ {
526
+ // Spread the initial model from LoadSettings
527
+ ...LoadSettings.init(),
528
+ // ...
529
+ },
530
+ cmd.ofMsg(Msg.loadSettings())
531
+ ];
532
+ };
533
+
534
+ // Spread the UpdateMap of LoadSettings into our update map
535
+ const update: UpdateMap<Props, Model, Message> = {
536
+ myMessage () {
537
+ return [{}];
538
+ },
539
+
540
+ ...LoadSettings.update,
541
+ };
542
+ ```
543
+
544
+ ## With an update function
545
+
546
+ Let's say you want to load some settings, you can write a module like this:
547
+
548
+ ```ts LoadSettings.ts
549
+ import { MsgSource, ErrorMessage, createCmd, UpdateReturnType, handleError } from "react-elmish";
427
550
 
428
551
  export type Settings = {
429
552
  // ...
430
553
  };
431
554
 
432
555
  // We use a MsgSource to differentiate between the messages
433
- type MessageSource = Elm.MsgSource<"LoadSettings">;
556
+ type MessageSource = MsgSource<"LoadSettings">;
434
557
 
435
558
  // Add that MessageSource to all the messages
436
559
  export type Message =
437
- | { name: "LoadSettings" } & MessageSource
438
- | { name: "SettingsLoaded", settings: Settings } & MessageSource
439
- | { name: "Error", error: Error } & MessageSource
560
+ | { name: "loadSettings" } & MessageSource
561
+ | { name: "settingsLoaded", settings: Settings } & MessageSource
562
+ | ErrorMessage & MessageSource
440
563
 
441
564
  // Do the same for the convenient functions
442
565
  const MsgSource: MessageSource = { source: "LoadSettings" };
443
566
 
444
567
  export const Msg = {
445
- loadSettings: (): Message => ({ name: "LoadSettings", ...MsgSource }),
446
- settingsLoaded: (settings: Settings): Message => ({ name: "SettingsLoaded", settings, ...MsgSource }),
447
- error: (error: Error): Message => ({ name: "Error", error, ...MsgSource }),
568
+ loadSettings: (): Message => ({ name: "loadSettings", ...MsgSource }),
569
+ settingsLoaded: (settings: Settings): Message => ({ name: "settingsLoaded", settings, ...MsgSource }),
570
+ error: (error: Error): Message => ({ name: "error", error, ...MsgSource }),
448
571
  };
449
572
 
450
- const cmd = Elm.createCmd<Message>();
573
+ const cmd = createCmd<Message>();
451
574
 
452
575
  export type Model = Readonly<{
453
576
  settings: Settings | null,
@@ -457,23 +580,23 @@ export const init = (): Model => ({
457
580
  settings: null,
458
581
  });
459
582
 
460
- export const update = (_model: Model, msg: Message): Elm.UpdateReturnType<Model, Message> => {
583
+ export const update = (_model: Model, msg: Message): UpdateReturnType<Model, Message> => {
461
584
  switch (msg.name) {
462
- case "LoadSettings":
585
+ case "loadSettings":
463
586
  return [{}, cmd.ofPromise.either(loadSettings, Msg.settingsLoaded, Msg.error)];
464
587
 
465
- case "SettingsLoaded":
588
+ case "settingsLoaded":
466
589
  return [{ settings: msg.settings }];
467
590
 
468
- case "Error":
469
- return Elm.handleError(msg.error);
591
+ case "error":
592
+ return handleError(msg.error);
470
593
  }
471
594
  };
472
595
 
473
- const loadSettings = async (): Promise<Settings> => {
596
+ async function loadSettings (): Promise<Settings> {
474
597
  // Call some service (e.g. database or backend)
475
- return Promise.resolve({});
476
- };
598
+ return {};
599
+ }
477
600
  ```
478
601
 
479
602
  > **Note**: This module has no **View**.
@@ -481,51 +604,51 @@ const loadSettings = async (): Promise<Settings> => {
481
604
  In other components where we want to use this **LoadSettings** module, we also need a message source:
482
605
 
483
606
  ```ts Composition.ts
484
- import * as Elm from "react-elmish";
607
+ import { createCmd, MsgSource, UpdateReturnType, Cmd } from "react-elmish";
485
608
  // Import the LoadSettings module
486
609
  import * as LoadSettings from "./LoadSettings";
487
610
 
488
611
  // Create a message source for this module
489
- type MessageSource = Elm.MsgSource<"Composition">;
612
+ type MessageSource = MsgSource<"Composition">;
490
613
 
491
614
  // Here we define our local messages
492
615
  // We don't need to export them
493
616
  type CompositionMessage =
494
- | { name: "MyMessage" } & MessageSource
495
- ;
617
+ | { name: "myMessage" } & MessageSource;
496
618
 
497
619
  // Combine the local messages and the ones from LoadSettings
498
620
  export type Message =
499
621
  | CompositionMessage
500
- | LoadSettings.Message
501
- ;
622
+ | LoadSettings.Message;
502
623
 
503
624
  const MsgSource: MessageSource = { source: "Composition" };
504
625
 
505
626
  export const Msg = {
506
- myMessage: (): Message => ({ name: "MyMessage", ...MsgSource }),
627
+ myMessage: (): Message => ({ name: "myMessage", ...MsgSource }),
628
+ ...LoadSettings.Msg,
507
629
  };
508
630
 
509
- const cmd = Elm.createCmd<Message>();
631
+ const cmd = createCmd<Message>();
510
632
 
511
633
  // Include the LoadSettings Model
512
- export type Model = Readonly<{
634
+ export interface Model extends LoadSettings.Model {
513
635
  // ...
514
- }> & LoadSettings.Model;
515
-
516
- export const init = (): [Model, Elm.Cmd<Message>] => {
517
- const model: Model = {
518
- // Spread the initial model from LoadSettings
519
- ...LoadSettings.init(),
520
- // ...
521
- };
636
+ }
522
637
 
638
+ export const init = (): [Model, Cmd<Message>] => {
523
639
  // Return the model and dispatch the LoadSettings message
524
- return [model, cmd.ofMsg(LoadSettings.Msg.loadSettings())];
640
+ return [
641
+ {
642
+ // Spread the initial model from LoadSettings
643
+ ...LoadSettings.init(),
644
+ // ...
645
+ },
646
+ cmd.ofMsg(Msg.loadSettings())
647
+ ];
525
648
  };
526
649
 
527
650
  // In our update function, we first distinguish between the sources of the messages
528
- export const update = (model: Model, msg: Message): Elm.UpdateReturnType<Model, Message> => {
651
+ export function update (model: Model, msg: Message): UpdateReturnType<Model, Message> {
529
652
  switch (msg.source) {
530
653
  case "Composition":
531
654
  // Then call the update function for the local messages
@@ -540,7 +663,7 @@ export const update = (model: Model, msg: Message): Elm.UpdateReturnType<Model,
540
663
  // For the msg parameter we use the local CompositionMessage type
541
664
  const updateComposition = (model: Model, msg: CompositionMessage): Elm.UpdateReturnType<Model, Message> => {
542
665
  switch (msg.name) {
543
- case "MyMessage":
666
+ case "myMessage":
544
667
  return [{}];
545
668
  }
546
669
  }
@@ -557,12 +680,12 @@ To inform the parent component about some action, let's say to close a dialog fo
557
680
  ```ts Dialog.ts
558
681
  export type Message =
559
682
  ...
560
- | { name: "Close" }
683
+ | { name: "close" }
561
684
  ...
562
685
 
563
686
  export const Msg = {
564
687
  ...
565
- close: (): Message => ({ name: "Close" }),
688
+ close: (): Message => ({ name: "close" }),
566
689
  ...
567
690
  }
568
691
  ```
@@ -579,8 +702,9 @@ To inform the parent component about some action, let's say to close a dialog fo
579
702
 
580
703
  ```ts Dialog.ts
581
704
  ...
582
- case "Close":
705
+ case "close":
583
706
  props.onClose();
707
+
584
708
  return [{}];
585
709
  ...
586
710
  ```
@@ -601,6 +725,7 @@ To test your **update** function you can use some helper functions in `react-elm
601
725
  | --- | --- |
602
726
  | `getOfMsgParams` | Extracts the messages out of a command |
603
727
  | `execCmd` | Executes the provided command and returns an array of all messages. |
728
+ | `getUpdateFn` | returns an `update` function for your update map object. |
604
729
 
605
730
  ### Testing the model and simple message commands
606
731
 
@@ -660,7 +785,20 @@ it("returns the correct cmd", () => {
660
785
  ...
661
786
  ```
662
787
 
663
- ## Migration from v1.x to 2.x
788
+ ### Testing with an UpdateMap
789
+
790
+ To test your update map, you can get an `update` function by calling `getUpdateFn`:
791
+
792
+ ```ts
793
+ import { getUpdateFn } from "react-elmish/dist/Testing";
794
+
795
+ const update = getUpdateFn(updateMap);
796
+
797
+ // in your test call update as usual
798
+ const [model, cmd] = update(msg, model, props);
799
+ ```
800
+
801
+ ## Migration from v1.x to v2.x
664
802
 
665
803
  * Use `Logger` and `Message` instead of `ILogger` and `IMessage`.
666
804
  * The global declaration of the `Nullable` type was removed, because it is unexpected for this library to declare such a type. You can declare this type for yourself if needed:
@@ -670,3 +808,16 @@ it("returns the correct cmd", () => {
670
808
  type Nullable<T> = T | null;
671
809
  }
672
810
  ```
811
+
812
+ ## Migration from v2.x to v3.x
813
+
814
+ The signature of `useElmish` has changed. It takes an options object now. Thus there is no need for the `useElmishMap` function. Use the new `useElmish` hook with an `UpdateMap` instead.
815
+
816
+ To use the old `useElmish` and `useElmishMap` functions, import them from the legacy namespace:
817
+
818
+ ```ts
819
+ import { useElmish } from "react-elmish/dist/legacy/useElmish";
820
+ import { useElmishMap } from "react-elmish/dist/legacy/useElmishMap";
821
+ ```
822
+
823
+ **Notice**: These functions are marked as deprecated and will be removed in a later release.
@@ -61,8 +61,9 @@ export declare abstract class ElmComponent<TModel, TMsg extends Message, TProps>
61
61
  * @abstract
62
62
  * @memberof ElmComponent
63
63
  */
64
- abstract update: (model: TModel, msg: TMsg, props: TProps) => UpdateReturnType<TModel, TMsg>;
64
+ abstract update: UpdateFunction<TProps, TModel, TMsg>;
65
65
  }
66
+ export declare type UpdateFunction<TProps, TModel, TMsg> = (model: TModel, msg: TMsg, props: TProps) => UpdateReturnType<TModel, TMsg>;
66
67
  /**
67
68
  * Type for the return value of the update function.
68
69
  */