fix(docs): useBoundStore instead of useStore (#1125)

* fix(docs): useBoundStore instead of useStore

* run prettier
This commit is contained in:
Daishi Kato 2022-07-26 08:58:43 +09:00 committed by GitHub
parent f07276c3f5
commit fa581ddd9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 418 additions and 410 deletions

View File

@ -39,7 +39,7 @@ interface BearState {
increment: () => void increment: () => void
} }
const useStoreBase = create<BearState>()((set) => ({ const useBearStoreBase = create<BearState>()((set) => ({
bears: 0, bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })), increase: (by) => set((state) => ({ bears: state.bears + by })),
increment: () => set((state) => ({ bears: state.bears + 1 })), increment: () => set((state) => ({ bears: state.bears + 1 })),
@ -49,17 +49,17 @@ const useStoreBase = create<BearState>()((set) => ({
## Apply that function to your store: ## Apply that function to your store:
```typescript ```typescript
const useStore = createSelectors(useStoreBase) const useBearStore = createSelectors(useBearStoreBase)
``` ```
## Now the selectors are auto generated: ## Now the selectors are auto generated:
```typescript ```typescript
// get the property // get the property
const bears = useStore.use.bears() const bears = useBearStore.use.bears()
// get the action // get the action
const increase = useStore.use.increment() const increase = useBearStore.use.increment()
``` ```
## Live Demo ## Live Demo

View File

@ -6,14 +6,14 @@ In order to fix this, the action needs to be wrapped in `unstable_batchedUpdates
```jsx ```jsx
import { unstable_batchedUpdates } from 'react-dom' // or 'react-native' import { unstable_batchedUpdates } from 'react-dom' // or 'react-native'
const useStore = create((set) => ({ const useFishStore = create((set) => ({
fishes: 0, fishes: 0,
increaseFishes: () => set((prev) => ({ fishes: prev.fishes + 1 })), increaseFishes: () => set((prev) => ({ fishes: prev.fishes + 1 })),
})) }))
const nonReactCallback = () => { const nonReactCallback = () => {
unstable_batchedUpdates(() => { unstable_batchedUpdates(() => {
useStore.getState().increaseFishes() useFishStore.getState().increaseFishes()
}) })
} }
``` ```

View File

@ -7,7 +7,7 @@ Although zustand is an unopinionated library, here's one of the recommended usag
- Define dispatch functions at the root level of the store to update one or more store slices - Define dispatch functions at the root level of the store to update one or more store slices
```js ```js
const useStore = create((set) => ({ const useBoundStore = create((set) => ({
storeSliceA: ..., storeSliceA: ...,
storeSliceB: ..., storeSliceB: ...,
storeSliceC: ..., storeSliceC: ...,
@ -34,12 +34,12 @@ const reducer = (state, { type, by = 1 }) => {
} }
} }
const useStore = create((set) => ({ const useGrumpyStore = create((set) => ({
grumpiness: 0, grumpiness: 0,
dispatch: (args) => set((state) => reducer(state, args)), dispatch: (args) => set((state) => reducer(state, args)),
})) }))
const dispatch = useStore((state) => state.dispatch) const dispatch = useGrumpyStore((state) => state.dispatch)
dispatch({ type: types.increase, by: 2 }) dispatch({ type: types.increase, by: 2 })
``` ```
@ -48,7 +48,7 @@ Or, just use our redux-middleware. It wires up your main-reducer, sets initial s
```typescript ```typescript
import { redux } from 'zustand/middleware' import { redux } from 'zustand/middleware'
const useStore = create(redux(reducer, initialState)) const useReduxStore = create(redux(reducer, initialState))
``` ```
Another way to update the store could be in functions wrapping the state functions. These could also handle side-effects of actions, for example for HTTP-calls. For using Zustand in a none-reactive way see [the readme](https://github.com/pmndrs/zustand#readingwriting-state-and-reacting-to-changes-outside-of-components) Another way to update the store could be in functions wrapping the state functions. These could also handle side-effects of actions, for example for HTTP-calls. For using Zustand in a none-reactive way see [the readme](https://github.com/pmndrs/zustand#readingwriting-state-and-reacting-to-changes-outside-of-components)

View File

@ -11,7 +11,7 @@ Quick example:
import create from 'zustand' import create from 'zustand'
import { persist } from 'zustand/middleware' import { persist } from 'zustand/middleware'
export const useStore = create( export const useFishStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
fishes: 0, fishes: 0,
@ -44,7 +44,7 @@ Simply pass a function that returns the storage you want to use.
Example: Example:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
// ... // ...
@ -78,7 +78,7 @@ Since the only way to store an object in a storage is via a string, you can use
For example, if you want to store your state in base64: For example, if you want to store your state in base64:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
// ... // ...
@ -104,7 +104,7 @@ If you pass a custom serialize function, you will most likely need to pass a cus
To continue the example above, you could deserialize the base64 value using the following: To continue the example above, you could deserialize the base64 value using the following:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
// ... // ...
@ -128,7 +128,7 @@ Enables you to omit some of the state's fields to be stored in the storage.
You could omit multiple fields using the following: You could omit multiple fields using the following:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
foo: 0, foo: 0,
@ -148,7 +148,7 @@ export const useStore = create(
Or you could allow only specific fields using the following: Or you could allow only specific fields using the following:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
foo: 0, foo: 0,
@ -171,7 +171,7 @@ This option enables you to pass a listener function that will be called when the
Example: Example:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
// ... // ...
@ -218,7 +218,7 @@ The migrate function takes the persisted state and the version number as argumen
For instance, if you want to rename a field, you can use the following: For instance, if you want to rename a field, you can use the following:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
newField: 0, // let's say this field was named otherwise in version 0 newField: 0, // let's say this field was named otherwise in version 0
@ -275,7 +275,7 @@ The shallow merge will erase the `baz` field from the `foo` object.
One way to fix this would be to give a custom deep merge function: One way to fix this would be to give a custom deep merge function:
```ts ```ts
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
foo: { foo: {
@ -307,7 +307,7 @@ This method can you get the options of the middleware.
For example, it can be used to obtain the storage name: For example, it can be used to obtain the storage name:
```ts ```ts
useStore.persist.getOptions().name useBoundStore.persist.getOptions().name
``` ```
### `setOptions` ### `setOptions`
@ -319,7 +319,7 @@ This method enables you to change the middleware options. Note that the new opti
For instance, this can be used to change the storage name: For instance, this can be used to change the storage name:
```ts ```ts
useStore.persist.setOptions({ useBoundStore.persist.setOptions({
name: 'new-name', name: 'new-name',
}) })
``` ```
@ -327,7 +327,7 @@ useStore.persist.setOptions({
Or even to change the storage engine: Or even to change the storage engine:
```ts ```ts
useStore.persist.setOptions({ useBoundStore.persist.setOptions({
getStorage: () => sessionStorage, getStorage: () => sessionStorage,
}) })
``` ```
@ -339,7 +339,7 @@ useStore.persist.setOptions({
This can be used to fully clear the persisted value in the storage. This can be used to fully clear the persisted value in the storage.
```ts ```ts
useStore.persist.clearStorage() useBoundStore.persist.clearStorage()
``` ```
### `rehydrate` ### `rehydrate`
@ -350,17 +350,17 @@ In some cases, you might want to trigger a rehydration manually.
This can be done by calling the `rehydrate` method. This can be done by calling the `rehydrate` method.
```ts ```ts
await useStore.persist.rehydrate() await useBoundStore.persist.rehydrate()
``` ```
### `hasHydrated` ### `hasHydrated`
> Schema: `() => boolean` > Schema: `() => boolean`
This is a non-reactive getter to know if the storage has been hydrated (note that this does update when calling `useStore.persist.rehydrate()`). This is a non-reactive getter to know if the storage has been hydrated (note that this does update when calling `useBoundStore.persist.rehydrate()`).
```ts ```ts
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
``` ```
### `onHydrate` ### `onHydrate`
@ -370,7 +370,7 @@ useStore.persist.hasHydrated()
The given listener will be called when the hydration process starts. The given listener will be called when the hydration process starts.
```ts ```ts
const unsub = useStore.persist.onHydrate((state) => { const unsub = useBoundStore.persist.onHydrate((state) => {
console.log('hydration starts') console.log('hydration starts')
}) })
@ -385,7 +385,7 @@ unsub()
The given listener will be called when the hydration process ends. The given listener will be called when the hydration process ends.
```ts ```ts
const unsub = useStore.persist.onFinishHydration((state) => { const unsub = useBoundStore.persist.onFinishHydration((state) => {
console.log('hydration finished') console.log('hydration finished')
}) })
@ -420,7 +420,7 @@ There's a fiew different ways to do this.
You can use the `onRehydrateStorage` option to update a field in the store: You can use the `onRehydrateStorage` option to update a field in the store:
```ts ```ts
const useStore = create( const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
// ... // ...
@ -441,7 +441,7 @@ const useStore = create(
); );
export default function App() { export default function App() {
const hasHydrated = useStore(state => state._hasHydrated); const hasHydrated = useBoundStore(state => state._hasHydrated);
if (!hasHydrated) { if (!hasHydrated) {
return <p>Loading...</p> return <p>Loading...</p>
@ -456,16 +456,16 @@ export default function App() {
You can also create a custom `useHydration` hook: You can also create a custom `useHydration` hook:
```ts ```ts
const useStore = create(persist(...)) const useBoundStore = create(persist(...))
const useHydration = () => { const useHydration = () => {
const [hydrated, setHydrated] = useState(useStore.persist.hasHydrated) const [hydrated, setHydrated] = useState(useBoundStore.persist.hasHydrated)
useEffect(() => { useEffect(() => {
const unsubHydrate = useStore.persist.onHydrate(() => setHydrated(false)) // Note: this is just in case you want to take into account manual rehydrations. You can remove this if you don't need it/don't want it. const unsubHydrate = useBoundStore.persist.onHydrate(() => setHydrated(false)) // Note: this is just in case you want to take into account manual rehydrations. You can remove this if you don't need it/don't want it.
const unsubFinishHydration = useStore.persist.onFinishHydration(() => setHydrated(true)) const unsubFinishHydration = useBoundStore.persist.onFinishHydration(() => setHydrated(true))
setHydrated(useStore.persist.hasHydrated()) setHydrated(useBoundStore.persist.hasHydrated())
return () => { return () => {
unsubHydrate() unsubHydrate()
@ -502,7 +502,7 @@ const storage: StateStorage = {
}, },
} }
export const useStore = create( export const useBoundStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
fishes: 0, fishes: 0,
@ -538,6 +538,6 @@ export const withStorageDOMEvents = (store: StoreWithPersist) => {
} }
} }
const useStore = create(persist(...)) const useBoundStore = create(persist(...))
withStorageDOMEvents(useStore) withStorageDOMEvents(useBoundStore)
``` ```

View File

@ -12,7 +12,7 @@ interface BearState {
increase: (by: number) => void increase: (by: number) => void
} }
const useStore = create<BearState>()((set) => ({ const useBearStore = create<BearState>()((set) => ({
bears: 0, bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })), increase: (by) => set((state) => ({ bears: state.bears + by })),
})) }))
@ -59,7 +59,7 @@ The thing is Zustand is lying in it's type, the simplest way to prove it by show
```ts ```ts
import create from 'zustand/vanilla' import create from 'zustand/vanilla'
const useStore = create<{ foo: number }>()((_, get) => ({ const useBoundStore = create<{ foo: number }>()((_, get) => ({
foo: get().foo, foo: get().foo,
})) }))
``` ```
@ -121,7 +121,7 @@ Alternatively you can also use `combine` which infers the state instead of you h
import create from 'zustand' import create from 'zustand'
import { combine } from 'zustand/middleware' import { combine } from 'zustand/middleware'
const useStore = create( const useBearStore = create(
combine({ bears: 0 }, (set) => ({ combine({ bears: 0 }, (set) => ({
increase: (by: number) => set((state) => ({ bears: state.bears + by })), increase: (by: number) => set((state) => ({ bears: state.bears + by })),
})) }))
@ -133,9 +133,9 @@ const useStore = create(
<br/> <br/>
We achieve the inference by lying a little in the types of `set`, `get` and `store` that you receive as parameters. The lie is that they're typed in a way as if the state is the first parameter only when in fact the state is the shallow-merge (`{ ...a, ...b }`) of both first parameter and the second parameter's return. So for example `get` from the second parameter has type `() => { bears: number }` and that's a lie as it should be `() => { bears: number, increase: (by: number) => void }`. And `useStore` still has the correct type, ie for example `useStore.getState` is typed as `() => { bears: number, increase: (by: number) => void }`. We achieve the inference by lying a little in the types of `set`, `get` and `store` that you receive as parameters. The lie is that they're typed in a way as if the state is the first parameter only when in fact the state is the shallow-merge (`{ ...a, ...b }`) of both first parameter and the second parameter's return. So for example `get` from the second parameter has type `() => { bears: number }` and that's a lie as it should be `() => { bears: number, increase: (by: number) => void }`. And `useBoundStore` still has the correct type, ie for example `useBoundStore.getState` is typed as `() => { bears: number, increase: (by: number) => void }`.
It's not a lie lie because `{ bears: number }` is still a subtype `{ bears: number, increase: (by: number) => void }`, so in most cases there won't be a problem. Just you have to be careful while using replace. For eg `set({ bears: 0 }, true)` would compile but will be unsound as it'll delete the `increase` function. (If you set from "outside" ie `useStore.setState({ bears: 0 }, true)` then it won't compile because the "outside" store knows that `increase` is missing.) Another instance where you should be careful you're doing `Object.keys`, `Object.keys(get())` will return `["bears", "increase"]` and not `["bears"]` (the return type of `get` can make you fall for this). It's not a lie lie because `{ bears: number }` is still a subtype `{ bears: number, increase: (by: number) => void }`, so in most cases there won't be a problem. Just you have to be careful while using replace. For eg `set({ bears: 0 }, true)` would compile but will be unsound as it'll delete the `increase` function. (If you set from "outside" ie `useBoundStore.setState({ bears: 0 }, true)` then it won't compile because the "outside" store knows that `increase` is missing.) Another instance where you should be careful you're doing `Object.keys`, `Object.keys(get())` will return `["bears", "increase"]` and not `["bears"]` (the return type of `get` can make you fall for this).
So `combine` trades-off a little type-safety for the convenience of not having to write a type for state. Hence you should use `combine` accordingly, usually it's not a big deal and it's okay to use it. So `combine` trades-off a little type-safety for the convenience of not having to write a type for state. Hence you should use `combine` accordingly, usually it's not a big deal and it's okay to use it.
@ -156,7 +156,7 @@ interface BearState {
increase: (by: number) => void increase: (by: number) => void
} }
const useStore = create<BearState>()( const useBearStore = create<BearState>()(
devtools( devtools(
persist((set) => ({ persist((set) => ({
bears: 0, bears: 0,
@ -179,7 +179,7 @@ interface BearState {
increase: (by: number) => void increase: (by: number) => void
} }
const useStore = create<BearState>()( const useBearStore = create<BearState>()(
myMiddlewares((set) => ({ myMiddlewares((set) => ({
bears: 0, bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })), increase: (by) => set((state) => ({ bears: state.bears + by })),
@ -199,8 +199,8 @@ const foo = (f, bar) => (set, get, store) => {
return f(set, get, store) return f(set, get, store)
} }
const useStore = create(foo(() => ({ bears: 0 }), 'hello')) const useBearStore = create(foo(() => ({ bears: 0 }), 'hello'))
console.log(useStore.foo.toUpperCase()) console.log(useBearStore.foo.toUpperCase())
``` ```
Yes, if you didn't know Zustand middlewares do and are allowed to mutate the store. But how could we possibly encode the mutation on the type-level? That is to say how could do we type `foo` so that this code compiles? Yes, if you didn't know Zustand middlewares do and are allowed to mutate the store. But how could we possibly encode the mutation on the type-level? That is to say how could do we type `foo` so that this code compiles?
@ -251,7 +251,7 @@ type PopArgument<T extends (...a: never[]) => unknown> = T extends (
// --- // ---
const useStore = create<BearState>()( const useBearStore = create<BearState>()(
logger( logger(
(set) => ({ (set) => ({
bears: 0, bears: 0,
@ -317,8 +317,8 @@ type Cast<T, U> = T extends U ? T : U
// --- // ---
const useStore = create(foo(() => ({ bears: 0 }), 'hello')) const useBearStore = create(foo(() => ({ bears: 0 }), 'hello'))
console.log(useStore.foo.toUpperCase()) console.log(useBearStore.foo.toUpperCase())
``` ```
### `create` without curried workaround ### `create` without curried workaround
@ -333,7 +333,7 @@ interface BearState {
increase: (by: number) => void increase: (by: number) => void
} }
const useStore = create< const useBearStore = create<
BearState, BearState,
[ [
['zustand/persist', BearState], ['zustand/persist', BearState],
@ -380,7 +380,7 @@ const createFishSlice: StateCreator<
addFish: () => set((state) => ({ fishes: state.fishes + 1 })), addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
}) })
const useStore = create<BearSlice & FishSlice>()((...a) => ({ const useBoundStore = create<BearSlice & FishSlice>()((...a) => ({
...createBearSlice(...a), ...createBearSlice(...a),
...createFishSlice(...a), ...createFishSlice(...a),
})) }))

View File

@ -27,7 +27,7 @@ Your store is a hook! You can put anything in it: primitives, objects, functions
```jsx ```jsx
import create from 'zustand' import create from 'zustand'
const useStore = create((set) => ({ const useBearStore = create((set) => ({
bears: 0, bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }), removeAllBears: () => set({ bears: 0 }),
@ -40,12 +40,12 @@ Use the hook anywhere, no providers needed. Select your state and the component
```jsx ```jsx
function BearCounter() { function BearCounter() {
const bears = useStore((state) => state.bears) const bears = useBearStore((state) => state.bears)
return <h1>{bears} around here ...</h1> return <h1>{bears} around here ...</h1>
} }
function Controls() { function Controls() {
const increasePopulation = useStore((state) => state.increasePopulation) const increasePopulation = useBearStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button> return <button onClick={increasePopulation}>one up</button>
} }
``` ```
@ -72,7 +72,7 @@ function Controls() {
You can, but bear in mind that it will cause the component to update on every state change! You can, but bear in mind that it will cause the component to update on every state change!
```jsx ```jsx
const state = useStore() const state = useBearStore()
``` ```
## Selecting multiple state slices ## Selecting multiple state slices
@ -80,8 +80,8 @@ const state = useStore()
It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks. It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks.
```jsx ```jsx
const nuts = useStore((state) => state.nuts) const nuts = useBearStore((state) => state.nuts)
const honey = useStore((state) => state.honey) const honey = useBearStore((state) => state.honey)
``` ```
If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function. If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function.
@ -90,22 +90,25 @@ If you want to construct a single object with multiple state-picks inside, simil
import shallow from 'zustand/shallow' import shallow from 'zustand/shallow'
// Object pick, re-renders the component when either state.nuts or state.honey change // Object pick, re-renders the component when either state.nuts or state.honey change
const { nuts, honey } = useStore( const { nuts, honey } = useBearStore(
(state) => ({ nuts: state.nuts, honey: state.honey }), (state) => ({ nuts: state.nuts, honey: state.honey }),
shallow shallow
) )
// Array pick, re-renders the component when either state.nuts or state.honey change // Array pick, re-renders the component when either state.nuts or state.honey change
const [nuts, honey] = useStore((state) => [state.nuts, state.honey], shallow) const [nuts, honey] = useBearStore(
(state) => [state.nuts, state.honey],
shallow
)
// Mapped picks, re-renders the component when state.treats changes in order, count or keys // Mapped picks, re-renders the component when state.treats changes in order, count or keys
const treats = useStore((state) => Object.keys(state.treats), shallow) const treats = useBearStore((state) => Object.keys(state.treats), shallow)
``` ```
For more control over re-rendering, you may provide any custom equality function. For more control over re-rendering, you may provide any custom equality function.
```jsx ```jsx
const treats = useStore( const treats = useBearStore(
(state) => state.treats, (state) => state.treats,
(oldTreats, newTreats) => compare(oldTreats, newTreats) (oldTreats, newTreats) => compare(oldTreats, newTreats)
) )
@ -118,7 +121,7 @@ The `set` function has a second argument, `false` by default. Instead of merging
```jsx ```jsx
import omit from 'lodash-es/omit' import omit from 'lodash-es/omit'
const useStore = create((set) => ({ const useFishStore = create((set) => ({
salmon: 1, salmon: 1,
tuna: 2, tuna: 2,
deleteEverything: () => set({}, true), // clears the entire store, actions included deleteEverything: () => set({}, true), // clears the entire store, actions included
@ -131,7 +134,7 @@ const useStore = create((set) => ({
Just call `set` when you're ready, zustand doesn't care if your actions are async or not. Just call `set` when you're ready, zustand doesn't care if your actions are async or not.
```jsx ```jsx
const useStore = create((set) => ({ const useFishStore = create((set) => ({
fishies: {}, fishies: {},
fetch: async (pond) => { fetch: async (pond) => {
const response = await fetch(pond) const response = await fetch(pond)
@ -145,7 +148,7 @@ const useStore = create((set) => ({
`set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`. `set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`.
```jsx ```jsx
const useStore = create((set, get) => ({ const useSoundStore = create((set, get) => ({
sound: "grunt", sound: "grunt",
action: () => { action: () => {
const sound = get().sound const sound = get().sound
@ -159,22 +162,22 @@ const useStore = create((set, get) => ({
Sometimes you need to access state in a non-reactive way, or act upon the store. For these cases the resulting hook has utility functions attached to its prototype. Sometimes you need to access state in a non-reactive way, or act upon the store. For these cases the resulting hook has utility functions attached to its prototype.
```jsx ```jsx
const useStore = create(() => ({ paw: true, snout: true, fur: true })) const useDogStore = create(() => ({ paw: true, snout: true, fur: true }))
// Getting non-reactive fresh state // Getting non-reactive fresh state
const paw = useStore.getState().paw const paw = useDogStore.getState().paw
// Listening to all changes, fires synchronously on every change // Listening to all changes, fires synchronously on every change
const unsub1 = useStore.subscribe(console.log) const unsub1 = useDogStore.subscribe(console.log)
// Updating state, will trigger listeners // Updating state, will trigger listeners
useStore.setState({ paw: false }) useDogStore.setState({ paw: false })
// Unsubscribe listeners // Unsubscribe listeners
unsub1() unsub1()
// Destroying the store (removing all listeners) // Destroying the store (removing all listeners)
useStore.destroy() useDogStore.destroy()
// You can of course use the hook as you always would // You can of course use the hook as you always would
const Component = () => { const Component = () => {
const paw = useStore((state) => state.paw) const paw = useDogStore((state) => state.paw)
... ...
``` ```
@ -191,25 +194,25 @@ subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubs
```js ```js
import { subscribeWithSelector } from 'zustand/middleware' import { subscribeWithSelector } from 'zustand/middleware'
const useStore = create( const useDogStore = create(
subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })) subscribeWithSelector(() => ({ paw: true, snout: true, fur: true }))
) )
// Listening to selected changes, in this case when "paw" changes // Listening to selected changes, in this case when "paw" changes
const unsub2 = useStore.subscribe((state) => state.paw, console.log) const unsub2 = useDogStore.subscribe((state) => state.paw, console.log)
// Subscribe also exposes the previous value // Subscribe also exposes the previous value
const unsub3 = useStore.subscribe( const unsub3 = useDogStore.subscribe(
(state) => state.paw, (state) => state.paw,
(paw, previousPaw) => console.log(paw, previousPaw) (paw, previousPaw) => console.log(paw, previousPaw)
) )
// Subscribe also supports an optional equality function // Subscribe also supports an optional equality function
const unsub4 = useStore.subscribe( const unsub4 = useDogStore.subscribe(
(state) => [state.paw, state.fur], (state) => [state.paw, state.fur],
console.log, console.log,
{ equalityFn: shallow } { equalityFn: shallow }
) )
// Subscribe and fire immediately // Subscribe and fire immediately
const unsub5 = useStore.subscribe((state) => state.paw, console.log, { const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, {
fireImmediately: true, fireImmediately: true,
}) })
``` ```
@ -231,7 +234,7 @@ You can even consume an existing vanilla store with React:
import create from 'zustand' import create from 'zustand'
import vanillaStore from './vanillaStore' import vanillaStore from './vanillaStore'
const useStore = create(vanillaStore) const useBoundStore = create(vanillaStore)
``` ```
:warning: Note that middlewares that modify `set` or `get` are not applied to `getState` and `setState`. :warning: Note that middlewares that modify `set` or `get` are not applied to `getState` and `setState`.
@ -241,13 +244,13 @@ const useStore = create(vanillaStore)
The subscribe function allows components to bind to a state-portion without forcing re-render on changes. Best combine it with useEffect for automatic unsubscribe on unmount. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance impact when you are allowed to mutate the view directly. The subscribe function allows components to bind to a state-portion without forcing re-render on changes. Best combine it with useEffect for automatic unsubscribe on unmount. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance impact when you are allowed to mutate the view directly.
```jsx ```jsx
const useStore = create(set => ({ scratches: 0, ... })) const useScratchStore = create(set => ({ scratches: 0, ... }))
const Component = () => { const Component = () => {
// Fetch initial state // Fetch initial state
const scratchRef = useRef(useStore.getState().scratches) const scratchRef = useRef(useScratchStore.getState().scratches)
// Connect to the store on mount, disconnect on unmount, catch state-changes in a reference // Connect to the store on mount, disconnect on unmount, catch state-changes in a reference
useEffect(() => useStore.subscribe( useEffect(() => useScratchStore.subscribe(
state => (scratchRef.current = state.scratches) state => (scratchRef.current = state.scratches)
), []) ), [])
... ...
@ -260,7 +263,7 @@ Reducing nested structures is tiresome. Have you tried [immer](https://github.co
```jsx ```jsx
import produce from 'immer' import produce from 'immer'
const useStore = create((set) => ({ const useLushStore = create((set) => ({
lush: { forest: { contains: { a: 'bear' } } }, lush: { forest: { contains: { a: 'bear' } } },
clearForest: () => clearForest: () =>
set( set(
@ -270,7 +273,7 @@ const useStore = create((set) => ({
), ),
})) }))
const clearForest = useStore((state) => state.clearForest) const clearForest = useLushStore((state) => state.clearForest)
clearForest() clearForest()
``` ```
@ -293,7 +296,7 @@ const log = (config) => (set, get, api) =>
api api
) )
const useStore = create( const useBeeStore = create(
log((set) => ({ log((set) => ({
bees: false, bees: false,
setBees: (input) => set({ bees: input }), setBees: (input) => set({ bees: input }),
@ -309,7 +312,7 @@ You can persist your store's data using any kind of storage.
import create from 'zustand' import create from 'zustand'
import { persist } from 'zustand/middleware' import { persist } from 'zustand/middleware'
const useStore = create( const useFishStore = create(
persist( persist(
(set, get) => ({ (set, get) => ({
fishes: 0, fishes: 0,
@ -333,7 +336,7 @@ Immer is available as middleware too.
import create from 'zustand' import create from 'zustand'
import { immer } from 'zustand/middleware/immer' import { immer } from 'zustand/middleware/immer'
const useStore = create( const useBeeStore = create(
immer((set) => ({ immer((set) => ({
bees: 0, bees: 0,
addBees: (by) => addBees: (by) =>
@ -358,12 +361,12 @@ const reducer = (state, { type, by = 1 }) => {
} }
} }
const useStore = create((set) => ({ const useGrumpyStore = create((set) => ({
grumpiness: 0, grumpiness: 0,
dispatch: (args) => set((state) => reducer(state, args)), dispatch: (args) => set((state) => reducer(state, args)),
})) }))
const dispatch = useStore((state) => state.dispatch) const dispatch = useGrumpyStore((state) => state.dispatch)
dispatch({ type: types.increase, by: 2 }) dispatch({ type: types.increase, by: 2 })
``` ```
@ -372,7 +375,7 @@ Or, just use our redux-middleware. It wires up your main-reducer, sets initial s
```jsx ```jsx
import { redux } from 'zustand/middleware' import { redux } from 'zustand/middleware'
const useStore = create(redux(reducer, initialState)) const useGrumpyStore = create(redux(reducer, initialState))
``` ```
## Redux devtools ## Redux devtools
@ -381,9 +384,9 @@ const useStore = create(redux(reducer, initialState))
import { devtools } from 'zustand/middleware' import { devtools } from 'zustand/middleware'
// Usage with a plain action store, it will log actions as "setState" // Usage with a plain action store, it will log actions as "setState"
const useStore = create(devtools(store)) const usePlainStore = create(devtools(store))
// Usage with a redux store, it will log full action types // Usage with a redux store, it will log full action types
const useStore = create(devtools(redux(reducer, initialState))) const useReduxStore = create(devtools(redux(reducer, initialState)))
``` ```
devtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument. devtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument.
@ -558,7 +561,7 @@ interface BearState {
increase: (by: number) => void increase: (by: number) => void
} }
const useStore = create<BearState>()( const useBearStore = create<BearState>()(
devtools( devtools(
persist((set) => ({ persist((set) => ({
bears: 0, bears: 0,

View File

@ -43,13 +43,13 @@ type CounterState = {
} }
it('uses the store with no args', async () => { it('uses the store with no args', async () => {
const useStore = create<CounterState>((set) => ({ const useBoundStore = create<CounterState>((set) => ({
count: 0, count: 0,
inc: () => set((state) => ({ count: state.count + 1 })), inc: () => set((state) => ({ count: state.count + 1 })),
})) }))
function Counter() { function Counter() {
const { count, inc } = useStore() const { count, inc } = useBoundStore()
useEffect(inc, [inc]) useEffect(inc, [inc])
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -60,14 +60,14 @@ it('uses the store with no args', async () => {
}) })
it('uses the store with selectors', async () => { it('uses the store with selectors', async () => {
const useStore = create<CounterState>((set) => ({ const useBoundStore = create<CounterState>((set) => ({
count: 0, count: 0,
inc: () => set((state) => ({ count: state.count + 1 })), inc: () => set((state) => ({ count: state.count + 1 })),
})) }))
function Counter() { function Counter() {
const count = useStore((s) => s.count) const count = useBoundStore((s) => s.count)
const inc = useStore((s) => s.inc) const inc = useBoundStore((s) => s.inc)
useEffect(inc, [inc]) useEffect(inc, [inc])
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -78,13 +78,13 @@ it('uses the store with selectors', async () => {
}) })
it('uses the store with a selector and equality checker', async () => { it('uses the store with a selector and equality checker', async () => {
const useStore = create(() => ({ item: { value: 0 } })) const useBoundStore = create(() => ({ item: { value: 0 } }))
const { setState } = useStore const { setState } = useBoundStore
let renderCount = 0 let renderCount = 0
function Component() { function Component() {
// Prevent re-render if new value === 1. // Prevent re-render if new value === 1.
const item = useStore( const item = useBoundStore(
(s) => s.item, (s) => s.item,
(_, newItem) => newItem.value === 1 (_, newItem) => newItem.value === 1
) )
@ -109,7 +109,7 @@ it('uses the store with a selector and equality checker', async () => {
}) })
it('only re-renders if selected state has changed', async () => { it('only re-renders if selected state has changed', async () => {
const useStore = create<CounterState>((set) => ({ const useBoundStore = create<CounterState>((set) => ({
count: 0, count: 0,
inc: () => set((state) => ({ count: state.count + 1 })), inc: () => set((state) => ({ count: state.count + 1 })),
})) }))
@ -117,13 +117,13 @@ it('only re-renders if selected state has changed', async () => {
let controlRenderCount = 0 let controlRenderCount = 0
function Counter() { function Counter() {
const count = useStore((state) => state.count) const count = useBoundStore((state) => state.count)
counterRenderCount++ counterRenderCount++
return <div>count: {count}</div> return <div>count: {count}</div>
} }
function Control() { function Control() {
const inc = useStore((state) => state.inc) const inc = useBoundStore((state) => state.inc)
controlRenderCount++ controlRenderCount++
return <button onClick={inc}>button</button> return <button onClick={inc}>button</button>
} }
@ -144,12 +144,12 @@ it('only re-renders if selected state has changed', async () => {
}) })
it('re-renders with useLayoutEffect', async () => { it('re-renders with useLayoutEffect', async () => {
const useStore = create(() => ({ state: false })) const useBoundStore = create(() => ({ state: false }))
function Component() { function Component() {
const { state } = useStore() const { state } = useBoundStore()
useLayoutEffect(() => { useLayoutEffect(() => {
useStore.setState({ state: true }) useBoundStore.setState({ state: true })
}, []) }, [])
return <>{`${state}`}</> return <>{`${state}`}</>
} }
@ -163,13 +163,13 @@ it('re-renders with useLayoutEffect', async () => {
}) })
it('can batch updates', async () => { it('can batch updates', async () => {
const useStore = create<CounterState>((set) => ({ const useBoundStore = create<CounterState>((set) => ({
count: 0, count: 0,
inc: () => set((state) => ({ count: state.count + 1 })), inc: () => set((state) => ({ count: state.count + 1 })),
})) }))
function Counter() { function Counter() {
const { count, inc } = useStore() const { count, inc } = useBoundStore()
useEffect(() => { useEffect(() => {
ReactDOM.unstable_batchedUpdates(() => { ReactDOM.unstable_batchedUpdates(() => {
inc() inc()
@ -187,13 +187,13 @@ it('can batch updates', async () => {
it('can update the selector', async () => { it('can update the selector', async () => {
type State = { one: string; two: string } type State = { one: string; two: string }
type Props = { selector: (state: State) => string } type Props = { selector: (state: State) => string }
const useStore = create<State>(() => ({ const useBoundStore = create<State>(() => ({
one: 'one', one: 'one',
two: 'two', two: 'two',
})) }))
function Component({ selector }: Props) { function Component({ selector }: Props) {
return <div>{useStore(selector)}</div> return <div>{useBoundStore(selector)}</div>
} }
const { findByText, rerender } = render(<Component selector={(s) => s.one} />) const { findByText, rerender } = render(<Component selector={(s) => s.one} />)
@ -206,13 +206,13 @@ it('can update the selector', async () => {
it('can update the equality checker', async () => { it('can update the equality checker', async () => {
type State = { value: number } type State = { value: number }
type Props = { equalityFn: (a: State, b: State) => boolean } type Props = { equalityFn: (a: State, b: State) => boolean }
const useStore = create<State>(() => ({ value: 0 })) const useBoundStore = create<State>(() => ({ value: 0 }))
const { setState } = useStore const { setState } = useBoundStore
const selector = (s: State) => s const selector = (s: State) => s
let renderCount = 0 let renderCount = 0
function Component({ equalityFn }: Props) { function Component({ equalityFn }: Props) {
const { value } = useStore(selector, equalityFn) const { value } = useBoundStore(selector, equalityFn)
return ( return (
<div> <div>
renderCount: {++renderCount}, value: {value} renderCount: {++renderCount}, value: {value}
@ -237,19 +237,19 @@ it('can update the equality checker', async () => {
await findByText('renderCount: 3, value: 0') await findByText('renderCount: 3, value: 0')
}) })
it('can call useStore with progressively more arguments', async () => { it('can call useBoundStore with progressively more arguments', async () => {
type State = { value: number } type State = { value: number }
type Props = { type Props = {
selector?: (state: State) => number selector?: (state: State) => number
equalityFn?: (a: number, b: number) => boolean equalityFn?: (a: number, b: number) => boolean
} }
const useStore = create<State>(() => ({ value: 0 })) const useBoundStore = create<State>(() => ({ value: 0 }))
const { setState } = useStore const { setState } = useBoundStore
let renderCount = 0 let renderCount = 0
function Component({ selector, equalityFn }: Props) { function Component({ selector, equalityFn }: Props) {
const value = useStore(selector as any, equalityFn) const value = useBoundStore(selector as any, equalityFn)
return ( return (
<div> <div>
renderCount: {++renderCount}, value: {JSON.stringify(value)} renderCount: {++renderCount}, value: {JSON.stringify(value)}
@ -286,8 +286,8 @@ it('can throw an error in selector', async () => {
type State = { value: string | number } type State = { value: string | number }
const initialState: State = { value: 'foo' } const initialState: State = { value: 'foo' }
const useStore = create<State>(() => initialState) const useBoundStore = create<State>(() => initialState)
const { setState } = useStore const { setState } = useBoundStore
const selector = (s: State) => const selector = (s: State) =>
// @ts-expect-error This function is supposed to throw an error // @ts-expect-error This function is supposed to throw an error
s.value.toUpperCase() s.value.toUpperCase()
@ -309,7 +309,7 @@ it('can throw an error in selector', async () => {
} }
function Component() { function Component() {
useStore(selector) useBoundStore(selector)
return <div>no error</div> return <div>no error</div>
} }
@ -331,8 +331,8 @@ it('can throw an error in equality checker', async () => {
type State = { value: string | number } type State = { value: string | number }
const initialState: State = { value: 'foo' } const initialState: State = { value: 'foo' }
const useStore = create(() => initialState) const useBoundStore = create(() => initialState)
const { setState } = useStore const { setState } = useBoundStore
const selector = (s: State) => s const selector = (s: State) => s
const equalityFn = (a: State, b: State) => const equalityFn = (a: State, b: State) =>
// @ts-expect-error This function is supposed to throw an error // @ts-expect-error This function is supposed to throw an error
@ -355,7 +355,7 @@ it('can throw an error in equality checker', async () => {
} }
function Component() { function Component() {
useStore(selector, equalityFn) useBoundStore(selector, equalityFn)
return <div>no error</div> return <div>no error</div>
} }
@ -439,8 +439,8 @@ it('can destroy the store', () => {
it('only calls selectors when necessary', async () => { it('only calls selectors when necessary', async () => {
type State = { a: number; b: number } type State = { a: number; b: number }
const useStore = create<State>(() => ({ a: 0, b: 0 })) const useBoundStore = create<State>(() => ({ a: 0, b: 0 }))
const { setState } = useStore const { setState } = useBoundStore
let inlineSelectorCallCount = 0 let inlineSelectorCallCount = 0
let staticSelectorCallCount = 0 let staticSelectorCallCount = 0
@ -450,8 +450,8 @@ it('only calls selectors when necessary', async () => {
} }
function Component() { function Component() {
useStore((s) => (inlineSelectorCallCount++, s.b)) useBoundStore((s) => (inlineSelectorCallCount++, s.b))
useStore(staticSelector) useBoundStore(staticSelector)
return ( return (
<> <>
<div>inline: {inlineSelectorCallCount}</div> <div>inline: {inlineSelectorCallCount}</div>
@ -478,13 +478,13 @@ it('ensures parent components subscribe before children', async () => {
children: { [key: string]: { text: string } } children: { [key: string]: { text: string } }
} }
type Props = { id: string } type Props = { id: string }
const useStore = create<State>(() => ({ const useBoundStore = create<State>(() => ({
children: { children: {
'1': { text: 'child 1' }, '1': { text: 'child 1' },
'2': { text: 'child 2' }, '2': { text: 'child 2' },
}, },
})) }))
const api = useStore const api = useBoundStore
function changeState() { function changeState() {
api.setState({ api.setState({
@ -495,12 +495,12 @@ it('ensures parent components subscribe before children', async () => {
} }
function Child({ id }: Props) { function Child({ id }: Props) {
const text = useStore((s) => s.children[id]?.text) const text = useBoundStore((s) => s.children[id]?.text)
return <div>{text}</div> return <div>{text}</div>
} }
function Parent() { function Parent() {
const childStates = useStore((s) => s.children) const childStates = useBoundStore((s) => s.children)
return ( return (
<> <>
<button onClick={changeState}>change state</button> <button onClick={changeState}>change state</button>
@ -520,15 +520,15 @@ it('ensures parent components subscribe before children', async () => {
// https://github.com/pmndrs/zustand/issues/84 // https://github.com/pmndrs/zustand/issues/84
it('ensures the correct subscriber is removed on unmount', async () => { it('ensures the correct subscriber is removed on unmount', async () => {
const useStore = create(() => ({ count: 0 })) const useBoundStore = create(() => ({ count: 0 }))
const api = useStore const api = useBoundStore
function increment() { function increment() {
api.setState(({ count }) => ({ count: count + 1 })) api.setState(({ count }) => ({ count: count + 1 }))
} }
function Count() { function Count() {
const c = useStore((s) => s.count) const c = useBoundStore((s) => s.count)
return <div>count: {c}</div> return <div>count: {c}</div>
} }
@ -561,16 +561,16 @@ it('ensures the correct subscriber is removed on unmount', async () => {
// https://github.com/pmndrs/zustand/issues/86 // https://github.com/pmndrs/zustand/issues/86
it('ensures a subscriber is not mistakenly overwritten', async () => { it('ensures a subscriber is not mistakenly overwritten', async () => {
const useStore = create(() => ({ count: 0 })) const useBoundStore = create(() => ({ count: 0 }))
const { setState } = useStore const { setState } = useBoundStore
function Count1() { function Count1() {
const c = useStore((s) => s.count) const c = useBoundStore((s) => s.count)
return <div>count1: {c}</div> return <div>count1: {c}</div>
} }
function Count2() { function Count2() {
const c = useStore((s) => s.count) const c = useBoundStore((s) => s.count)
return <div>count2: {c}</div> return <div>count2: {c}</div>
} }

View File

@ -16,17 +16,17 @@ type CounterState = {
describe('counter state spec (no middleware)', () => { describe('counter state spec (no middleware)', () => {
it('no middleware', () => { it('no middleware', () => {
const useStore = create<CounterState>((set, get) => ({ const useBoundStore = create<CounterState>((set, get) => ({
count: 0, count: 0,
inc: () => set({ count: get().count + 1 }, false), inc: () => set({ count: get().count + 1 }, false),
})) }))
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
return <></> return <></>
} }
TestComponent TestComponent
@ -43,7 +43,7 @@ describe('counter state spec (single middleware)', () => {
}) })
it('immer', () => { it('immer', () => {
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
immer((set, get) => ({ immer((set, get) => ({
count: 0, count: 0,
inc: () => inc: () =>
@ -53,12 +53,12 @@ describe('counter state spec (single middleware)', () => {
})) }))
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
return <></> return <></>
} }
TestComponent TestComponent
@ -69,7 +69,7 @@ describe('counter state spec (single middleware)', () => {
}) })
it('redux', () => { it('redux', () => {
const useStore = create( const useBoundStore = create(
redux<{ count: number }, { type: 'INC' }>( redux<{ count: number }, { type: 'INC' }>(
(state, action) => { (state, action) => {
switch (action.type) { switch (action.type) {
@ -83,10 +83,10 @@ describe('counter state spec (single middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.dispatch)({ type: 'INC' }) useBoundStore((s) => s.dispatch)({ type: 'INC' })
useStore().dispatch({ type: 'INC' }) useBoundStore().dispatch({ type: 'INC' })
useStore.dispatch({ type: 'INC' }) useBoundStore.dispatch({ type: 'INC' })
return <></> return <></>
} }
TestComponent TestComponent
@ -98,7 +98,7 @@ describe('counter state spec (single middleware)', () => {
it('devtools', () => { it('devtools', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
(set, get) => ({ (set, get) => ({
count: 0, count: 0,
@ -108,13 +108,13 @@ describe('counter state spec (single middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
return <></> return <></>
} }
TestComponent TestComponent
@ -125,20 +125,20 @@ describe('counter state spec (single middleware)', () => {
}) })
it('subscribeWithSelector', () => { it('subscribeWithSelector', () => {
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
subscribeWithSelector((set, get) => ({ subscribeWithSelector((set, get) => ({
count: 1, count: 1,
inc: () => set({ count: get().count + 1 }, false), inc: () => set({ count: get().count + 1 }, false),
})) }))
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
@ -152,18 +152,18 @@ describe('counter state spec (single middleware)', () => {
}) })
it('combine', () => { it('combine', () => {
const useStore = create( const useBoundStore = create(
combine({ count: 1 }, (set, get) => ({ combine({ count: 1 }, (set, get) => ({
inc: () => set({ count: get().count + 1 }, false), inc: () => set({ count: get().count + 1 }, false),
})) }))
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
return <></> return <></>
} }
TestComponent TestComponent
@ -174,7 +174,7 @@ describe('counter state spec (single middleware)', () => {
}) })
it('persist', () => { it('persist', () => {
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
count: 1, count: 1,
@ -184,13 +184,13 @@ describe('counter state spec (single middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent
@ -201,7 +201,7 @@ describe('counter state spec (single middleware)', () => {
}) })
it('persist with partialize', () => { it('persist with partialize', () => {
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
count: 1, count: 1,
@ -211,14 +211,14 @@ describe('counter state spec (single middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
useStore.persist.setOptions({ useBoundStore.persist.setOptions({
// @ts-expect-error to test if the partialized state is inferred as number // @ts-expect-error to test if the partialized state is inferred as number
partialize: () => 'not-a-number', partialize: () => 'not-a-number',
}) })
@ -228,7 +228,7 @@ describe('counter state spec (single middleware)', () => {
}) })
it('persist without custom api (#638)', () => { it('persist without custom api (#638)', () => {
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
count: 1, count: 1,
@ -238,12 +238,12 @@ describe('counter state spec (single middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
return <></> return <></>
} }
TestComponent TestComponent
@ -261,7 +261,7 @@ describe('counter state spec (double middleware)', () => {
it('devtools & immer', () => { it('devtools & immer', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
immer((set, get) => ({ immer((set, get) => ({
count: 0, count: 0,
@ -274,13 +274,13 @@ describe('counter state spec (double middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
return <></> return <></>
} }
TestComponent TestComponent
@ -288,7 +288,7 @@ describe('counter state spec (double middleware)', () => {
it('devtools & redux', () => { it('devtools & redux', () => {
__DEV__ = false __DEV__ = false
const useStore = create( const useBoundStore = create(
devtools( devtools(
redux( redux(
(state, action: { type: 'INC' }) => { (state, action: { type: 'INC' }) => {
@ -305,11 +305,11 @@ describe('counter state spec (double middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.dispatch)({ type: 'INC' }) useBoundStore((s) => s.dispatch)({ type: 'INC' })
useStore().dispatch({ type: 'INC' }) useBoundStore().dispatch({ type: 'INC' })
useStore.dispatch({ type: 'INC' }) useBoundStore.dispatch({ type: 'INC' })
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
return <></> return <></>
} }
TestComponent TestComponent
@ -317,7 +317,7 @@ describe('counter state spec (double middleware)', () => {
it('devtools & combine', () => { it('devtools & combine', () => {
__DEV__ = false __DEV__ = false
const useStore = create( const useBoundStore = create(
devtools( devtools(
combine({ count: 1 }, (set, get) => ({ combine({ count: 1 }, (set, get) => ({
inc: () => set({ count: get().count + 1 }, false, 'inc'), inc: () => set({ count: get().count + 1 }, false, 'inc'),
@ -326,20 +326,20 @@ describe('counter state spec (double middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
return <></> return <></>
} }
TestComponent TestComponent
}) })
it('subscribeWithSelector & combine', () => { it('subscribeWithSelector & combine', () => {
const useStore = create( const useBoundStore = create(
subscribeWithSelector( subscribeWithSelector(
combine({ count: 1 }, (set, get) => ({ combine({ count: 1 }, (set, get) => ({
inc: () => set({ count: get().count + 1 }, false), inc: () => set({ count: get().count + 1 }, false),
@ -347,13 +347,13 @@ describe('counter state spec (double middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
@ -364,7 +364,7 @@ describe('counter state spec (double middleware)', () => {
it('devtools & subscribeWithSelector', () => { it('devtools & subscribeWithSelector', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
subscribeWithSelector((set, get) => ({ subscribeWithSelector((set, get) => ({
count: 1, count: 1,
@ -374,17 +374,17 @@ describe('counter state spec (double middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
return <></> return <></>
} }
TestComponent TestComponent
@ -392,7 +392,7 @@ describe('counter state spec (double middleware)', () => {
it('devtools & persist', () => { it('devtools & persist', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
persist( persist(
(set, get) => ({ (set, get) => ({
@ -405,14 +405,14 @@ describe('counter state spec (double middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent
@ -430,7 +430,7 @@ describe('counter state spec (triple middleware)', () => {
it('devtools & persist & immer', () => { it('devtools & persist & immer', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
persist( persist(
immer((set, get) => ({ immer((set, get) => ({
@ -446,14 +446,14 @@ describe('counter state spec (triple middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent
@ -461,7 +461,7 @@ describe('counter state spec (triple middleware)', () => {
it('devtools & subscribeWithSelector & combine', () => { it('devtools & subscribeWithSelector & combine', () => {
__DEV__ = false __DEV__ = false
const useStore = create( const useBoundStore = create(
devtools( devtools(
subscribeWithSelector( subscribeWithSelector(
combine({ count: 1 }, (set, get) => ({ combine({ count: 1 }, (set, get) => ({
@ -472,17 +472,17 @@ describe('counter state spec (triple middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
return <></> return <></>
} }
TestComponent TestComponent
@ -490,7 +490,7 @@ describe('counter state spec (triple middleware)', () => {
it('devtools & subscribeWithSelector & persist', () => { it('devtools & subscribeWithSelector & persist', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
subscribeWithSelector( subscribeWithSelector(
persist( persist(
@ -505,18 +505,18 @@ describe('counter state spec (triple middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent
@ -534,7 +534,7 @@ describe('counter state spec (quadruple middleware)', () => {
it('devtools & subscribeWithSelector & persist & immer (#616)', () => { it('devtools & subscribeWithSelector & persist & immer (#616)', () => {
__DEV__ = false __DEV__ = false
const useStore = create<CounterState>()( const useBoundStore = create<CounterState>()(
devtools( devtools(
subscribeWithSelector( subscribeWithSelector(
persist( persist(
@ -552,18 +552,18 @@ describe('counter state spec (quadruple middleware)', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
useStore.setState({ count: 0 }, false, 'reset') useBoundStore.setState({ count: 0 }, false, 'reset')
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent
@ -572,7 +572,7 @@ describe('counter state spec (quadruple middleware)', () => {
describe('more complex state spec with subscribeWithSelector', () => { describe('more complex state spec with subscribeWithSelector', () => {
it('#619, #632', () => { it('#619, #632', () => {
const useStore = create( const useBoundStore = create(
subscribeWithSelector( subscribeWithSelector(
persist( persist(
() => ({ () => ({
@ -583,14 +583,14 @@ describe('more complex state spec with subscribeWithSelector', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.foo) useBoundStore((s) => s.foo)
useStore().foo useBoundStore().foo
useStore.getState().foo useBoundStore.getState().foo
useStore.subscribe( useBoundStore.subscribe(
(state) => state.foo, (state) => state.foo,
(foo) => console.log(foo) (foo) => console.log(foo)
) )
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent
@ -600,7 +600,7 @@ describe('more complex state spec with subscribeWithSelector', () => {
type MyState = { type MyState = {
foo: number | null foo: number | null
} }
const useStore = create<MyState>()( const useBoundStore = create<MyState>()(
subscribeWithSelector( subscribeWithSelector(
() => () =>
({ ({
@ -609,10 +609,10 @@ describe('more complex state spec with subscribeWithSelector', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.foo) useBoundStore((s) => s.foo)
useStore().foo useBoundStore().foo
useStore.getState().foo useBoundStore.getState().foo
useStore.subscribe( useBoundStore.subscribe(
(state) => state.foo, (state) => state.foo,
(foo) => console.log(foo) (foo) => console.log(foo)
) )
@ -627,7 +627,7 @@ describe('more complex state spec with subscribeWithSelector', () => {
authenticated: boolean authenticated: boolean
authenticate: (username: string, password: string) => Promise<void> authenticate: (username: string, password: string) => Promise<void>
} }
const useStore = create<MyState>()( const useBoundStore = create<MyState>()(
persist( persist(
(set) => ({ (set) => ({
token: undefined, token: undefined,
@ -640,12 +640,12 @@ describe('more complex state spec with subscribeWithSelector', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.authenticated) useBoundStore((s) => s.authenticated)
useStore((s) => s.authenticate)('u', 'p') useBoundStore((s) => s.authenticate)('u', 'p')
useStore().authenticated useBoundStore().authenticated
useStore().authenticate('u', 'p') useBoundStore().authenticate('u', 'p')
useStore.getState().authenticated useBoundStore.getState().authenticated
useStore.getState().authenticate('u', 'p') useBoundStore.getState().authenticate('u', 'p')
return <></> return <></>
} }
TestComponent TestComponent
@ -654,7 +654,7 @@ describe('more complex state spec with subscribeWithSelector', () => {
describe('create with explicitly annotated mutators', () => { describe('create with explicitly annotated mutators', () => {
it('subscribeWithSelector & persist', () => { it('subscribeWithSelector & persist', () => {
const useStore = create< const useBoundStore = create<
CounterState, CounterState,
[ [
['zustand/subscribeWithSelector', never], ['zustand/subscribeWithSelector', never],
@ -672,18 +672,18 @@ describe('create with explicitly annotated mutators', () => {
) )
) )
const TestComponent = () => { const TestComponent = () => {
useStore((s) => s.count) * 2 useBoundStore((s) => s.count) * 2
useStore((s) => s.inc)() useBoundStore((s) => s.inc)()
useStore().count * 2 useBoundStore().count * 2
useStore().inc() useBoundStore().inc()
useStore.getState().count * 2 useBoundStore.getState().count * 2
useStore.getState().inc() useBoundStore.getState().inc()
useStore.subscribe( useBoundStore.subscribe(
(state) => state.count, (state) => state.count,
(count) => console.log(count * 2) (count) => console.log(count * 2)
) )
useStore.setState({ count: 0 }, false) useBoundStore.setState({ count: 0 }, false)
useStore.persist.hasHydrated() useBoundStore.persist.hasHydrated()
return <></> return <></>
} }
TestComponent TestComponent

View File

@ -50,7 +50,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist( persist(
() => ({ () => ({
count: 0, count: 0,
@ -65,7 +65,7 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count, name } = useStore() const { count, name } = useBoundStore()
return ( return (
<div> <div>
count: {count}, name: {name} count: {count}, name: {name}
@ -94,7 +94,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -103,7 +103,7 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -123,21 +123,21 @@ describe('persist middleware with async configuration', () => {
const createStore = () => { const createStore = () => {
const onRehydrateStorageSpy = jest.fn() const onRehydrateStorageSpy = jest.fn()
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
onRehydrateStorage: () => onRehydrateStorageSpy, onRehydrateStorage: () => onRehydrateStorageSpy,
}) })
) )
return { useStore, onRehydrateStorageSpy } return { useBoundStore, onRehydrateStorageSpy }
} }
// Initialize from empty storage // Initialize from empty storage
const { useStore, onRehydrateStorageSpy } = createStore() const { useBoundStore, onRehydrateStorageSpy } = createStore()
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -148,7 +148,7 @@ describe('persist middleware with async configuration', () => {
}) })
// Write something to the store // Write something to the store
act(() => useStore.setState({ count: 42 })) act(() => useBoundStore.setState({ count: 42 }))
await findByText('count: 42') await findByText('count: 42')
expect(setItemSpy).toBeCalledWith( expect(setItemSpy).toBeCalledWith(
'test-storage', 'test-storage',
@ -158,11 +158,11 @@ describe('persist middleware with async configuration', () => {
// Create the same store a second time and check if the persisted state // Create the same store a second time and check if the persisted state
// is loaded correctly // is loaded correctly
const { const {
useStore: useStore2, useBoundStore: useBoundStore2,
onRehydrateStorageSpy: onRehydrateStorageSpy2, onRehydrateStorageSpy: onRehydrateStorageSpy2,
} = createStore() } = createStore()
function Counter2() { function Counter2() {
const { count } = useStore2() const { count } = useBoundStore2()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -188,7 +188,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
version: 13, version: 13,
@ -199,7 +199,7 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -228,7 +228,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create<{ const useBoundStore = create<{
count: number count: number
name: string name: string
setName: (name: string) => void setName: (name: string) => void
@ -249,7 +249,7 @@ describe('persist middleware with async configuration', () => {
) )
function Component() { function Component() {
const { count, setName, name } = useStore() const { count, setName, name } = useBoundStore()
useEffect(() => { useEffect(() => {
setName('test') setName('test')
}, [setName]) }, [setName])
@ -266,7 +266,7 @@ describe('persist middleware with async configuration', () => {
await findByText('count: 42') await findByText('count: 42')
await findByText('name: test') await findByText('name: test')
expect(useStore.getState()).toEqual( expect(useBoundStore.getState()).toEqual(
expect.objectContaining({ expect.objectContaining({
count: 42, count: 42,
name: 'test', name: 'test',
@ -287,7 +287,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
version: 13, version: 13,
@ -297,7 +297,7 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -324,7 +324,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
version: 13, version: 13,
@ -337,7 +337,7 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -367,7 +367,7 @@ describe('persist middleware with async configuration', () => {
const unstorableMethod = () => {} const unstorableMethod = () => {}
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0, unstorableMethod }), { persist(() => ({ count: 0, unstorableMethod }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -376,7 +376,7 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
@ -407,7 +407,7 @@ describe('persist middleware with async configuration', () => {
const unstorableMethod = () => {} const unstorableMethod = () => {}
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0, actions: { unstorableMethod } }), { persist(() => ({ count: 0, actions: { unstorableMethod } }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -424,14 +424,14 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
const { findByText } = render(<Counter />) const { findByText } = render(<Counter />)
await findByText('count: 1') await findByText('count: 1')
expect(useStore.getState()).toEqual({ expect(useBoundStore.getState()).toEqual({
count: 1, count: 1,
actions: { actions: {
unstorableMethod, unstorableMethod,
@ -451,7 +451,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -460,14 +460,14 @@ describe('persist middleware with async configuration', () => {
) )
function Counter() { function Counter() {
const { count } = useStore() const { count } = useBoundStore()
return <div>count: {count}</div> return <div>count: {count}</div>
} }
const { findByText } = render(<Counter />) const { findByText } = render(<Counter />)
await findByText('count: 1') await findByText('count: 1')
expect(useStore.getState()).toEqual({ expect(useBoundStore.getState()).toEqual({
count: 1, count: 1,
}) })
}) })
@ -481,7 +481,7 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -489,8 +489,8 @@ describe('persist middleware with async configuration', () => {
) )
storage.getItem = async () => storageValue storage.getItem = async () => storageValue
await useStore.persist.rehydrate() await useBoundStore.persist.rehydrate()
expect(useStore.getState()).toEqual({ expect(useBoundStore.getState()).toEqual({
count: 1, count: 1,
}) })
}) })
@ -502,17 +502,19 @@ describe('persist middleware with async configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
}) })
) )
expect(useStore.persist.hasHydrated()).toBe(false) expect(useBoundStore.persist.hasHydrated()).toBe(false)
await new Promise((resolve) => useStore.persist.onFinishHydration(resolve)) await new Promise((resolve) =>
expect(useStore.persist.hasHydrated()).toBe(true) useBoundStore.persist.onFinishHydration(resolve)
)
expect(useBoundStore.persist.hasHydrated()).toBe(true)
await useStore.persist.rehydrate() await useBoundStore.persist.rehydrate()
expect(useStore.persist.hasHydrated()).toBe(true) expect(useBoundStore.persist.hasHydrated()).toBe(true)
}) })
}) })

View File

@ -46,7 +46,7 @@ describe('persist middleware with sync configuration', () => {
} }
const onRehydrateStorageSpy = jest.fn() const onRehydrateStorageSpy = jest.fn()
const useStore = create( const useBoundStore = create(
persist( persist(
() => ({ () => ({
count: 0, count: 0,
@ -60,7 +60,10 @@ describe('persist middleware with sync configuration', () => {
) )
) )
expect(useStore.getState()).toEqual({ count: 42, name: 'test-storage' }) expect(useBoundStore.getState()).toEqual({
count: 42,
name: 'test-storage',
})
expect(onRehydrateStorageSpy).toBeCalledWith( expect(onRehydrateStorageSpy).toBeCalledWith(
{ count: 42, name: 'test-storage' }, { count: 42, name: 'test-storage' },
undefined undefined
@ -93,24 +96,24 @@ describe('persist middleware with sync configuration', () => {
const createStore = () => { const createStore = () => {
const onRehydrateStorageSpy = jest.fn() const onRehydrateStorageSpy = jest.fn()
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
onRehydrateStorage: () => onRehydrateStorageSpy, onRehydrateStorage: () => onRehydrateStorageSpy,
}) })
) )
return { useStore, onRehydrateStorageSpy } return { useBoundStore, onRehydrateStorageSpy }
} }
// Initialize from empty storage // Initialize from empty storage
const { useStore, onRehydrateStorageSpy } = createStore() const { useBoundStore, onRehydrateStorageSpy } = createStore()
expect(useStore.getState()).toEqual({ count: 0 }) expect(useBoundStore.getState()).toEqual({ count: 0 })
expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined) expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined)
// Write something to the store // Write something to the store
useStore.setState({ count: 42 }) useBoundStore.setState({ count: 42 })
expect(useStore.getState()).toEqual({ count: 42 }) expect(useBoundStore.getState()).toEqual({ count: 42 })
expect(setItemSpy).toBeCalledWith( expect(setItemSpy).toBeCalledWith(
'test-storage', 'test-storage',
JSON.stringify({ state: { count: 42 }, version: 0 }) JSON.stringify({ state: { count: 42 }, version: 0 })
@ -119,10 +122,10 @@ describe('persist middleware with sync configuration', () => {
// Create the same store a second time and check if the persisted state // Create the same store a second time and check if the persisted state
// is loaded correctly // is loaded correctly
const { const {
useStore: useStore2, useBoundStore: useBoundStore2,
onRehydrateStorageSpy: onRehydrateStorageSpy2, onRehydrateStorageSpy: onRehydrateStorageSpy2,
} = createStore() } = createStore()
expect(useStore2.getState()).toEqual({ count: 42 }) expect(useBoundStore2.getState()).toEqual({ count: 42 })
expect(onRehydrateStorageSpy2).toBeCalledWith({ count: 42 }, undefined) expect(onRehydrateStorageSpy2).toBeCalledWith({ count: 42 }, undefined)
}) })
@ -141,7 +144,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
version: 13, version: 13,
@ -151,7 +154,7 @@ describe('persist middleware with sync configuration', () => {
}) })
) )
expect(useStore.getState()).toEqual({ count: 99 }) expect(useBoundStore.getState()).toEqual({ count: 99 })
expect(migrateSpy).toBeCalledWith({ count: 42 }, 12) expect(migrateSpy).toBeCalledWith({ count: 42 }, 12)
expect(setItemSpy).toBeCalledWith( expect(setItemSpy).toBeCalledWith(
'test-storage', 'test-storage',
@ -176,7 +179,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
version: 13, version: 13,
@ -185,7 +188,7 @@ describe('persist middleware with sync configuration', () => {
}) })
) )
expect(useStore.getState()).toEqual({ count: 0 }) expect(useBoundStore.getState()).toEqual({ count: 0 })
expect(console.error).toHaveBeenCalled() expect(console.error).toHaveBeenCalled()
expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined) expect(onRehydrateStorageSpy).toBeCalledWith({ count: 0 }, undefined)
}) })
@ -203,7 +206,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
version: 13, version: 13,
@ -215,7 +218,7 @@ describe('persist middleware with sync configuration', () => {
}) })
) )
expect(useStore.getState()).toEqual({ count: 0 }) expect(useBoundStore.getState()).toEqual({ count: 0 })
expect(onRehydrateStorageSpy).toBeCalledWith( expect(onRehydrateStorageSpy).toBeCalledWith(
undefined, undefined,
new Error('migrate error') new Error('migrate error')
@ -237,7 +240,7 @@ describe('persist middleware with sync configuration', () => {
const unstorableMethod = () => {} const unstorableMethod = () => {}
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0, unstorableMethod }), { persist(() => ({ count: 0, unstorableMethod }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -247,7 +250,7 @@ describe('persist middleware with sync configuration', () => {
const expectedState = { count: 1, unstorableMethod } const expectedState = { count: 1, unstorableMethod }
expect(useStore.getState()).toEqual(expectedState) expect(useBoundStore.getState()).toEqual(expectedState)
expect(onRehydrateStorageSpy).toBeCalledWith(expectedState, undefined) expect(onRehydrateStorageSpy).toBeCalledWith(expectedState, undefined)
}) })
@ -267,7 +270,7 @@ describe('persist middleware with sync configuration', () => {
const unstorableMethod = () => {} const unstorableMethod = () => {}
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0, actions: { unstorableMethod } }), { persist(() => ({ count: 0, actions: { unstorableMethod } }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -283,7 +286,7 @@ describe('persist middleware with sync configuration', () => {
}) })
) )
expect(useStore.getState()).toEqual({ expect(useBoundStore.getState()).toEqual({
count: 1, count: 1,
actions: { actions: {
unstorableMethod, unstorableMethod,
@ -303,7 +306,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -311,7 +314,7 @@ describe('persist middleware with sync configuration', () => {
}) })
) )
expect(useStore.getState()).toEqual({ expect(useBoundStore.getState()).toEqual({
count: 1, count: 1,
}) })
}) })
@ -325,7 +328,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist( persist(
() => ({ () => ({
object: { object: {
@ -359,7 +362,7 @@ describe('persist middleware with sync configuration', () => {
) )
) )
useStore.setState({}) useBoundStore.setState({})
expect(setItemSpy).toBeCalledWith( expect(setItemSpy).toBeCalledWith(
'test-storage', 'test-storage',
JSON.stringify({ JSON.stringify({
@ -388,14 +391,14 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
}) })
) )
expect(useStore.persist.getOptions().name).toBeDefined() expect(useBoundStore.persist.getOptions().name).toBeDefined()
expect(useStore.persist.getOptions().name).toBe('test-storage') expect(useBoundStore.persist.getOptions().name).toBe('test-storage')
}) })
it('can change the options through the api', () => { it('can change the options through the api', () => {
@ -407,7 +410,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -415,20 +418,20 @@ describe('persist middleware with sync configuration', () => {
}) })
) )
useStore.setState({}) useBoundStore.setState({})
expect(setItemSpy).toBeCalledWith( expect(setItemSpy).toBeCalledWith(
'test-storage', 'test-storage',
'{"state":{"count":0},"version":0}' '{"state":{"count":0},"version":0}'
) )
useStore.persist.setOptions({ useBoundStore.persist.setOptions({
name: 'test-storage-2', name: 'test-storage-2',
partialize: (state) => partialize: (state) =>
Object.fromEntries( Object.fromEntries(
Object.entries(state).filter(([key]) => key !== 'count') Object.entries(state).filter(([key]) => key !== 'count')
), ),
}) })
useStore.setState({}) useBoundStore.setState({})
expect(setItemSpy).toBeCalledWith( expect(setItemSpy).toBeCalledWith(
'test-storage-2', 'test-storage-2',
'{"state":{},"version":0}' '{"state":{},"version":0}'
@ -444,14 +447,14 @@ describe('persist middleware with sync configuration', () => {
removeItem: removeItemSpy, removeItem: removeItemSpy,
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
}) })
) )
useStore.persist.clearStorage() useBoundStore.persist.clearStorage()
expect(removeItemSpy).toBeCalledWith('test-storage') expect(removeItemSpy).toBeCalledWith('test-storage')
}) })
@ -464,7 +467,7 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
@ -472,8 +475,8 @@ describe('persist middleware with sync configuration', () => {
) )
storage.getItem = () => storageValue storage.getItem = () => storageValue
useStore.persist.rehydrate() useBoundStore.persist.rehydrate()
expect(useStore.getState()).toEqual({ expect(useBoundStore.getState()).toEqual({
count: 1, count: 1,
}) })
}) })
@ -485,17 +488,17 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
}) })
) )
expect(useStore.persist.hasHydrated()).toBe(true) expect(useBoundStore.persist.hasHydrated()).toBe(true)
await useStore.persist.rehydrate() await useBoundStore.persist.rehydrate()
expect(useStore.persist.hasHydrated()).toBe(true) expect(useBoundStore.persist.hasHydrated()).toBe(true)
}) })
it('can wait for rehydration through the api', async () => { it('can wait for rehydration through the api', async () => {
@ -513,23 +516,23 @@ describe('persist middleware with sync configuration', () => {
removeItem: () => {}, removeItem: () => {},
} }
const useStore = create( const useBoundStore = create(
persist(() => ({ count: 0 }), { persist(() => ({ count: 0 }), {
name: 'test-storage', name: 'test-storage',
getStorage: () => storage, getStorage: () => storage,
}) })
) )
const hydrateUnsub1 = useStore.persist.onHydrate(onHydrateSpy1) const hydrateUnsub1 = useBoundStore.persist.onHydrate(onHydrateSpy1)
useStore.persist.onHydrate(onHydrateSpy2) useBoundStore.persist.onHydrate(onHydrateSpy2)
const finishHydrationUnsub1 = useStore.persist.onFinishHydration( const finishHydrationUnsub1 = useBoundStore.persist.onFinishHydration(
onFinishHydrationSpy1 onFinishHydrationSpy1
) )
useStore.persist.onFinishHydration(onFinishHydrationSpy2) useBoundStore.persist.onFinishHydration(onFinishHydrationSpy2)
storage.getItem = () => storageValue1 storage.getItem = () => storageValue1
await useStore.persist.rehydrate() await useBoundStore.persist.rehydrate()
expect(onHydrateSpy1).toBeCalledWith({ count: 0 }) expect(onHydrateSpy1).toBeCalledWith({ count: 0 })
expect(onHydrateSpy2).toBeCalledWith({ count: 0 }) expect(onHydrateSpy2).toBeCalledWith({ count: 0 })
expect(onFinishHydrationSpy1).toBeCalledWith({ count: 1 }) expect(onFinishHydrationSpy1).toBeCalledWith({ count: 1 })
@ -539,7 +542,7 @@ describe('persist middleware with sync configuration', () => {
finishHydrationUnsub1() finishHydrationUnsub1()
storage.getItem = () => storageValue2 storage.getItem = () => storageValue2
await useStore.persist.rehydrate() await useBoundStore.persist.rehydrate()
expect(onHydrateSpy1).not.toBeCalledTimes(2) expect(onHydrateSpy1).not.toBeCalledTimes(2)
expect(onHydrateSpy2).toBeCalledWith({ count: 1 }) expect(onHydrateSpy2).toBeCalledWith({ count: 1 })
expect(onFinishHydrationSpy1).not.toBeCalledTimes(2) expect(onFinishHydrationSpy1).not.toBeCalledTimes(2)

View File

@ -57,23 +57,23 @@ describe('shallow', () => {
}) })
describe('types', () => { describe('types', () => {
it('works with useStore and array selector (#1107)', () => { it('works with useBoundStore and array selector (#1107)', () => {
const useStore = create(() => ({ const useBoundStore = create(() => ({
villages: [] as { name: string }[], villages: [] as { name: string }[],
})) }))
const Component = () => { const Component = () => {
const villages = useStore((state) => state.villages, shallow) const villages = useBoundStore((state) => state.villages, shallow)
return <>{villages.length}</> return <>{villages.length}</>
} }
expect(Component).toBeDefined() expect(Component).toBeDefined()
}) })
it('works with useStore and string selector (#1107)', () => { it('works with useBoundStore and string selector (#1107)', () => {
const useStore = create(() => ({ const useBoundStore = create(() => ({
refetchTimestamp: '', refetchTimestamp: '',
})) }))
const Component = () => { const Component = () => {
const refetchTimestamp = useStore( const refetchTimestamp = useBoundStore(
(state) => state.refetchTimestamp, (state) => state.refetchTimestamp,
shallow shallow
) )

View File

@ -46,7 +46,7 @@ it('can use exposed types', () => {
storeApi.setState({ num: v }) storeApi.setState({ num: v })
}, },
})) }))
const useStore = storeApi const useBoundStore = storeApi
const stateCreator: StateCreator<ExampleState> = (set, get) => ({ const stateCreator: StateCreator<ExampleState> = (set, get) => ({
num: 1, num: 1,
@ -74,7 +74,7 @@ it('can use exposed types', () => {
_destroy: StoreApi<ExampleState>['destroy'], _destroy: StoreApi<ExampleState>['destroy'],
_equalityFn: (a: ExampleState, b: ExampleState) => boolean, _equalityFn: (a: ExampleState, b: ExampleState) => boolean,
_stateCreator: StateCreator<ExampleState>, _stateCreator: StateCreator<ExampleState>,
_useStore: UseBoundStore<StoreApi<ExampleState>> _useBoundStore: UseBoundStore<StoreApi<ExampleState>>
) { ) {
expect(true).toBeTruthy() expect(true).toBeTruthy()
} }
@ -91,7 +91,7 @@ it('can use exposed types', () => {
storeApi.destroy, storeApi.destroy,
equalityFn, equalityFn,
stateCreator, stateCreator,
useStore useBoundStore
) )
}) })