storeon-velo v4.1.3
storeon-velo
A tiny event-based state manager Storeon for Velo by Wix.
❗ BREAKING CHANGE ❗
storeon-velois moving toV4with a few breaking changes
Example
import { createStoreon } from 'storeon-velo';
const app = (store) => {
store.on('@init', () => ({ count: 0 }));
store.on('increment', ({ count }) => ({ count: count + 1 }));
};
const { getState, setState, dispatch, connect, readyStore } = createStoreon([app]);
// Subscribe for state property 'count'.
// The callback function will be run when the store is redy `readyStore()`
// and each time when property 'count' would change.
connect('count', ({ count }) => {
$w('#text1').text = String(count);
});
$w.onReady(() => {
$w('#button1').onClick(() => {
// Emit event
dispatch('increment');
});
// initialize observe of the state changes
return readyStore();
});Install
You use the Package Manager to manage the npm packages in your site.
Check latest available version

API
createStoreon
Creates a store that holds the complete state tree of your app and returns 5 methods for work with the app state. (modules API)
const { getState, setState, dispatch, connect, readyStore } = createStoreon(modules);Syntax
function createStoreon(Array<Module | false>): Store
type Store = {
getState: Function
setState: Function
dispatch: Function
connect: Function
readyStore: Function
}getState
Returns an object that holds the complete state of your app.
const state = getState();Syntax
function getState(): objectsetState
Set partial state. Accepts an object that will assign to the state.
setState({ xyz: 123 });Syntax
function setState(data: object): voiddispatch
Emits an event with optional data.
dispatch('event/type', { xyz: 123 });Syntax
function dispatch(event: string, data?: any): voidconnect
Connects to state by property key. It will return the function disconnect from the store.
const disconnect = connect('key', (state) => { });
disconnect();You can connect for multiple keys, the last argument must be a function.
connect('key1', 'key2', (state) => { });Syntax
function connect(...args: [key: string, ...key: string[], handler: ConnectHandler]): Disconnect
type ConnectHandler = (state: object) => void | Promise<void>
type Disconnect = () => voidreadyStore
Start to observe the state changes and calls of the connect() callbacks.
It must be used inside $w.onReady() when all the page elements have finished loading
$w.onReady(() => {
return readyStore();
});Syntax
function readyStore(): Promise<any[]>Store
The store should be created with createStoreon() function.
It accepts a list of the modules.
Each module is just a function, which will accept a store and bind their event listeners.
import wixWindow from 'wix-window';
import { createStoreon } from 'storeon-velo';
// Business logic
const appModule = (store) => {
store.on('@init', () => {
return {
items: [],
};
});
store.on('items/add', ({ items }, item) => {
return {
items: [...items, item],
};
});
};
// Devtools
const logger = (store) => {
store.on('@dispatch', (state, [event, data]) => {
if (event === '@changed') {
const keys = Object.keys(data).join(', ');
console.log('changed:', keys, state);
} else if (typeof data !== 'undefined') {
console.log('action:', event, data);
} else {
console.log('action:', event);
}
});
};
const { getState, setState, dispatch, connect, readyStore } = createStoreon([
appModule,
wixWindow.viewMode === 'Preview' && logger,
]);
$w.onReady(readyStore);Syntax
function createStoreon(Array<Module | false>): Store
type Module = (store: StoreonStore) => void
type StoreonStore = {
dispatch: Function
on: Function
get: Function
set: Function
}Storeon store methods
store.dispatch
Emits an event with optional data.
store.dispatch('event/type', { xyz: 'abc' });Syntax
function dispatch(event: string, data?: any): voidstore.on
Adds an event listener. store.on() returns cleanup function.
This function will remove the event listener.
const off = store.on('event/type', (state, data) => { });
off();Syntax
function on(event: string, listener: EventListener): Unbind
type EventListener = (state: object, data?: any) => Result
type Unbind = () => void
type Result = object | void | Promise<void> | falsestore.get
Returns an object that holds the complete state of your app. The app state is always an object.
const state = store.get();Syntax
function get(): objectstore.set
Set partial state. Accepts an object that will assign to the state. it can be useful for async event listeners.
store.set({ xyz: 123 });Syntax
function set(data: object): voidEvents
There are 5 built-in events:
@init
It will be fired in createStoreon(). The best moment to set an initial state.
store.on('@init', () => { });@ready
It will be fired in readyStore() (it must be inside $w.onReady() when all the page elements have finished loading).
store.on('@ready', (state) => { });@dispatch
It will be fired on every new action (on dispatch() calls and @changed event).
It receives an array with the event name and the event’s data.
it can be useful for debugging.
store.on('@dispatch', (state, [event, data]) => { });@set
It will be fired when you use setState() or store.set() calls.
store.on('@set', (state, changes) => { });@changed
It will be fired when any event changes the state. It receives object with state changes.
store.on('@changed', (state, changes) => { });You can dispatch any other events. Just do not start event names with @.
Reducers
If the event listener returns an object, this object will update the state. You do not need to return the whole state, return an object with changed keys.
// 'products': [] will be added to state on initialization
store.on('@init', () => {
return { products: [] };
});Event listener accepts the current state as a first argument and optional event object as a second.
So event listeners can be a reducer as well. As in Redux’s reducers, you should change immutable.
Reducer
store.on('products/add', ({ products }, product) => {
return {
products: [...products, product],
};
});Dispatch
$w('#buttonAdd').onClick(() => {
dispatch('products/add', {
_id: uuid(),
name: $w('#inputName').value,
});
});Connector
connect('products', ({ products }) => {
// Set new items to repeater
$w('#repeater').data = products;
// Update repeater items
$w('#repeater').forEachItem(($item, itemData) => {
$item('#text').text = itemData.name;
});
});Async operations
You can dispatch other events in event listeners. It can be useful for async operations.
Also, you can use store.set() method for async listeners.
import wixData from 'wix-data';
import { createStoreon } from 'storeon-velo';
const appModule = (store) => {
store.on('@init', () => {
return {
products: [],
error: null,
};
});
store.on('@ready', async () => {
try {
// wait to fetch items from the database
const { items } = await wixData.query('Products').find();
// resolve
store.set({ products: items });
} catch (error) {
// reject
store.set({ error });
}
});
// Listener with the logic of adding new items to list
store.on('products/add', ({ products }, product) => {
return {
products: [product, ...products],
};
});
store.on('products/save', async (_, product) => {
try {
// wait until saving to database
await wixData.save('Products', product);
// resolve
store.dispatch('products/add', product);
} catch (error) {
// reject
store.set({ error });
}
});
}
const { getState, setState, dispatch, connect, readyStore } = createStoreon([
appModule,
]);Work with Repeater
Use forEachItem() for updating a $w.Repeater items into connect() callback.
connect('products', ({ products }) => {
// Set new items to repeater
$w('#repeater').data = products;
// Update repeater items
$w('#repeater').forEachItem(($item, itemData) => {
$item('#text').text = itemData.name;
});
});Never nest the event handler for repeated items into any repeater loop.
Use global selector $w() instead and use context for retrieving repeater item data.
connect('products', ({ products }) => {
$w('#repeater').data = products;
$w('#repeater').forEachItem(($item, itemData) => {
$item('#text').text = itemData.name;
- $item('#repeatedContainer').onClick((event) => {
- dispatch('cart/add', itemData);
- });
});
});
$w.onReady(() => {
+ $w('#repeatedContainer').onClick((event) => {
+ const { products } = getState();
+ const product = products.find((i) => i._id === event.context.itemId);
+
+ dispatch('cart/add', product);
+ });
return readyStore();
});more:
License
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago