Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/infinitexyy/zoov
Use 🐻 Zustand with Module-like api
https://github.com/infinitexyy/zoov
hooks state-management zustand
Last synced: 5 days ago
JSON representation
Use 🐻 Zustand with Module-like api
- Host: GitHub
- URL: https://github.com/infinitexyy/zoov
- Owner: InfiniteXyy
- License: mit
- Created: 2020-12-30T15:18:12.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2024-11-10T02:30:32.000Z (about 2 months ago)
- Last Synced: 2024-12-21T17:07:15.874Z (12 days ago)
- Topics: hooks, state-management, zustand
- Language: TypeScript
- Homepage: https://stackblitz.com/edit/vitejs-vite-mgdqal
- Size: 734 KB
- Stars: 69
- Watchers: 4
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
ZOOV
✨ ZOOV = Zustand + module
## Features
- 😌 Easy: Comfortable type inference
- ✨ Magic: Update state by just mutate it (with support of immer)
- 🍳 Tiny: < 200 line code based on Zustand
- 🧮 Powerful: Modular state management (Redux-like)
- 📖 Smart: Scope supported with Algebraic Effects
- 📦 Flexible: Attach state/actions inside or outside React## Quick Start
You can try it on [StackBlitz](https://stackblitz.com/edit/vitejs-vite-mgdqal) or [CodeSandbox](https://codesandbox.io/s/zoov-example-9q0eb5)
Or install locally
```sh
yarn add immer zustand # peer dependencies
yarn add zoov
```## First Glance
```typescript jsx
const { use: useCounter } = defineModule({ count: 0 })
.actions({
add: (draft) => draft.count++,
minus: (draft) => draft.count--,
})
.computed({
doubled: (state) => state.count * 2,
})
.build();const App = () => {
const [{ count }, { add }] = useCounter();
return {count};
};// state is shared
const App2 = () => {
const [, , { doubled }] = useCounter();
returndoubled: {doubled};
};
```## More Examples
### Use Methods
```typescript jsx
import { effect } from 'zoov/effect';const counterModule = defineModule({ count: 0 })
.actions({
add: (draft) => draft.count++,
minus: (draft) => draft.count--,
})
.methods(({ getActions }) => {
return {
addAndMinus: () => {
getActions().add();
getActions().add();
setTimeout(() => getActions().minus(), 100);
},
// async function is supported
asyncAdd: async () => {
await something();
getActions().add();
},
// [TIPS] If you want to `rxjs` in `zoov`, your should first install `rxjs`
addAfter: effect((payload$) =>
payload$.pipe(
exhaustMap((timeout) => {
return timer(timeout).pipe(tap(() => getActions().add()));
}),
),
),
};
})
// using `this` is allowed now! remember to set `noImplicitThis` true in tsconfig
.methods({
addTwo() {
this.getActions().add();
this.getActions().add();
},
})
.build();
```### Use Selector
```typescript jsx
const { use: useCounter } = defineModule({ count: 0, input: 'hello' })
.actions({
add: (draft) => draft.count++,
setInput: (draft, value: string) => (draft.input = value),
})
.build();const App = () => {
// will not rerender unless "count" changes
const [count] = useCounter((state) => state.count);
return {count};
};
```Additionally, you can install [react-tracked](https://github.com/dai-shi/react-tracked) and use `useTrackedModule` to automatically generate selector
```tsx
// will not rerender unless "count" changes
const [{ count }, { add }] = useTrackedModule(module);
```### Use subscriptions
```typescript
const module = defineModule({ pokemonIndex: 0, input: '' })
.subscribe((state, prevState) => console.log(state)) // subscribe to the whole store
.subscribe({
selector: (state) => state.pokemonIndex, // only subscribe to some property
listener: async (pokemonIndex, prev, { addCleanup }) => {
const abortController = new AbortController();
const abortSignal = abortController.signal;
addCleanup(() => abortController.abort());
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonIndex}`, { signal: abortSignal });
console.log(await response.json());
},
})
.build();
```### Use Middleware
```typescript jsx
// 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 internal Actions
```typescript jsx
// a lite copy of solid-js/store, with strict type check
const { useActions } = defineModule({ count: 0, nested: { checked: boolean } }).build();const { $setState, $reset } = useActions();
$setState('count', 1);
$setState('nested', 'checked', (v) => !v);
$reset();
```### Use Provider
```typescript jsx
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 (
);
};
```### Attach state outside components
```typescript jsx
// by default, it will get the state under global scope
const actions = module.getActions();
const state = module.getState();// you can specify the scope with params
const context = useScopeContext();
const scopeActions = module.getActions(context);
```