_toni
LiveView. Implementing a Shopping Cart Component. How to load state from localStorage and forward it to Components
Hello 
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
CartComponentwhoseupdatere-renders with new value. - Whatever new event in the cart logic -->
CartComponenthandles 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
CartComponentwith emptyshoppingCart - Second time PageLive is mounted when the
connected?(socket)is true --> receives newshoppingCartfrom JS hook. -
CartComponent'supdatedoes fire and receives the updatedshoppingCartpost-socket connected. -
CartComponent's UIrenderdoes not update - Manually try to fire a
component-clickevent -->CartComponenthandles 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?
Thanks!
Marked As Solved
outlog
your example is a bit broken - the js breaks, when localstorage is empty fix:
shoppingCart: JSON.parse(localStorage.shoppingCart || null) || "{}",
now when decoding the json you’ll get a map - not a struct…
so change the render in cartcomponent to Map.get(assigns.shoppingCart, "total")
notice “total” vs :total
likewise when you update do it in a map:
shoppingCart = %{
"total" => Enum.random(10..1000)
}
this code is broken elixir
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)
<div><%= Map.get(assigns.shoppingCart,"total") %></div>
then it should work…
ps: made a fork with the fixes if anybody finds this thread… https://github.com/petermm/component-test
Also Liked
Exadra37
This is client side storage, and you cannot trust it, therefore I am not fan of using the browser local storage for storing data.
So my advice is for you to figure out how you can make it work properly via backend, and not not via browser local storage.
Sorry not be able to help you with your way, but I am also new to Live View.
_toni
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.







