zustand-utils
Version:
some utils for zustand
298 lines (216 loc) • 8.14 kB
Markdown
<img src="https://gw.alipayobjects.com/zos/antfincdn/R8sN%24GNdh6/language.svg" width="18"> English | [简体中文](./README.zh-CN.md)
# zustand-utils
[![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url] [![install size][npm-size]][npm-size-url]
[![Test CI status][test-ci]][test-ci-url] [![Deploy CI][release-ci]][release-ci-url] [![Coverage][coverage]][codecov-url]
[![ docs by dumi][dumi-url]](https://d.umijs.org/) [![Build With father][father-url]](https://github.com/umijs/father/)
<!-- gitpod url -->
[gitpod-badge]: https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod
[gitpod-url]: https://gitpod.io/#https://github.com/arvinxx/zustand-utils
<!-- umi url -->
[dumi-url]: https://img.shields.io/badge/docs%20by-dumi-blue
[father-url]: https://img.shields.io/badge/build%20with-father-028fe4.svg
<!-- npm url -->
[npm-image]: http://img.shields.io/npm/v/zustand-utils.svg?style=flat-square&color=deepgreen&label=latest
[npm-url]: http://npmjs.org/package/zustand-utils
[npm-size]: https://img.shields.io/bundlephobia/minzip/zustand-utils?color=deepgreen&label=gizpped%20size&style=flat-square
[npm-size-url]: https://packagephobia.com/result?p=zustand-utils
<!-- coverage -->
[coverage]: https://codecov.io/gh/arvinxx/zustand-utils/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/arvinxx/zustand-utils/branch/master
<!-- Github CI -->
[test-ci]: https://github.com/arvinxx/zustand-utils/workflows/Test%20CI/badge.svg
[release-ci]: https://github.com/arvinxx/zustand-utils/workflows/Release%20CI/badge.svg
[test-ci-url]: https://github.com/arvinxx/zustand-utils/actions?query=workflow%3ATest%20CI
[release-ci-url]: https://github.com/arvinxx/zustand-utils/actions?query=workflow%3ARelease%20CI
[download-image]: https://img.shields.io/npm/dm/zustand-utils.svg?style=flat-square
[download-url]: https://npmjs.org/package/zustand-utils
## Introduction
Some utils for zustand
### createContext
A replacement createContext from zustand/context that is deprecated in v4 and will be removed in v5. (Discussion: [#1276](https://github.com/pmndrs/zustand/discussions/1276))
```tsx
import create from 'zustand'
import { createContext } from 'zustand-utils'
const { Provider, useStore } = createContext()
const createStore = () => create(...)
const App = () => (
<Provider createStore={createStore}>
...
</Provider>
)
const Component = () => {
const state = useStore()
const slice = useStore(selector)
...
```
### optionalDevtools
a wrapper for zustand devtools middleware that with config to enable devtools.(Discussion: [#1266](https://github.com/pmndrs/zustand/discussions/1266))
```ts
import { optionalDevtools } from 'zustand-utils';
type Store = {
foo: string;
};
export const createStore = (withDevtools?: boolean) => {
// can enable or controlled by config
const devtools = optionalDevtools(withDevtools);
// use as zustands/middleware devtools
return create<Store>()(devtools((set) => ({})));
};
```
What's improve?
`zustands/middleware`'s `devtools` have an option `enable`, but it doesn't work if set to false in development.
## Usage
### createContext
#### createContext usage in real components
> Migration from [zustand-v3-create-context.md](https://github.com/pmndrs/zustand/blob/b857d5e79f41e2e2c756448eca466ac31abdabc3/docs/previous-versions/zustand-v3-create-context.md)
```jsx
import create from "zustand";
import { createContext } from 'zustand-utils';
// Best practice: You can move the below createContext() and createStore to a separate file(store.js) and import the Provider, useStore here/wherever you need.
const { Provider, useStore } = createContext();
const createStore = () =>
create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 })
}));
const Button = () => {
return (
{/** store() - This will create a store for each time using the Button component instead of using one store for all components **/}
<Provider createStore={createStore}>
<ButtonChild />
</Provider>
);
};
const ButtonChild = () => {
const state = useStore();
return (
<div>
{state.bears}
<button
onClick={() => {
state.increasePopulation();
}}
>
+
</button>
</div>
);
};
export default function App() {
return (
<div className="App">
<Button />
<Button />
</div>
);
}
```
#### createContext usage with initialization from props
> Migration from [zustand-v3-create-context.md](https://github.com/pmndrs/zustand/blob/b857d5e79f41e2e2c756448eca466ac31abdabc3/docs/previous-versions/zustand-v3-create-context.md)
```tsx
import create from 'zustand';
import { createContext } from 'zustand-utils';
const { Provider, useStore } = createContext();
export default function App({ initialBears }) {
return (
<Provider
createStore={() =>
create((set) => ({
bears: initialBears,
increase: () => set((state) => ({ bears: state.bears + 1 })),
}))
}
>
<Button />
</Provider>
);
}
```
#### Refactor app store to a component store with createContext
a most usage of createContext is refactoring an app to a component. Here's progress:
1. Create an App without context :
```ts
// store.ts
import create from 'zustand';
export const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}));
```
components in app use `useStore` to consume store:
```tsx
// Component.ts
import { useStore } from './store';
const ButtonChild = () => {
const state = useStore();
return (
<div>
{state.bears}
<button
onClick={() => {
state.increasePopulation();
}}
>
+
</button>
</div>
);
};
export default ButtonChild;
```
2. Just wrapper the App with `createContext`, and don't need to refactor any code in children components.
```diff
// store.ts
import create from "zustand";
+ const createStore = ()=> create((set) => ({
- export const useStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 })
}));
+ const { Provider, useStore } = createContext();
+ export { Provider, useStore , createStore }
```
```tsx
// Wrapper.tsx
import { createStore, Provider } from './store';
const Wrapper = () => {
return (
<Provider createStore={createStore}>
<ButtonChild />
</Provider>
);
};
```
It become a component, can be used in any other app.
# createStoreUpdater
`createStoreUpdater` is a function for updating the value of a specified key in the Store.
## Parameters
`createStoreUpdater` takes a `StoreApi` object as its parameter, which contains methods for manipulating the Store, such as `getState`, `setState`, `subscribe`, and `destroy`.
`createStoreUpdater` returns a function that takes the following parameters:
- `key`: the key in the Store that needs to be updated;
- `value`: the value that needs to be updated;
- `deps`: an array of dependencies, defaults to `[value]`;
- `setStoreState`: an optional callback function for updating the Store state, defaults to `storeApi.setState`.
## Return Value
`createStoreUpdater` returns a function that updates the value of the specified key in the Store.
## Example
```typescript
import { createStoreUpdater } from 'path/to/createStoreUpdater';
import { useStore } from 'path/to/useStore';
interface User {
name: string;
age: number;
}
const storeApi = useStore<User>({ name: '', age: 0 });
const updateUser = createStoreUpdater(storeApi);
// Update name
updateUser('name', 'John Doe');
// Update age
updateUser('age', 18);
```
In the example above, we first create a Store using `useStore`, then create an updater `updateUser` using `createStoreUpdater`, and finally update the `name` and `age` in the Store by calling `updateUser`.
## License
[MIT](./LICENSE)