What’s the point of this line? Why are you pulling out events from the assigns (not sure what you’re putting in there in mount) and assigning them as an async result? If you’re trying to use the component, wouldn’t you have assigned it as an async result in the loading state initially? But having an assign and a stream for the same thing seems wrong.
If your intention was to ensure that the template has something to work with before handle_async populates the stream, you could assign a new empty stream in mount that gets populated in handle_async.
def mount(socket) do
{:ok,
socket
|> stream(:events, [])
|> start_async(:get_events, fn -> fetch_events(location) end)}
end
def handle_async(:get_events, {:ok, fetched_events}, socket) do
{:noreply, stream(socket, :events, fetched_events)}
end
By default, calling stream/4 on an existing stream will bulk insert the new items on the client while leaving the existing items in place.
And this way your template can just use @streams.events rather than juggling and switching between @events and @streams.events.
You don’t need an assign that is an AsyncResult in the first place, which means you don’t need to use the <.async_result ...> component. I don’t think a stream will work with the async_result component.
You can handle the empty stream or use a simple atom or string assign to hold the loading state.
Personally, I found it to be okay to use both AsyncResult, start_async and stream in the same LiveView together.
AsyncResult and the async_result component make loading and error states easy to represent, and can serve to carry ancillary data like counts or other book-keeping.
The stream, then, is solely responsible for shipping data to the client efficiently.