storexstate
v0.1.5
Published
small global state management library based on xstate and inspired by @reduxjs/toolkit
Downloads
1
Readme
storexstate
small global state management library based on xstate and inspired by @reduxjs/toolkit
npm install storexstate
Usage
Setting up your store
import {
createSpawnEvent,
createSlice,
createStore,
createSelector,
} from "storexstate";
import { fromPromise } from "xstate";
// create spawn events if you need async logic
const asyncIncrement = createSpawnEvent<number>(
"asyncIncrement",
fromPromise(({ input }) => wait(0).then(() => input))
);
// build slices
const counterSlice = createSlice({
name: "counter",
initialState: { count: 0, loading: false, error: false },
transitions: {
incrementByOne: (state) => {
state.count += 1;
},
incrementBy: (state, action: { payload: number }) => {
state.count += action.payload;
},
[asyncIncrement.init]: (state) => {
state.loading = true;
state.error = false;
},
[asyncIncrement.done]: (state, action: DoneActorEvent<number>) => {
state.count += action.output;
state.loading = false;
},
[asyncIncrement.error]: (state) => {
state.error = true;
state.loading = false;
},
},
});
// add them slices to your store
const store = createStore({
counter: counterSlice.transition,
});
// create selectors
const countSelector = createSelector(
// select slice ref from store
(root: SnapshotFrom<typeof store>) => root.context.slices.counter,
// select anything from slice's snapshot
(counter) => counter.context.count
);
Vanilla usage
import { createActor } from "xstate";
const actor = createActor(store);
actor.start();
actor.send(counterSlice.actions.incrementByOne());
actor.send(counterSlice.actions.incrementBy(1));
actor.send(asyncIncrement(1));
const count = countSelector(actor.getSnapshot());
With React
import { StoreProvider, useDispatch, useReselector } from "storexstate/react";
function App() {
return (
<StoreProvider store={store}>
<Counter />
</StoreProvider>
);
}
function Counter() {
const dispatch = useDispatch();
const count = useReselector(countSelector);
return (
<button onClick={() => dispatch(counterSlice.actions.increment())}>
{count}
</button>
);
}
Using createMachine instead of createSlice
import { createMachine } from "xstate";
// any actor logic that can receive events
const counter = createMachine({
context: {
count: 0,
},
on: {
increment: {
actions: assign({
count: ({ context }) => context.count + 1,
}),
},
},
});
const store = createStore({
counter,
});
Using createMachine for spawn events
import { createMachine } from "xstate";
const asyncIncrement = createSpawnEvent<number>(
"asyncIncrement",
// any actor logic with input, output, and reaches final state by itself
createMachine({
context: ({ input }) => ({
input,
}),
after: {
0: {
target: ".done",
},
},
initial: "init",
states: {
init: {},
done: {
type: "final",
},
},
output: ({ context }) => context.input,
})
);