A reactive store of data for global state management in JS.
npm install storem
// OR
yarn add storem
In store.js file:
import { Store } from 'storem';
const store = new Store();
export default store;
In otherfile.js file:
import store from './store'
store.set({ name: 'John' })
// When "name" changes in the store, this callback will be executed
store.listen('name', (value, oldValue) => {
if (value !== oldValue) {
console.log(`name changed to ${value}`)
}
})
store.set({ name: 'Mary' }) // Will emit the event binded before - name changed to 'Mary'
store.get('name') // Mary
store.delete('name')
store.get('name') // undefined
To start using Storem we should create a file to instantiate the store and export it:
// store.js
import { Store } from 'storem';
const store = new Store({ persist: true });
export default { store };
Obs: Storem does not have support for multiple stores yet.
Storem can receive a configuration object as the constructor argument. Below are the available options:
-
persist
Determines if the data should be persisted throughout the pages (uses localStorage, so it is only available in the browser) -
debug
Turn on debug mode. If enabled, all store changes will be logged in the console. -
reactOnDelete
If true, will execute the callback events when data is deleted from the store.
Example:
import { Store } from 'storem'
const store = new Store({
persist: true,
debug: true,
reactOnDelete: false
})
To add data to the store or update an existing data, we use the set method:
import { Store } from 'storem'
const store = new Store()
store.set({ name: 'John' }) // Will create the data "name" with value "John"
store.set({ name: 'Mary' }) // Will update the data "name" to "Mary"
Fetch a specific field from the store:
const name = store.get('name')
Fetch all data from the store.
const storeData = store.all()
// Using destructuring assignment
const { name, age, address } = store.all()
Retrieves specifics fields from the store. This method will return an object with the required data:
const data = store.only(['name', 'age', 'address'])
// { name: 'John', age: 23, address: 'Lombard Street' }
// Using destructuring assignment
const { name, age, address } = store.only(['name', 'age', 'address'])
Verify if a data exists in the store:
store.add({ name: 'John' })
store.has('name') // true
You can aswell delete a data from the store
store.set({ name: 'John' })
store.delete('name')
console.log(store.get('name')) // undefined
Storem is reactive to data change. This means that Storem can detect all data changes in your application. It's possible to attach an event callback to be executed whenever a specific data is updated in the store. This is a powerfull tool, since it can be used to globally sinchronize all components of your application.
Listen to changes in a specific data in the store, and once it changes, the callback function is executed. The callback function will receive the current value and the old value as arguments:
store.set({ activeLesson: 1 })
// Listen to changes in the activeLesson data, and executes the callback
store.listen('activeLesson', (value, oldValue) => {
console.log('Active Lesson has changed to: ' + value)
}
The listen method is where the magic happens. You can synchronize all components within your application listening to changes in the store.
Example:
// Whenever cart items changes, total value is recalculated
store.listen('cartItems', (cardItems) => {
recalculateTotal(cardItems)
})
Mutations are functions that can be registered in the store to alter his data. It is possible to create a mutation using the setMutation method, passing name and function as arguments. The passed function will receive the complete store state as argument. Let's create a mutation that increment a counter, for example:
// Create a counter inside the store
store.set({ counter: 1 })
// Create a mutation to increment the counter
store.setMutation('INCREMENT', state => {
state.counter++
})
To run a mutation, we use the runMutation method passing the mutation name as argument:
store.runMutation('INCREMENT') // Increment counter
store.runMutation('INCREMENT') // Increment counter again
console.log(store.get('counter')) // 3
You can either pass arguments to mutations:
// Create quantity data inside the store with value 0
store.set({ quantity: 0 })
// Create mutation receiving a "quantity" argument
store.setMutation('ADD_QUANTITY', (state, quantity) => {
state.quantity += quantity
})
store.runMutation('ADD_QUANTITY', 10) // Will add 10 to state.quantity
console.log(store.get('quantity')) // 10
Obs: Storem does not react to data change inside mutations. In others words, listeners will not be executed when data changes occurs inside a mutation.
If you want the data to persist throughout the pages, and refreshes, just set the persist config to true in the store configs. With this config, Storem will save the data in the localStorage, and will automatically load it from there when the page refreshes or the user change the route. Storem state will be the same, regardless of page change. This will NOT work in the Node enviroment, just in the browsers.
// Set persist to true when creating the store
const store = new Store({
persist: true,
})
It's important to mention that, if you persist the data, when you refresh or change the page in your application, Storem will load the data automatically from localStorage, but if you set a data manually to the store, it will overwrite the data that was loaded from localStorage. So, if you want to avoid that behaviour, you can check if a data already exists before set the data:
if (!store.has('name')) {
store.set({ name: 'John Doe' })
}
// OR
!store.has('name') && store.set({ name: 'John Doe' })
Storem works well with React, either. In order to use a store in React, the most important thing is to sinchronyze the component rendering with the data in the store. Storem makes this easy.
Look at how to sinchronyze a component with the store:
import React, { useState, useEffect } from 'react'
import store from './store'
export default function Name() {
const [name, setName] = useState(store.get('name') || 'John Doe')
// Synchronize the component state with store data whenever it changes
useEffect(() => {
store.listen('name', value => setName(value))
}, [])
// Function to change the name
const changeName = () => {
store.set({ name: 'Mary' })
}
return (
<div>
<span>{name}</span>
<button onClick={changeName}>Change name</button>
</div>
)
}