This topic is a bit all over the place, but just wanted to chime in with a
different approach that I've found works pretty well for updating
database-backed state, especially in contexts where you aren't syncing with
the backend via websockets or similar.
That is to follow the principle: no database-backed state gets updated
directly in the frontend app. Updates are made via *commands *to the
backend (like an extension of Msgs to the backend, essentially). New state
is always loaded from the backend, not from a cache (unless you need that
and can deal with cache invalidation cleanly), preserving the
unidirectional flow of state. In this way, you can minimize the need for a
'store', and never need to directly update data in the 'store' based on
user input. I think this may be related to what Kasey was saying
originally.
Eric
On Sunday, April 30, 2017 at 3:31:49 PM UTC-4, Oliver Searle-Barnes wrote:
>
> The pattern we use is to have our Page.update functions return
>
> (model, cmd, storeCmd)
>
> the main update then applies the storeCmd to the Store. (the actual code
> supports a few other things but that's the basic gist of it). Hit me up on
> slack if you want to chat about it.
>
>
>
> On Sunday, 30 April 2017 08:45:49 UTC+2, Dustin Farris wrote:
>>
>> I think I've just had an aha moment with this post.
>>
>> I am in the process of refactoring my monolith MUV into separate modules
>> with their own MUV for each "page" of my SPA. Up to this point, I have had
>> a separate Store module with its own Model and Msg types and an update
>> function (no view, obviously). This has worked well up until now, but
>> after splitting off the pages of my app, it is getting more cumbersome to
>> update the Store in a way that looks nice.
>>
>> e.g. in my Main.elm I'm ending up with something like
>>
>> update msg model =
>> case msg of
>> UserProfilePageMsg msg_ ->
>> let
>> ( userProfilePageModel, userProfilePageCmd ) =
>> UserProfilePage.update msg_ model.userProfilePage
>> in
>> case msg_ of
>> UserProfilePage.StoreMsg msg__ ->
>> let
>> ( storeModel, storeCmd ) =
>> Store.update msg__ model.store
>> in
>> { model
>> | userProfilePage = userProfilePageModel
>> , store = storeModel
>> }
>> ! [ Cmd.map UserProfilePageMsg
>> userProfilePageCmd
>> , Cmd.map StoreMsg storeCmd
>> ]
>> _ ->
>> { model | userProfilePage = userProfilePageModel
>> }
>> ! [ Cmd.map UserProfilePageMsg
>> userProfilePageCmd ]
>>
>>
>> and so on for every page that invokes Store.Msg—which is most pages.
>>
>> I am thinking that there is a better way, and perhaps Kasey's suggestion
>> of forgoing an in-memory Store on the Model might be it. I'm still not
>> sure—I do like the snappy feel of a page loading instantly if the data is
>> in memory—even if it might change after a brief consultation with the
>> server.
>>
>> Dustin
>>
>>
>> On Wednesday, April 19, 2017 at 7:28:06 PM UTC-4, Kasey Speakman wrote:
>>>
>>> I'm probably slow, but in recent months I've discovered that trying to
>>> use Elm's Model like a database or cache (as I have previously seen
>>> suggested) has turned out to be pretty painful for me. An example
>>> database-minded model where a section could display *either* a list of
>>> employees *or* a list of courses.
>>>
>>> type alias Model =
>>> { employees : List Employee
>>> , courses : List Course
>>> , loadingError : Maybe Http.Error
>>> , route : MyRoute -- employee or course
>>> }
>>>
>>> The problem this runs into is having to worry about state management. I
>>> have to remember to "reset" or "turn off" things when they are not active.
>>> As my application grew, I had a lot of problems that boiled down to tedious
>>> state management details. My cached data didn't turn out to be all that
>>> useful because I usually had to reload it anyway, in case something changed.
>>>
>>> Instead, I have been moving toward the model only representing the
>>> current state of my UI. The big difference here is the model representing
>>> the current *visual* elements and their data. This leads more to using
>>> union types to represent parts of the UI. When you switch to a different
>>> case of the union type, the data from the previous case is *dropped on
>>> the floor*. This leaves nothing to remember to "reset". RemoteData is a
>>> good micro-example of this. If there was an error fetching the data, when
>>> the user requests the data again, you switch back to Loading, the error
>>> message is dropped on the floor. No forgetting to hide it.
>>>
>>> type RemoteData e a
>>> = NotAsked
>>> | Loading
>>> | Failure e
>>> | Success a
>>>
>>> If it is really important to cache the data, I prefer to keep that as a
>>> persistence concern, not on Model. It can be part of the process for
>>> retrieving the data to first check my chosen cache before making a request
>>> for fresh data. For instance, first check local storage before making an
>>> HTTP call. (Currently, this scenario is easier with Native modules for lack
>>> of Local Storage API or being able to wait on port subscriptions. But it's
>>> still doable.)
>>>
>>> So working towards a Model reflecting the visuals on the page has been
>>> an interesting challenge. I'm not claiming it's easier, but so far I've
>>> found it avoids a class of problems, and has led to some interesting
>>> discoveries in my own apps. One small example: I realized that my LoggedIn
>>> and NotLoggedIn routes should actually be separate "apps". Attempts to
>>> model this in a SPA fashion with the LoggedIn and NotLoggedIn routes as
>>> siblings always came up with the conundrum: how do I make it a compiler
>>> error for the model to be in LoggedIn mode but I receive a NotLoggedIn
>>> message, or vice versa? Even using TEA, I could not avoid this situation.
>>> Then I realized the only way to do that would be as separate apps. And that
>>> it was entirely possible to separate them. My "login page" turned out to be
>>> an entirely self-contained process: the user filling in info, obtaining a
>>> token, and saving it to local storage.
>>>
>>> I post this in the slim hope it is helpful to someone.
>>>
>>
--
You received this message because you are subscribed to the Google Groups "Elm
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.