@zuze/stateful v4.1.2
@zuze/stateful
What is this?
It's a ridiculously tiny and highly performant state mangement solution when you don't want to implement redux. It's supposed to be minimalistic (comes in most handy for a source of state for library developers) and extremely simple. It comes with everything you need (including a super tiny memoized selector implementation) to maintain your state.
Getting Started
Install it as a dependency in your JavaScript/Typescript project
npm install @zuze/stateful
# or
yarn add @zuze/statefulOr just pull it in from the browser:
<script src="https://unpkg.com/@zuze/stateful"></script>
<script>
const { state } = stateful;
const myState = state('jim!');
myState.subscribe(console.log); // jim!
</script>API
state(initialState: T): Stateful<T>
Create a stateful instance with an initial state. Returns the stateful interface:
getState(): TReturns the current state.setState((state: T) => T): voidCan be used set state using a functionimport { state } from '@zuze/stateful'; const s = state({ fetching: false, error: false }); s.setState(state => ({ ...state, fetching: false, data: 'some data' })); // { fetching: false, error: false, data: 'some data' }import { state } from '@zuze/stateful'; const s = state({ fetching: false, error: false }); s.setState(state => ({ ...state, fetching: false, data: 'some data' })); // { fetching: false, error: false, data: 'some data' }subscribe(subscriberFunction: Subscriber<T>): UnsubscribeRegister a subscriber function to be notified every time the state changes (see selectors). Returns an unsubscribe function.const s = state('jim'); const unsub = s.subscribe(console.log); // logs jim s.setState(() => 'fred'); // logs fred unsub(); s.setState(() => 'bill'); // nothing logged
createSelector(...selectors, combiner)
The purpose of a selector (popularized in reselect) is to minimize expensive computations through memoization.
There is an alternate method for using selectors outside of minimizing expensive computations: because the combiner function only gets called when at least one of it's arguments change, it essentially becomes a callback for changes in the input selectors.
import { createSelector, state } from '@zuze/stateful';
const myFetchingSelector = createSelector(
({ fetching }) => fetching,
(fetching) => {
console.log("fetching changed",fetching);
}
);
const s = state({
fetching: false,
data: null,
error: true;
});
s.subscribe(myFetchingSelector); // logs "fetching changed",false
(async() => {selectors
s.setState(state => ({ ...state, fetching: true })); // logs "fetching changed",true
try {
const data = await someAPICall();
s.setState(state => ({ ...state, data })) // not called!
} catch {
s.setState((state => ({ ...state, error: true })) // not called!
}
s.setState((state => ({ ...state, fetching: false })); // logs "fetching changed",false
});
memo(fn)
Bare bones memoization implementation. You aren't allowed to mess with the comparator
import { memo } from '@zuze/stateful';
const myFunc = (...someArgs) => {
// ... some expensive computations
return 42;
};
const memoed = memo(myFunc);
console.log(memoed(...someArgs)); // outputs 42 - expensive computations performed
console.log(memoed(...someArgs)); // outputs 42 - expensive computations skipped!About Selector Memoization
There are 2 levels of memoizations going on when creating a selector.
- The function returned from
createSelectoritself is memoized usingchecker - The
combinerfunction is memoized usingchecker.
What this effectively means is:
- If the function returns from
createSelectoris called with the same arguments neither the input selectors nor the combiner will be called - If the function returned from
createSelectoris called with different arguments, all input selectors will be called. If these executions result in the same arguments as the last time, thecombinerwill NOT be called.
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
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago