LiveStores augments your ActionCable channels with methods to easily update Svelte stores directly from your backend.
Let's assume you have a web app and would like to display real-time messages to a specific user. This can be easily done with LiveStores by pushing new messages to a Svelte store as they happen. Let's have a look:
Inside your component, set up a subscription and initialize a messages
store.
<script>
import { subscribe, getStore } from 'livestores'
import { onDestroy } from 'svelte'
// Set up a subscription to the UserChannel
// (You can also subscribe with params: subscribe('SomeChannel', {someparam: 123}))
const subscription = subscribe('UserChannel')
// Don't forget to unsubscribe when the component is destroyed
onDestroy(subscription.unsubscribe)
// Get a reference to the messages store and optionally initialize it with an empty array
const messages = getStore('messages', [])
</script>
{#each $messages as message}
<p>{message.text}</p>
{/each}
On the Ruby side, we of course need a channel that we can subscribe to:
# user_channel.rb
class UserChannel < ApplicationCable::Channel
def subscribed
stream_for current_user
end
end
Now you can server-side push directly into the messages
store through the UserChannel:
UserChannel[some_user].store('messages').merge([{text: "Hello from Ruby"}])
LiveStores comes with 3 built-in methods that you can use to update stores on the client: set
, merge
, and upsert
:
set(data)
UserChannel[some_user].store('current_user').set(current_user.as_json)
This replaces the value of store with whatever is passed to the method.
merge(data)
UserChannel[some_user].store('current_user').merge({name: 'new name'})
This deeply merges the value of the store with whatever is passed to the method. If the deep merge encounters arrays, they will be concatenated.
upsert(data, key = "id")
UserChannel[some_user].store('projects').upsert([{id: 4, name: "new name"}])
This is basically the same as merge
, but instead of concatenating arrays, it upserts the objects inside the array, using specified key
(id
by default).
You can also define custom methods to update your stores.
import { registerHandler } from 'livestores'
registerHandler('concat', function(store, data) {
store.update(current => `${current}${data}`)
})
const longString = getStore('long_string', "initial string")
UserChannel[some_user].store('long_string').concat "next chunk"
Note that custom methods can only take one argument.
The store
method is also available on a Channel instance. That means that you can update Svelte stores through a specific connection, instead of broadcasting to all subscribers:
# user_channel.rb
class UserChannel < ApplicationCable::Channel
def subscribed
stream_for current_user
store('current_user').set(current_user.as_json)
end
end
When using LiveStores in an SSR context, it is very important to call reset()
before or after rendering, to clear the stores and avoid any data leakage between requests.
import { reset } from 'livestores'
reset()
// ... rest of code comes here
Add this line to your application's Gemfile:
gem 'livestores'
And then execute:
$ bundle install
Install the package:
$ npm i -D livestores