plain-store
v0.7.0
Published
A simple immutable store management for React
Downloads
25
Maintainers
Readme
Installation
# npm
npm install plain-store
# yarn
yarn add plain-store
Usage
using with bundler or es module
import { createStore, isDeepEqual, deepFreeze } from 'plain-store';
const initialState = {
count: 0
};
const store = createStore(initialState);
store.setStore({ count: 1 });
function Counter() {
const { count } = store.useStore();
// derive a new value from the store value
const doubled = store.useSelector((state) => state.count * 2);
return (
<div>
<div>count: {count}</div>
<div>doubled: {doubled}</div>
<button onClick={() => store.setStore((prev) => ({ count: 1 + prev.count }))}>Increment</button>
</div>
);
}
store.getStore(); // { count: 1 }
store.setStore((prev) => ({ count: 2 + prev.count })); // { count: 3 }, will trigger Counter re-render
using with script tag
<!-- include react -->
<script src="https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/plain-store/dist/index.iife.js"></script>
<script>
const { createStore, isDeepEqual, deepFreeze } = PlainStore;
const store = createStore({ count: 0 }, {
onChange: (value) => {
console.log('store value changed', value);
}
});
store.setStore({ count: 1 });
</script>
API
createStore(initialState, options?)
Create a store with the initial state.
import { createStore } from 'plain-store';
interface ICreateStoreOptions<T> {
/**
* listen to the store value changes
*/
onChange?: (value: Readonly<T>) => void;
/**
* custom comparator for store value changes, default to `isDeepEqual`
* * use it when the default comparator is not working as expected
* * `isDeepEqual` works for most cases, but it's not perfect, you can provide a custom comparator to handle the edge cases or performance issues.
*/
comparator?: (a: any, b: any) => boolean;
}
interface ISetStoreOptions {
/**
* only update the partial value of the store,
* * the new value will be merged with the old value
*/
partial?: boolean;
}
type ISetStoreOptionsType = boolean | ISetStoreOptions
interface IStore<T> {
// Get the current state of the store, none reactive, could be used anywhere.
getStore: () => Readonly<T>;
// Set the state of the store, could be used anywhere, callback could be async.
// * return a promise if the params is async function
// * use getStore() to get the latest state of the store when using async function
// * use partial option to update the partial value of the store
setStore(newValue: T | ((prev: T) => (T | Promise<T>)), cfg?: ISetStoreOptionsType): void | Promise<void>
// react hook to get the current state of the store.
useStore: () => Readonly<T>;
// react hook to select a part of the state.
useSelector: <R>(selector: (state: T) => R) => Readonly<R>;
}
function createStore<T>(initialState: T | (() => T), options?: ICreateStoreOptions<T>): IStore<T>;
[!WARNING] The store value is immutable(freezed by Object.freeze), do not mutate the store value directly or an error will be thrown.
// always use a new object to update the store value
store.setStore((prev) => ({ ...prev, newItem: 'xxx' }))
isDeepEqual(a, b)
Check if two values are deeply equal, can efficiently compare common data structures like objects, arrays, regexp, date and primitives.
import { isDeepEqual } from 'plain-store';
function isDeepEqual(a: any, b: any): boolean;
isPromiseLike(obj)
Check if a value is a promise
import { isPromiseLike } from 'plain-store';
function isPromiseLike(obj: any): boolean;
License
MIT