njwest
LiveView Hooks: Handling Channel connection race condition between mounted()/updated()
We have a Channel-based web app that we’re putting into a LiveView component, however we have run into a suspected race condition in our Hook between joining a channel and setting state from response in mounted(), which pops up when we try to use channel response state set in mounted() in updated(). Every so often, the state isn’t set fast enough in mounted() and is undefined when updated() runs, so we get error: undefined.
Our hook code:
mounted() {
this.channel = socket.channel('channel:' + channel_id)
this.channel.join().receive("ok", response => {
this.someState = response.some_state;
})
},
updated(){
/// This sometimes fails with error: this.someState is undefined
doSomething(this.someState)
}
Wondering if there is a way to block updated() from running until after mounted/channel join, maybe with something async inside mounted()?
Would prefer to not have to join this channel outside of its component, as it is component-specific.
Most Liked
aseigo
So, what I do with LiveView is keep all the pubsub on the server side and only use the LiveView websocket for LiveView communication. This keeps things nice and simple ![]()
That said .. could you just cache the events in update until this.valueFromChannel is set? e.g.:
if (this.valueFromChannel == null) {
pendingUpdateEvents.push(valueFromAssigns);
} else {
performUpdateAction(valueFromAssigns);
}
}```
and then in in the “ok” response function do something like
pendingUpdateEvents.forEach(update => performUpdateAction(update));
pendingUpdateEvents = [];
Flavor to taste ![]()
njwest
Many thanks for the suggestions
Tried 1 and 2 already but when the negative outcome of this race condition occurs, updated will have already run before this.someState is set, so a check in this case will prevent the error while leaving doSomething untriggered. 3 does not really work for the pattern of this live_component/hook (more on this in a moment).
Also tried doing an async/await with the channel join (on the join() and also on a setter called in the join.response) however running async/await anywhere in our hook (even on an arbitrary call elsewhere) results in a regeneratorRuntime error. I’ll have to learn more about Hooks under the hood before I go down that webpack/babel rabbithole.
In the earlier Channels + JS iteration of this web app, doSomething did occur in the join.receive callback, however in our current component + JS hook structure calling doSomething in the join.receive will go against the pattern we’re using that operates on a value we’re getting in the DOM from the LiveView socket assigns.
The pattern we implemented in the interest of brevity:
mounted(){
this.channel = socket.channel('channel:' + channel_id)
this.channel.join().receive("ok", response => {
this.valueFromChannel = response.valueFromChannel;
})
}
updated(){
if (valueFromAssigns === "foo") {
doSomething(this.valueFromChannel)
} else if (valueFromAssigns === "bar"){
doSomethingElse(this.valueFromChannel)
}
}
Looks like we have to further break up our live_components in this case, which is probably for the best in the long run.
I am likely overlooking another way to accomplish this within the hook, but it does feel like the LiveView JS hook lifecycle could benefit from an intuitive way to achieve asynchronicity. I realize that the entire point of using LiveView is to manage DOM/state from the server, not from the JS, but in our quick-and-dirty conversion it would be convenient if we could block updated() until something like an async/await function completes, similar to React/Vue lifecycle methods/plain JS with async/await.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance










