# Preventing invalid states in update function

**URL:** <https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060>\
**Category:** Learn\
**Created:** [March 8, 2021, 1:58am UTC](https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060 "2021-03-08T01:58:22Z")\
**Posts on this page:** 4\
**Page:** 2

<div class="post-metadata">

**Author:** ![rupert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rupert/32/1775_2.png) [@rupert](https://discourse.elm-lang.org/u/rupert)\
**Post date:** [March 12, 2021, 12:32pm UTC](https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060/21 "2021-03-12T12:32:15Z")

</div>

> [@jfine2358](#):
>
> Having the view HTML return this version number as part of every message might help the `update` message to be appropriately suspicious. The `update` could treat the version number as a security token which can expire.

Its possible to do this, but I don’t think it will be at all helpful. In fact, that version number is capturing model state, since it is +1 from the previous model version and not a delta:

```auto
-- Somewhere in the view
SomeMsg (model.version + 1) -- captures model state!!

-- Should be a delta
SomeMsg 1 -- Here 1 just means bump the version by 1, its a delta, also pointless but just to illustrate.

```

An `update` function can receive `Msg`s from multiple sources - the `view`, the `subscription` or some `Cmd` it ran earlier that is going to produce a response, such as an HTTP request. It is actually perfectly ok for there to be many events in the queue awaiting processing by the `update` function, and those events can perfectly legally have been built upon earlier versions of the `Model`, without harm - so long as they are deltas and do not capture any absolute model state.

If you force a strict version numbering sequencing, it means that each event can only legally have been generated from the current-minus-one `Model` version.

Suppose you fired off an HTTP request some time ago, then the user clicked some buttons to filter or organize data in a table, and then the HTTP response comes back and you want to add more data into the table. Maybe that HTTP response was sent on the current-minus-ten version of the `Model`, but it is still valid to the current application state, so it should be all ok.

Better is to define the application as a state machine:

```elm
type Model = 
    | WorkingWithTheDataTable { records : List Record, tableFilter : Filter, whatever ... }
    | DoingSomethingElse

```

So there can simultaneously be events going on around how the table is viewed, and how its data is populated, but the state remains the same as these events are processed.

If you switched to the `DoingSomethingElse` state before the HTTP request for table data comes back - then that event can be ignored based on the application state.

Use a state machine to turn the real world event stream into a sufficiently ordered stream of allowed events. There is no need to totally order events in lock step, as that dissallows event orderings that will happen in the real world and that should be allowed by the application.

If you need to break down the `WorkingWithTheDataTable` state into a more fine grained state machine for working with table views - you can do that too, and nest its state machine inside the overall one for the application.

My recommendation is to always draw a pen-and-paper sketch of your application state machine, and iterate that sketch a few times till you get it right.

---

<div class="post-metadata">

**Author:** ![DullBananas](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dullbananas/32/3709_2.png) [@DullBananas](https://discourse.elm-lang.org/u/DullBananas)\
**Post date:** [March 18, 2021, 2:30am UTC](https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060/22 "2021-03-18T02:30:10Z")

</div>

In my counter+switch example i think it would be better to have a record with both the switch and counter states

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [March 20, 2021, 10:35am UTC](https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060/23 "2021-03-20T10:35:19Z")

</div>

I learned something the other day. For updates triggered by DOM events, stale updates can only happen for events that redraw on the next animation frame – not for events that redraw synchronously.

It all boils down to this part of the Kernel code:

> <https://github.com/elm/browser/blob/1d28cd625b3ce07be6dfad51660bea6de2c905f2/src/Elm/Kernel/Browser.js#L114-L134>

If `isSync` is true, it’s all fine. Then `draw` is called _immediately,_ which means that all event listeners are re-applied with a fresh `model` before any more events have a chance to fire. But if it’s `false`, `draw` isn’t called until the next animation frame so events can happen during that time with a now stale `model`. Mouse events fire very quickly so they are likely to fire multiple times before the next animation frame.

When is `isSync` set to true then? It’s – completely non-intuitively – based on if `stopPropagation` should be called on the event or not.

> <https://github.com/elm/virtual-dom/blob/5a5bcf48720bc7d53461b3cd42a9f19f119c5503/src/Elm/Kernel/VirtualDom.js#L655>

For example, [`onInput` uses `stopPropagation`](https://github.com/elm/html/blob/94c079007f8a7ed282d5b53f4a49101dd0b6cf99/src/Html/Events.elm#L115-L122) while [`onMouseEnter` doesn’t](https://github.com/elm/html/blob/94c079007f8a7ed282d5b53f4a49101dd0b6cf99/src/Html/Events.elm#L72-L74).

I updated some Ellie demos with a “sync vs animation frame” toggle to demonstrate that synchronous redraws don’t result in stale updates:

- Rupert’s mouse demo: [https://ellie-app.com/cGntbfDpys2a1](https://ellie-app.com/cGntbfDpys2a1)
- Coury Ditch’s typing demo: [https://ellie-app.com/cGntJQCdKfLa1](https://ellie-app.com/cGntJQCdKfLa1)

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [March 30, 2021, 10:35am UTC](https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060/24 "2021-03-30T10:35:33Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.

[Previous page](https://discourse.elm-lang.org/t/preventing-invalid-states-in-update-function/7060.md?page=1)
