-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #92 from imarabinda/main
Add Support for Zustand 4.5.0+ and Introduce Mutative Middleware
- Loading branch information
Showing
38 changed files
with
1,188 additions
and
4,908 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
--- | ||
'zustand-x': major | ||
--- | ||
|
||
- Added support for Zustand 4.5.0+. | ||
- `mutative` support. Pass `mutative: true` in the options. | ||
|
||
## Migration Instructions | ||
|
||
Update the Store Initialization: | ||
|
||
1. Replace the old method of initializing the store with the new API. | ||
|
||
```tsx | ||
const store = createStore( | ||
() => ({ | ||
name: 'zustandX', | ||
stars: 0, | ||
}), | ||
{ | ||
name: 'repo', | ||
immer: true, | ||
} | ||
); | ||
``` | ||
|
||
or | ||
|
||
```tsx | ||
const store = createStore({ | ||
name: 'zustandX', | ||
stars: 0, | ||
}, | ||
{ | ||
name: 'repo', | ||
immer: true, | ||
} | ||
); | ||
``` | ||
|
||
2. Ensure to pass the configuration object with name and other options as needed. | ||
3. If your application relies on immer, enable it by passing immer: true in the configuration object. | ||
|
||
```tsx | ||
const store = createStore( | ||
() => ({ | ||
name: 'zustandX', | ||
stars: 0, | ||
}), | ||
{ | ||
name: 'repo', | ||
immer: true, | ||
} | ||
); | ||
``` | ||
|
||
4. With the new version, integrating middlewares has also changed. Here's how to upgrade your middleware usage: | ||
|
||
```tsx | ||
const store = createStore( | ||
middlewareWrapHere(() => ({ | ||
name: 'zustandX', | ||
stars: 0, | ||
})), | ||
{ | ||
name: 'repo', | ||
immer: true, | ||
} | ||
); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
> [!NOTE] | ||
> `@udecode/zustood` has been renamed to `zustand-x`. | ||
> [!NOTE] > `@udecode/zustood` has been renamed to `zustand-x`. | ||
> Using Jotai? See [JotaiX](https://github.com/udecode/jotai-x). | ||
# ZustandX | ||
|
@@ -21,12 +20,9 @@ code. | |
which solves these challenges, so you can focus on your app. | ||
|
||
```bash | ||
yarn add zustand@4.4.7 zustand-x | ||
yarn add zustand@latest zustand-x | ||
``` | ||
|
||
> [!IMPORTANT] | ||
> `zustand` 4.5.0+ is not yet supported. See https://github.com/udecode/zustand-x/issues/79. | ||
Visit [zustand-x.udecode.dev](https://zustand-x.udecode.dev) for the | ||
API. | ||
|
||
|
@@ -36,23 +32,28 @@ API. | |
- Modular state management: | ||
- Derived selectors | ||
- Derived actions | ||
- `immer`, `devtools` and `persist` middlewares | ||
- `immer`, `devtools` , `mutative` and `persist` middlewares | ||
- Full typescript support | ||
- `react-tracked` support | ||
|
||
## Create a store | ||
|
||
```ts | ||
import { createStore } from 'zustand-x' | ||
|
||
const repoStore = createStore('repo')({ | ||
name: 'zustandX', | ||
stars: 0, | ||
owner: { | ||
name: 'someone', | ||
email: '[email protected]', | ||
import { createStore } from 'zustand-x'; | ||
|
||
const repoStore = createStore( | ||
{ | ||
name: 'zustandX', | ||
stars: 0, | ||
owner: { | ||
name: 'someone', | ||
email: '[email protected]', | ||
}, | ||
}, | ||
}) | ||
{ | ||
name: 'repo', | ||
} | ||
); | ||
``` | ||
|
||
- the parameter of the first function is the name of the store, this is | ||
|
@@ -66,10 +67,10 @@ Note that the zustand store is accessible through: | |
|
||
```ts | ||
// hook store | ||
repoStore.useStore | ||
repoStore.useStore; | ||
|
||
// vanilla store | ||
repoStore.store | ||
repoStore.store; | ||
``` | ||
|
||
## Selectors | ||
|
@@ -80,8 +81,8 @@ Use the hooks in React components, no providers needed. Select your | |
state and the component will re-render on changes. Use the `use` method: | ||
|
||
```ts | ||
repoStore.use.name() | ||
repoStore.use.stars() | ||
repoStore.use.name(); | ||
repoStore.use.stars(); | ||
``` | ||
|
||
We recommend using the global hooks (see below) to support ESLint hook | ||
|
@@ -95,7 +96,7 @@ Use the tracked hooks in React components, no providers needed. Select your | |
state and the component will trigger re-renders only if the **accessed property** is changed. Use the `useTracked` method: | ||
|
||
```ts | ||
repoStore.useTracked.owner() | ||
repoStore.useTracked.owner(); | ||
``` | ||
|
||
### Getters | ||
|
@@ -104,14 +105,14 @@ Don't overuse hooks. If you don't need to subscribe to the state, use | |
instead the `get` method: | ||
|
||
```ts | ||
repoStore.get.name() | ||
repoStore.get.stars() | ||
repoStore.get.name(); | ||
repoStore.get.stars(); | ||
``` | ||
|
||
You can also get the whole state: | ||
|
||
```ts | ||
repoStore.get.state() | ||
repoStore.get.state(); | ||
``` | ||
|
||
### Extend selectors | ||
|
@@ -121,11 +122,16 @@ selectors) for reusability. ZustandX supports extending selectors with | |
full typescript support: | ||
|
||
```ts | ||
const repoStore = createStore('repo')({ | ||
name: 'zustandX', | ||
stars: 0, | ||
middlewares: ['immer', 'devtools', 'persist'] | ||
}) | ||
const repoStore = createStore( | ||
{ | ||
name: 'zustandX', | ||
stars: 0, | ||
middlewares: ['immer', 'devtools', 'persist'], | ||
}, | ||
{ | ||
name: 'repo', | ||
} | ||
) | ||
.extendSelectors((state, get, api) => ({ | ||
validName: () => get.name().trim(), | ||
// other selectors | ||
|
@@ -134,17 +140,17 @@ const repoStore = createStore('repo')({ | |
// get.validName is accessible | ||
title: (prefix: string) => | ||
`${prefix + get.validName()} with ${get.stars()} stars`, | ||
})) | ||
// extend again... | ||
})); | ||
// extend again... | ||
``` | ||
|
||
## Actions | ||
|
||
Update your store from anywhere by using the `set` method: | ||
|
||
```ts | ||
repoStore.set.name('new name') | ||
repoStore.set.stars(repoStore.get.stars + 1) | ||
repoStore.set.name('new name'); | ||
repoStore.set.stars(repoStore.get.stars + 1); | ||
``` | ||
|
||
### Extend actions | ||
|
@@ -155,17 +161,23 @@ You can update the whole state from your app: | |
store.set.state((draft) => { | ||
draft.name = 'test'; | ||
draft.stars = 1; | ||
return draft; | ||
}); | ||
``` | ||
|
||
However, you generally want to create derived actions for reusability. | ||
ZustandX supports extending actions with full typescript support: | ||
|
||
```ts | ||
const repoStore = createStore('repo')({ | ||
name: 'zustandX', | ||
stars: 0, | ||
}) | ||
const repoStore = createStore( | ||
{ | ||
name: 'zustandX', | ||
stars: 0, | ||
}, | ||
{ | ||
name: 'repo', | ||
} | ||
) | ||
.extendActions((set, get, api) => ({ | ||
validName: (name: string) => { | ||
set.name(name.trim()); | ||
|
@@ -178,8 +190,8 @@ const repoStore = createStore('repo')({ | |
set.validName(name); | ||
set.stars(0); | ||
}, | ||
})) | ||
// extend again... | ||
})); | ||
// extend again... | ||
``` | ||
|
||
## Global store | ||
|
@@ -218,14 +230,8 @@ export const actions = mapValuesKey('set', rootStore); | |
### Global hook selectors | ||
|
||
```ts | ||
import shallow from 'zustand/shallow' | ||
|
||
useStore().repo.name() | ||
useStore().modal.isOpen() | ||
|
||
// prevent unnecessary re-renders | ||
// more see: https://docs.pmnd.rs/zustand/recipes#selecting-multiple-state-slices | ||
useStore().repo.middlewares(shallow) | ||
useStore().repo.name(); | ||
useStore().modal.isOpen(); | ||
``` | ||
|
||
### Global tracked hook selectors | ||
|
@@ -258,17 +264,17 @@ By using `useStore() or useTrackStore()`, ESLint will correctly lint hook errors | |
### Global getter selectors | ||
|
||
```ts | ||
store.repo.name() | ||
store.modal.isOpen() | ||
store.repo.name(); | ||
store.modal.isOpen(); | ||
``` | ||
|
||
These can be used anywhere. | ||
|
||
### Global actions | ||
|
||
```ts | ||
actions.repo.stars(store.repo.stars + 1) | ||
actions.modal.open() | ||
actions.repo.stars(store.repo.stars + 1); | ||
actions.modal.open(); | ||
``` | ||
|
||
These can be used anywhere. | ||
|
@@ -278,24 +284,47 @@ These can be used anywhere. | |
The second parameter of `createStore` is for options: | ||
|
||
```ts | ||
export interface CreateStoreOptions<T extends State> { | ||
middlewares?: any[]; | ||
export interface CreateStoreOptions { | ||
name: string; | ||
devtools?: DevtoolsOptions; | ||
immer?: ImmerOptions; | ||
mutative?: MutativeOptions; | ||
persist?: PersistOptions; | ||
} | ||
``` | ||
|
||
### Middlewares | ||
|
||
ZustandX is using these middlewares: | ||
- `immer`: required. Autofreeze can be enabled using | ||
`immer.enabledAutoFreeze` option. | ||
- `devtools`: enabled if `devtools.enabled` option is `true`. | ||
|
||
- `immer`: enabled if `immer.enabled` option is `true`. `immer` implements from [zustand](https://github.com/pmndrs/zustand?tab=readme-ov-file#immer-middleware). | ||
- `devtools`: enabled if `devtools.enabled` option is `true`. `devtools` implements `DevtoolsOptions` interface from [zustand](https://github.com/pmndrs/zustand?tab=readme-ov-file#redux-devtools). | ||
- `mutative`: enabled if `mutative.enabled` option is `true`. | ||
- `persist`: enabled if `persist.enabled` option is `true`. `persist` | ||
implements `PersistOptions` interface from | ||
[zustand](https://github.com/pmndrs/zustand#persist-middleware) | ||
- custom middlewares can be added using `middlewares` option | ||
- custom middlewares can be added by wrapping `state initiator`. | ||
|
||
```ts | ||
import { createStore } from 'zustand-x'; | ||
import { combine } from 'zustand/middleware'; | ||
|
||
const store = createStore<{ name: string; stars?: number }>( | ||
combine( | ||
() => ({ | ||
name: 'zustandX', | ||
}), | ||
() => ({ stars: 0 }) | ||
), | ||
{ | ||
name: 'repo', | ||
//enables persist middleware | ||
persist: { | ||
enabled: true, | ||
}, | ||
} | ||
); | ||
``` | ||
|
||
## Contributing and project organization | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,21 +94,22 @@ | |
"prettier": "^3.1.0", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-is": "18.2.0", | ||
"react-test-renderer": "18.2.0", | ||
"rimraf": "^5.0.5", | ||
"ts-jest": "^29.1.1", | ||
"tsup": "8.0.1", | ||
"turbo": "^1.11.0", | ||
"turbowatch": "2.29.4", | ||
"typedoc": "^0.25.4", | ||
"typescript": "5.3.3", | ||
"zustand": "^4.4.7" | ||
"typescript": "5.3.3" | ||
}, | ||
"packageManager": "[email protected]", | ||
"engines": { | ||
"node": ">=18.12.0", | ||
"yarn": ">=1.22.0", | ||
"npm": "please-use-yarn" | ||
}, | ||
"dependencies": { | ||
"zustand": "5.0.2" | ||
} | ||
} |
Oops, something went wrong.