Stores

Learn what a store is and how to create, modify and reset them.

What is a store?

A store is an object that holds global state. They are named objects, meaning different groups of global data can be accessed under a single name.

Stores are also reactive. Any changes made to a store will trigger a re-render on all Alpine components that are subscribed.

An Alpine component will be automatically subscribed to global state changes when it first accesses a piece of global state. There is no need to tell Spruce which components need to be re-rendered when state changes.

Creating a new store

You can create a store using the Spruce.store() method.

Spruce.store('user', {
name: 'Ryan Chandler',
email: 'support@ryangjchandler.co.uk',
})

The first argument is the name of the store. This should describe what data is being tracked inside of the store. The second argument is the state itself.

Accepted data types

When defining a store, you will most likely be using an object or array to hold your data. However, Spruce also supports using scalar values such as string, int or boolean as the data type.

Spruce.store('name', 'Ryan Chandler')

The Spruce object is defined on the windowobject, therefore accessing it inside of a bundled file (or outside of the global scope) will require you to use window.Spruce instead.

Spruce also accepts a function as the second argument. This function should return the store's state, similar to function-based components in Alpine.

Spruce.store('user', function () {
let user = window.User
/* Do some stuff with `user` here */
return {
name: user.name,
email: user.email,
}
})

Accessing a store from Alpine

To access a store from an Alpine component, Spruce provides a magic $store variable. This is an object that holds each store under a property.

For example, to access tthe user store above, you would use $store.user.name inside of your Alpine component.

<span x-data x-text="$store.user.name">
<!-- outputs "Ryan Chandler" -->
</span>

Previous versions of Spruce required the user of an x-subscribe directive on the root element. Since 1.x, this is no longer needed. The $store variable is automatically available for use in your components.

If you are accessing the $store variable from inside a function on your component, you must use this.$store instead.

function componentData() {
return {
getName() {
return this.$store.user.name;
}
}
}

Accessing a store from JavaScript

If you would like to access a store inside of a JavaScript file or <script> tag, you can use the Spruce.store() method too. Pass the name of the store, omitting the second argument.

Spruce.store('user', {
name: 'Ryan Chandler',
email: 'support@ryangjchandler.co.uk',
})
const userStore = Spruce.store('user')
console.log(userStore.name)

This will return the underlying Proxy which can be used to access all of your state.

Overwriting a store

If you wish to overwrite, or redefine, your store at runtime, you must use the Spruce.reset() method. This method will forcefully overwrite the current state.

Spruce.reset('user', {
name: 'John Doe',
})
console.log(Spruce.store('user').name) // "John Doe"
console.log(Spruce.store('user').email) // "undefined"

Adding methods to stores

Much like data objects in Alpine, you can define methods on your Spruce stores. These methods can then be called on the store object.

Spruce.store('user', {
name: 'Ryan Chandler',
email: 'support@ryangjchandler.co.uk',
getFirstName() {
return this.name.split(' ')[0]
}
})
const userStore = Spruce.store('user')
console.log(userStore.getFirstName()) // "Ryan"

The this context of a method is bound to the store object itself, so you can access all of the defined properties.

Getters and setters

Since Spruce generally uses "object literals" to hold state, you can also define getters and setters on those objects.

Spruce.store('user', {
name: 'Ryan Chandler',
email: 'support@ryangjchandler.co.uk',
get firstName() {
return this.name.split(' ')[0]
}
})
const userStore = Spruce.store('user')
console.log(userStore.firstName) // "Ryan"