This is a simple state management library with model coding style for javascript/typescript.
Create a model function:
// model.js
// A parameter for model state.
export function counting(state){
// Define instance object for outside usage.
return {
// Define properties for instance.
count: state,
symbol: !state? '': (state > 0? '+' : '-'),
// Create action methods for changing state.
increase:()=> state + 1,
decrease:()=> state - 1,
add(...additions){
return additions.reduce((result, current)=>{
return result + current;
}, state);
}
};
}
Create store:
// store.js
import {counting} from './model';
import {createStore} from 'as-model';
// Create and initialize a model store.
const store = createStore(counting, 0);
// Get instance and call action methods to change state.
store.getInstance().increase();
// Get new properties from instance.
console.log(store.getInstance().count); // 1
store.getInstance().add(2,3);
console.log(store.getInstance().count); // 6
Create multiple stores:
import {counting} from './model';
import {createKey, createStores} from 'as-model';
// Create model key with initial state.
const countingKey0 = createKey(counting, 0);
const countingKey1 = createKey(counting, 1);
// Use model keys as templates to create multiple stores.
const stores = createStores(countingKey0, countingKey1);
// Find store by model key
const store0 = stores.find(countingKey0);
store0?.getInstance().increase();
console.log(store0?.getInstance().count); // 1
Model key is a template for creating multiple stores, and it is also an identifier to find the rightstore from multiple stores.
Use model API to create store or key.
import {counting} from './model';
import {model} from 'as-model';
const store = model(counting).createStore(0);
const key = model(counting).createKey(0);
......
In typescript develop environment, model
API can do a type check for making sure the model action method returns a correct type.
// ts
import {model} from 'as-model';
// The model api ensures every action method returns a same type value with model state.
const counting = model((state: number)=>{
return {
count: state,
increase:()=>state + 1 + '', // type error, should be number, but returns string.
decrease:()=>state - 1,
add(...additions: number[]){
return additions.reduce((result, current)=>{
return result + current;
}, state);
}
};
});
const store = counting.createStore(0);
const key = counting.createKey(0);
......
Sync store
import {counting} from './model';
import {model} from 'as-model';
const store = model(counting).createStore(0);
const {getInstance} = store;
// Subscribe the state changes.
const unsubscribe = store.subscribe((action)=>{
console.log(store.getInstance());
});
getInstance().increase(); // output: {count: 1}
// Destroy subscription.
unsubscribe();
Sync store with state in react hooks:
import {model, createStores} from 'as-model';
import {
createContext,
useRef,
useState,
useEffect,
useContext
} from 'react';
// Local state management
function useModel(modelFn, defaultState){
// Use ref to persist the store object.
const storeRef = useRef(model(modelFn).createStore(defaultState));
const store = storeRef.current;
// Set store instance as an initial state for useState.
const [state, setState] = useState(store.getInstance());
useEffect(()=>{
// Subscribe the state changes.
const unsubscribe = store.subscribe((action)=>{
setState(store.getInstance());
});
// Destroy subscription when component unmounts.
return unsubscribe;
}, []);
return state;
}
function App(){
const {
count,
increase
} = useModel(counting, 0);
}
// global static state management
function useModelStore(store){
const [state, setState] = useState(store.getInstance());
useEffect(()=>{
const unsubscribe = store.subscribe((action)=>{
setState(store.getInstance());
});
return unsubscribe;
}, []);
return state;
}
const store = model(counting).createStore({state: 0});
function App(){
const {
count,
increase
} = useModelStore(store);
}
// global dynamic state management
const ModelContext = createContext(null);
function create(...keys){
return {
provide(Component){
return function Provider(props){
// Create and persist multiple stores in Component, that makes different elements from this Component carry different stores.
const storesRef = useRef(createStores(...keys));
const stores = storesRef.current;
// Use Context to provide multiple stores to all the children.
return (
<ModelContext.Provider value={stores}>
<Component {...props} />
</ModelContext.Provider>
);
}
}
}
}
function useModelKey(key){
const stores = useContext(ModelContext);
if(stores==null){
throw new Error('ModelContext is not provided');
}
// Use model key to find the right store.
const store = stores.find(key);
if(store==null){
throw new Error('Can not find store by model key');
}
const [state, setState] = useState(store.getInstance());
useEffect(()=>{
const unsubscribe = store.subscribe((action)=>{
setState(store.getInstance());
});
return unsubscribe;
}, []);
return state;
}
const countingKey = model(counting).createKey(0);
const App = create(countingKey).provide(function App(){
const {
count,
increase
} = useModelKey(countingKey);
});
npm install as-model
function createStore(modelFnOrKey, initialState?):store
- modelFnOrKey - a model function accepts a state parameter and returns an object with action methods, or a model key of model function.
- initialState - the initial state of the model.
A store object with model key and methods. The store object has getInstance
method to get the instance of the model; subscribe
method to subscribe the state changes; update
method to update the store with new model function and initial state; destroy
method to destroy the store.
store structure:
{
getInstance: ()=>instance,
subscribe: (listener)=>unsubscribe,
key: modelKey,
destroy: ()=>void
update: (
data:{
model?:modelFn,
initialState?: any
}
)=>void
}
function createKey(modelFn, initialState?):key
- modelFn - a model function accepts a state parameter and returns an object with action methods.
- initialState - the initial state of the model.
A model key function with createStore
method to create a store with the model key.
key structure:
{
createStore: (initialState?)=>store
}
function createStores(...keys):stores
- keys - multiple model keys of model functions.
Multiple stores created by the model keys.
stores structure:
{
find: (key)=>store,
destroy: ()=>void
}
function createSignal(store):signalAPI
- store - a store object created by
createStore
method.
Signal api object with subscribe
method to subscribe the state changes, and getSignal
method to get the signal callback function.
signalAPI structure:
{
subscribe: (listener)=>unsubscribe,
getSignal: ()=>signal,
key: modelKey,
}
The signal function returns a real time instance from store. Only when the properties picked from real time instance are changed, the subscribed listener can receive an action notification.
function model(modelFn):modelAPI
- modelFn - a model function accepts a state parameter and returns an object with action methods.
Model api object with createStore
and createKey
methods to create store and key for the model function.
modelAPI structure:
{
createStore: (initialState?)=>store,
createKey: (initialState?)=>key
}
function config(options):configAPI
- options - an object with the following properties:
- batchNotify - a callback function to batch notify the listeners, for example:
unstable_batchedUpdates
from react-dom.
- batchNotify - a callback function to batch notify the listeners, for example:
All apis above except createSignal
API.
{
createStore: (modelFnOrKey, initialState?)=>store,
createKey: (modelFn, initialState?)=>key,
createStores: (...keys)=>stores,
model: (modelFn)=>modelAPI
}
chrome: '>=91',
edge: '>=91',
firefox: '=>90',
safari: '>=15'