Phoenix Liveview using LocalStorage or SessionStorage
Sometimes we want to fetch or store data in localStorage
or sessionStorage
, we can fetch/store this data using LiveView hooks.
These hooks allow for interoperability with JavaScript and grant us the ability to interact with a liveview during its lifecycle.
For storing the data, we will call a JavaScript hook inside a liveview function to store the value of a counter.
For fetching the data, we will be using the reconnected
hook to fetch data from localStorage
or sessionStorage
. In this post we will be using sessionStorage
as the data is wiped when we close the tab. If you wanted to persist the data, localStorage
is what you are looking for. Both storages use the same function getItem(key)
where we provide a key which hopefully has the values we are looking for.
For more information regarding sessionStorage
and localStorage
see the Mozilla Docs
Cloning the Demo Project
Let’s clone the demo Phoenix LiveView app. The main branch will only contain the minimal amount of setup without implementing any hooks:
git clone git@github.com:distortia/counter_example.git
For the complete solution check out the complete
branch.
Setting up the Hooks
NOTE: All hooks need to be defined above the following line let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
Initializing our empty hooks
object to be populated later:
let hooks = {}
We need to add our hooks to the liveSocket
:
let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken},
hooks: hooks,
})
Saving State on Button Click
To save the state of our counter on button click we need to write a custom hook.
In app.js
, we are going to create a hook called saveCount
:
hooks.saveCount = {
mounted() {
this.handleEvent("saveCount", ({ count }) =>
sessionStorage.setItem("count", count)
)
}
}
saveCount
uses the mounted
hook so it gets attached as the component is mounted.
For the handle_event
function we use a new function called push_event
which will invoke the JavaScript hook we called saveCount
def handle_event("inc", _session, socket) do
count = socket.assigns.count + 1
socket = socket |> assign(count: count)
{:noreply, push_event(socket, "saveCount", %{count: count})}
end
To wire up our button we need to add a unique ID as well as the hook:
<button id="inc-button" phx-click="inc" phx-hook="saveCount">Increment</button>
We should now see the value of count being incremented in sessionStorage
as well as on the page
Restoring state on Reconnection
To restore the state from sessionStorage
we need to create a new hook that I will call restore
:
hooks.restore = {
mounted() {
count = sessionStorage.getItem("count")
this.pushEvent("restore", {count: count})
}
}
This hook will call our restore
event in the liveview which looks like:
def handle_event("restore", %{"count" => count}, socket) do
count = String.to_integer(count)
socket = socket |> assign(count: count)
{:noreply, socket}
end
NOTE: Values in sessionStorage are strings and we need to properly convert to the correct type
We do need to handle cases where we have no value in the sessionStorage
, we can do that by explcitly handling nil
values above the original handle_event
call:
def handle_event("restore", %{"count" => nil}, socket), do: {:noreply, socket}
def handle_event("restore", %{"count" => count}, socket) do
count = String.to_integer(count)
socket = socket |> assign(count: count)
{:noreply, socket}
end
In order to actually call the restore
hook we need to add a unique ID to the element and attach our hook to it:
<section class="phx-hero" id="page-live" phx-hook="restore" >
We now have a way to restore data from sessionStorage
or localStorage
into our LiveView!
To test this out, we can go into the developer settings and manually add key-value pair to sessionStorage
example: count: 100
When we refresh the page, the counter should reflect the value in the sessionStorage
Conclusion
This is how to store and restore data in a LiveView using Hooks and sessionStorage
. If you found it helpful, reach out on twitter or share with your friends!
If you are interested in working with elixir every day, come join my remote team at InfluxData!
Thanks!