njwest

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

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 :slight_smile:

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 :slight_smile:

njwest

njwest

Many thanks for the suggestions :slight_smile: 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.

Where Next?

Popular in Questions Top

_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2, I would like to get: ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52673 488
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement