README
ZOOV
✨ ZOOV = Zustand + Module
Features
- 😌 Comfortable type inference
- ✨ Immer in the first class support
- 🍳 150 line code based on Zustand
- 🧮 Modular state management (Redux-like)
- 📖 Scope supported with Algebraic Effects
Quick Start
You can try it on StackBlitz or CodeSandbox
Or install locally
yarn add immer zustand # peer dependencies
yarn add zoov
First Glance
const CounterModule = defineModule({ count: 0 })
.actions({
add: (draft) => draft.count++,
minus: (draft) => draft.count--,
})
.computed({
doubled: (state) => state.count * 2,
})
.build();
const App = () => {
const [{ count }, { add }] = CounterModule.use();
return <button onClick={add}>{count}</button>;
};
// state is shared
const App2 = () => {
const { doubled } = CounterModule.useComputed();
return <div>doubled: {doubled}</div>;
};
More Examples
Use Methods
const CounterModule = defineModule({ count: 0 })
.actions({
add: (draft) => draft.count++,
minus: (draft) => draft.count--,
})
.computed({
doubled: (state) => state.count * 2,
})
.methods(({ getActions }) => {
const { add, minus } = getActions();
return {
addAndMinus: () => {
add();
add();
setTimeout(() => minus(), 100);
},
// async function is supported
asyncAdd: async () => {
await something()
add()
},
// !rxjs is required if you want to use effect, import from 'zoov/utils'
addAfter: effect<number>((payload$) =>
payload$.pipe(
exhaustMap((timeout) => {
return timer(timeout).pipe(tap(() => add()));
})
)
),
};
})
.build();
Use Selector
const CounterModule = defineModule({ count: 0, input: 'hello' })
.actions({
add: (draft) => draft.count++,
setInput: (draft, value: string) => (draft.input = value),
})
.build();
const App = () => {
// <App /> will not rerender unless "count" changes
const [count] = CounterModule.use((state) => state.count);
return <span>{count}</span>;
};
Use Middleware
// see more examples in https://github.com/pmndrs/zustand/blob/master/src/middleware.ts
const Module = defineModule({ count: 0 })
.actions({ add: (draft) => draft.count++ })
.middleware((store) => persist(store, { name: 'counter' }))
.build();
Use default setState Action
// a lite copy of solid-js/store, with strict type check
const Module = defineModule({ count: 0, nested: { checked: boolean } }).build();
const [{ setState }] = Module.useActions()
setState('count', 1)
setState('nested', 'checked', v => !v)
Use Provider
import { defineProvider } from 'zoov';
const CustomProvider = defineProvider((handle) => {
// create a new Module scope for all its children(can be nested)
handle(YourModule, {
defaultState: {},
});
handle(AnotherModule, {
defaultState: {},
});
});
const App = () => {
// if a Module is not handled by any of its parent, then used global scope
return (
<div>
<CustomProvider>
<Component />
</CustomProvider>
<Component />
</div>
);
};