I’m trying to implement something similar to a shopping cart.
I’ve got two LV.
Page LV
SecondPage LV
They both need to render the CartComponent (in the future a full-featured cart, right now only a %{ total: X}.
The component is stateful and handles the events (see component-click event within CartComponent).
I originally followed this very interesting thread regarding not un-mounting components upon a LV change via routing, however I couldn’t make the proposed solution work. Regardless I’m giving it a try via localStorage instead, so I figure that’s a different solution.
My goal is by steps:
User lands in either of the LVs (which make use of CartComponent)
JS hooks load localStorage.shoppingCart --> forward it to LV
LV receives the socketParams and forwards it to the CartComponent whose update re-renders with new value.
Whatever new event in the cart logic --> CartComponent handles it and it is appropriately updated.
The problem I’m encountering is with the first render of all. Something like this happens.
User lands in PageLive --> renders CartComponent with empty shoppingCart
Second time PageLive is mounted when the connected?(socket) is true --> receives new shoppingCart from JS hook.
CartComponent's update does fire and receives the updated shoppingCart post-socket connected.
CartComponent's UI render does not update
Manually try to fire a component-click event --> CartComponent handles and UI is properly updated.
This is the very test repo i’m running
Am I missing something to make that first update re-render?
The way I see it localStorage here is only a reflection of what has been processed and verified at the backend beforehand(ie every event and change is handled in BE).
It’s synced to localStorage in order to resume upon page change (and hence new LV mount)
Or even across different visits to the same page.
I’d be happy to try a different architecture/implementation defly if you have any suggestions. Even though I’d still like to know why this doesn’t work upon first update.
shoppingCart = %{}
if connected?(socket) do
IO.puts("Component connected")
shoppingCart = Jason.decode!(Map.get(assigns.socketParams, "shoppingCart"))
end
do:
shoppingCart = if connected?(socket) do
IO.puts("Component connected")
Jason.decode!(Map.get(assigns.socketParams, "shoppingCart"))
else
%{}
end
also wrap your value in a html element (liveview needs this)
Just to let you know that I’m still trying to tie up both LVs and the LiveComponent in sync with the same data after page changes via handle_info at LVs (didn’t manage to do so yet ).
Regardless your suggestions helped me a lot so far! Will update here as soon as I manage to find a solution, thanks!
Thank you @outlog your answer helped me debug the specific problem communicating LiveView -> LiveComponent
In parallel I also tried to solve a tangential problem here, which tries to have a common source of truth for all LVs that will consume the shoppingCart (basically I wanted to keep the localStorage-resumed shoppingCart in memory so every newly mounted LV could consume it without having to resume form localStorage again). Unfortunately I couldn’t land it in the way I intended, but I think I’m overcomplicating things so I’ll pivot my approach.
Regardless that’s a different story.