Hello!
I’m working on an elm-land
SPA, and looking into ways I can reduce the number of loading skeletons and spinners throughout the user experience. I’m here to solicit advice as well as share a solution that seems promising but I haven’t really put it through its paces.
In React I might use swr
or @tanstack/react-query
to achieve what I’m after, and in Elm I’ve heard of the “store pattern” and elm-fetch
.
I haven’t had luck adapting the store pattern to work in elm-land
. The direct approach of importing Pages.*
stuff (like a dataRequests : Store -> Route params -> List Action
presented in the demo store pattern app) into Shared
results in circular dependencies that I couldn’t really see how to work around without getting into patching the code-gen. An alternative approach of setting a List Store.Action
from each page’s init
isn’t quite the same: in order to respond to changes in the store to do waterfall requests (or dependent queries as TanStack calls them) you end up with a lot of boilerplate in my experience—because you can’t dynamically create the List Action
based on existing Store
, if you want to have A : Action
followed by B : Action
in one use case, but just do A : Action
in another case, you’d need to split A
into two different actions, or have it take a Bool
parameter. I don’t know if that makes sense it’s possible that there’s ways around that I wasn’t seeing.
(I didn’t try out elm-fetch
, at a glance it seems like it might have the same limitations.)
The pattern I landed on seems closer to swr
from what I understand (without having used that library) and I’m curious if anyone has experience with something similar. It borrows from store pattern but with a bit “looser” types in exchange for less boilerplate and a bit more ease-of-use (at least as far as I’ve experimented with it).
A Store
in this approach is just a simple cache, basically Dict Url (WebData Json.Decode.Value)
, and you can use
Effect.sendStoreRequest : Strategy -> Request a -> Effect msg
in init
& update
to make requests, and
Store.get : Request a -> Store -> WebData a
to lookup the requested data in view
, where Request
is essentially the argument to Http.request
type alias Request a =
{ method : String
, headers : List Http.Header
, path : List String
, query : List Url.Builder.QueryParameter
, body : Http.Body
, decoder : Decoder a
}
I removed timeout
& tracker
because I never use them, and split the url
into a bit more structured pieces to make it easier to manipulate.
Here’s an example of a page with multiple requests, an example of pagination, and a waterfall request. And here’s a link to the deployed demo app (the home, posts, and users pages use the cache).
I’m still not sure about a good solution for waterfall requests with this approach. What I ended up with is just emitting an event from view
via a custom element when the result of the initial request loads React-brained for sure, but it works.