mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
Merge branch 'main' into v5
This commit is contained in:
commit
abbc74025a
@ -326,7 +326,7 @@ type State = {
|
||||
|
||||
type Actions = {
|
||||
updateCount: (
|
||||
countCallback: (count: State['count']) => State['count']
|
||||
countCallback: (count: State['count']) => State['count'],
|
||||
) => void
|
||||
}
|
||||
|
||||
@ -362,7 +362,7 @@ type State = {
|
||||
|
||||
type Actions = {
|
||||
updateCount: (
|
||||
countCallback: (count: State['count']) => State['count']
|
||||
countCallback: (count: State['count']) => State['count'],
|
||||
) => void
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ type WithSelectors<S> = S extends { getState: () => infer T }
|
||||
: never
|
||||
|
||||
const createSelectors = <S extends UseBoundStore<StoreApi<object>>>(
|
||||
_store: S
|
||||
_store: S,
|
||||
) => {
|
||||
let store = _store as WithSelectors<typeof _store>
|
||||
store.use = {}
|
||||
|
||||
@ -38,8 +38,8 @@ export const useBoundStore = create(
|
||||
{
|
||||
name: 'food-storage', // unique name
|
||||
storage: createJSONStorage(() => hashStorage),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -135,7 +135,7 @@ import { useStoreWithEqualityFn } from 'zustand/traditional'
|
||||
|
||||
function useBearContext<T>(
|
||||
selector: (state: BearState) => T,
|
||||
equalityFn?: (left: T, right: T) => boolean
|
||||
equalityFn?: (left: T, right: T) => boolean,
|
||||
): T {
|
||||
const store = useContext(BearContext)
|
||||
if (!store) throw new Error('Missing BearContext.Provider in the tree')
|
||||
|
||||
@ -108,8 +108,8 @@ export const useBoundStore = create(
|
||||
...createBearSlice(...a),
|
||||
...createFishSlice(...a),
|
||||
}),
|
||||
{ name: 'bound-store' }
|
||||
)
|
||||
{ name: 'bound-store' },
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -249,7 +249,7 @@ export default mergeConfig(
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./setup-vitest.ts'],
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
@ -302,7 +302,7 @@ export const createCounterStore = () => {
|
||||
}
|
||||
|
||||
export const CounterStoreContext = createContext<StoreApi<CounterStore>>(
|
||||
null as never
|
||||
null as never,
|
||||
)
|
||||
|
||||
export type CounterStoreProviderProps = PropsWithChildren
|
||||
@ -322,13 +322,13 @@ export const CounterStoreProvider = ({
|
||||
export type UseCounterStoreContextSelector<T> = (store: CounterStore) => T
|
||||
|
||||
export const useCounterStoreContext = <T,>(
|
||||
selector: UseCounterStoreContextSelector<T>
|
||||
selector: UseCounterStoreContextSelector<T>,
|
||||
): T => {
|
||||
const counterStoreContext = useContext(CounterStoreContext)
|
||||
|
||||
if (counterStoreContext === undefined) {
|
||||
throw new Error(
|
||||
'useCounterStoreContext must be used within CounterStoreProvider'
|
||||
'useCounterStoreContext must be used within CounterStoreProvider',
|
||||
)
|
||||
}
|
||||
|
||||
@ -371,7 +371,7 @@ describe('Counter', () => {
|
||||
|
||||
expect(await screen.findByText(/^1$/)).toBeInTheDocument()
|
||||
expect(
|
||||
await screen.findByRole('button', { name: /one up/i })
|
||||
await screen.findByRole('button', { name: /one up/i }),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -441,7 +441,7 @@ describe('CounterWithContext', () => {
|
||||
|
||||
expect(await screen.findByText(/^1$/)).toBeInTheDocument()
|
||||
expect(
|
||||
await screen.findByRole('button', { name: /one up/i })
|
||||
await screen.findByRole('button', { name: /one up/i }),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@ Imagine you have a scenario like this:
|
||||
|
||||
```ts
|
||||
declare const withError: <T, E>(
|
||||
p: Promise<T>
|
||||
p: Promise<T>,
|
||||
) => Promise<[error: undefined, value: T] | [error: E, value: undefined]>
|
||||
declare const doSomething: () => Promise<string>
|
||||
|
||||
@ -113,10 +113,10 @@ Here, `T` is inferred to be a `string` and `E` is inferred to be `unknown`. You
|
||||
```ts
|
||||
declare const withError: {
|
||||
<E>(): <T>(
|
||||
p: Promise<T>
|
||||
p: Promise<T>,
|
||||
) => Promise<[error: undefined, value: T] | [error: E, value: undefined]>
|
||||
<T, E>(
|
||||
p: Promise<T>
|
||||
p: Promise<T>,
|
||||
): Promise<[error: undefined, value: T] | [error: E, value: undefined]>
|
||||
}
|
||||
declare const doSomething: () => Promise<string>
|
||||
@ -142,7 +142,7 @@ import { combine } from 'zustand/middleware'
|
||||
const useBearStore = create(
|
||||
combine({ bears: 0 }, (set) => ({
|
||||
increase: (by: number) => set((state) => ({ bears: state.bears + by })),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
```
|
||||
|
||||
@ -181,9 +181,9 @@ const useBearStore = create<BearState>()(
|
||||
bears: 0,
|
||||
increase: (by) => set((state) => ({ bears: state.bears + by })),
|
||||
}),
|
||||
{ name: 'bearStore' }
|
||||
)
|
||||
)
|
||||
{ name: 'bearStore' },
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -204,7 +204,7 @@ const useBearStore = create<BearState>()(
|
||||
myMiddlewares((set) => ({
|
||||
bears: 0,
|
||||
increase: (by) => set((state) => ({ bears: state.bears + by })),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
```
|
||||
|
||||
@ -245,12 +245,12 @@ type Logger = <
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
f: StateCreator<T, Mps, Mcs>,
|
||||
name?: string
|
||||
name?: string,
|
||||
) => StateCreator<T, Mps, Mcs>
|
||||
|
||||
type LoggerImpl = <T extends State>(
|
||||
f: StateCreator<T, [], []>,
|
||||
name?: string
|
||||
name?: string,
|
||||
) => StateCreator<T, [], []>
|
||||
|
||||
const loggerImpl: LoggerImpl = (f, name) => (set, get, store) => {
|
||||
@ -274,8 +274,8 @@ const useBearStore = create<BearState>()(
|
||||
bears: 0,
|
||||
increase: (by) => set((state) => ({ bears: state.bears + by })),
|
||||
}),
|
||||
'bear-store'
|
||||
)
|
||||
'bear-store',
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -298,7 +298,7 @@ type Foo = <
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
f: StateCreator<T, [...Mps, ['foo', A]], Mcs>,
|
||||
bar: A
|
||||
bar: A,
|
||||
) => StateCreator<T, Mps, [['foo', A], ...Mcs]>
|
||||
|
||||
declare module 'zustand' {
|
||||
@ -309,7 +309,7 @@ declare module 'zustand' {
|
||||
|
||||
type FooImpl = <T extends State, A>(
|
||||
f: StateCreator<T, [], []>,
|
||||
bar: A
|
||||
bar: A,
|
||||
) => StateCreator<T, [], []>
|
||||
|
||||
const fooImpl: FooImpl = (f, bar) => (set, get, _store) => {
|
||||
@ -445,11 +445,11 @@ const bearStore = createStore<BearState>()((set) => ({
|
||||
function useBearStore(): BearState
|
||||
function useBearStore<T>(
|
||||
selector: (state: BearState) => T,
|
||||
equals?: (a: T, b: T) => boolean
|
||||
equals?: (a: T, b: T) => boolean,
|
||||
): T
|
||||
function useBearStore<T>(
|
||||
selector?: (state: BearState) => T,
|
||||
equals?: (a: T, b: T) => boolean
|
||||
equals?: (a: T, b: T) => boolean,
|
||||
) {
|
||||
return useStore(bearStore, selector!, equals)
|
||||
}
|
||||
@ -473,12 +473,12 @@ const bearStore = createStore<BearState>()((set) => ({
|
||||
|
||||
const createBoundedUseStore = ((store) => (selector, equals) =>
|
||||
useStore(store, selector as never, equals)) as <S extends StoreApi<unknown>>(
|
||||
store: S
|
||||
store: S,
|
||||
) => {
|
||||
(): ExtractState<S>
|
||||
<T>(
|
||||
selector: (state: ExtractState<S>) => T,
|
||||
equals?: (a: T, b: T) => boolean
|
||||
equals?: (a: T, b: T) => boolean,
|
||||
): T
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ export const useCountStore = create<State & Actions>()(
|
||||
set((state) => {
|
||||
state.count -= qty
|
||||
}),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
```
|
||||
|
||||
@ -99,7 +99,7 @@ export const useTodoStore = create<State & Actions>()(
|
||||
set((state) => {
|
||||
state.todos[todoId].done = !state.todos[todoId].done
|
||||
}),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -30,8 +30,8 @@ export const useBearStore = create(
|
||||
{
|
||||
name: 'food-storage', // name of the item in the storage (must be unique)
|
||||
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -72,8 +72,8 @@ export const useBoundStore = create(
|
||||
{
|
||||
// ...
|
||||
storage: createJSONStorage(() => AsyncStorage),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -98,10 +98,10 @@ export const useBoundStore = create(
|
||||
// ...
|
||||
partialize: (state) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(state).filter(([key]) => !['foo'].includes(key))
|
||||
Object.entries(state).filter(([key]) => !['foo'].includes(key)),
|
||||
),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -117,8 +117,8 @@ export const useBoundStore = create(
|
||||
{
|
||||
// ...
|
||||
partialize: (state) => ({ foo: state.foo }),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -151,8 +151,8 @@ export const useBoundStore = create(
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -202,8 +202,8 @@ export const useBoundStore = create(
|
||||
|
||||
return persistedState
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -256,8 +256,8 @@ export const useBoundStore = create(
|
||||
// ...
|
||||
merge: (persistedState, currentState) =>
|
||||
deepMerge(currentState, persistedState),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -285,8 +285,8 @@ export const useBoundStore = create(
|
||||
{
|
||||
// ...
|
||||
skipHydration: true,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -473,7 +473,7 @@ import { useState, useEffect } from 'react'
|
||||
|
||||
const useStore = <T, F>(
|
||||
store: (callback: (state: T) => unknown) => unknown,
|
||||
callback: (state: T) => F
|
||||
callback: (state: T) => F,
|
||||
) => {
|
||||
const result = store(callback) as F
|
||||
const [data, setData] = useState<F>()
|
||||
@ -505,8 +505,8 @@ export const useBearStore = create(
|
||||
}),
|
||||
{
|
||||
name: 'food-storage',
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -625,8 +625,8 @@ export const useBoundStore = create(
|
||||
{
|
||||
name: 'food-storage', // unique name
|
||||
storage: createJSONStorage(() => storage),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -673,8 +673,8 @@ export const useBearStore = create<BearState>()(
|
||||
{
|
||||
name: 'food-storage',
|
||||
storage,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -728,8 +728,8 @@ export const useBearStore = create<MyState>()(
|
||||
name: 'food-storage', // name of item in the storage (must be unique)
|
||||
storage: createJSONStorage(() => sessionStorage), // (optional) by default the 'localStorage' is used
|
||||
partialize: (state) => ({ bears: state.bears }),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -96,7 +96,8 @@ export default function App({ initialBears }) {
|
||||
bears: initialBears,
|
||||
increase: () => set((state) => ({ bears: state.bears + 1 })),
|
||||
}))
|
||||
}>
|
||||
}
|
||||
>
|
||||
<Button />
|
||||
</Provider>
|
||||
)
|
||||
|
||||
@ -180,11 +180,7 @@
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"bracketSameLine": true,
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80
|
||||
"singleQuote": true
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
30
readme.md
30
readme.md
@ -98,12 +98,12 @@ const useBearStore = create((set) => ({
|
||||
|
||||
// Object pick, re-renders the component when either state.nuts or state.honey change
|
||||
const { nuts, honey } = useBearStore(
|
||||
useShallow((state) => ({ nuts: state.nuts, honey: state.honey }))
|
||||
useShallow((state) => ({ nuts: state.nuts, honey: state.honey })),
|
||||
)
|
||||
|
||||
// Array pick, re-renders the component when either state.nuts or state.honey change
|
||||
const [nuts, honey] = useBearStore(
|
||||
useShallow((state) => [state.nuts, state.honey])
|
||||
useShallow((state) => [state.nuts, state.honey]),
|
||||
)
|
||||
|
||||
// Mapped picks, re-renders the component when state.treats changes in order, count or keys
|
||||
@ -115,7 +115,7 @@ For more control over re-rendering, you may provide any custom equality function
|
||||
```jsx
|
||||
const treats = useBearStore(
|
||||
(state) => state.treats,
|
||||
(oldTreats, newTreats) => compare(oldTreats, newTreats)
|
||||
(oldTreats, newTreats) => compare(oldTreats, newTreats),
|
||||
)
|
||||
```
|
||||
|
||||
@ -196,7 +196,7 @@ subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubs
|
||||
```js
|
||||
import { subscribeWithSelector } from 'zustand/middleware'
|
||||
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
|
||||
@ -204,13 +204,13 @@ const unsub2 = useDogStore.subscribe((state) => state.paw, console.log)
|
||||
// Subscribe also exposes the previous value
|
||||
const unsub3 = useDogStore.subscribe(
|
||||
(state) => state.paw,
|
||||
(paw, previousPaw) => console.log(paw, previousPaw)
|
||||
(paw, previousPaw) => console.log(paw, previousPaw),
|
||||
)
|
||||
// Subscribe also supports an optional equality function
|
||||
const unsub4 = useDogStore.subscribe(
|
||||
(state) => [state.paw, state.fur],
|
||||
console.log,
|
||||
{ equalityFn: shallow }
|
||||
{ equalityFn: shallow },
|
||||
)
|
||||
// Subscribe and fire immediately
|
||||
const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, {
|
||||
@ -272,7 +272,7 @@ const useLushStore = create((set) => ({
|
||||
set(
|
||||
produce((state) => {
|
||||
state.lush.forest.contains = null
|
||||
})
|
||||
}),
|
||||
),
|
||||
}))
|
||||
|
||||
@ -296,14 +296,14 @@ const log = (config) => (set, get, api) =>
|
||||
console.log(' new state', get())
|
||||
},
|
||||
get,
|
||||
api
|
||||
api,
|
||||
)
|
||||
|
||||
const useBeeStore = create(
|
||||
log((set) => ({
|
||||
bees: false,
|
||||
setBees: (input) => set({ bees: input }),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
```
|
||||
|
||||
@ -324,8 +324,8 @@ const useFishStore = create(
|
||||
{
|
||||
name: 'food-storage', // name of the item in the storage (must be unique)
|
||||
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
@ -346,7 +346,7 @@ const useBeeStore = create(
|
||||
set((state) => {
|
||||
state.bees += by
|
||||
}),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
```
|
||||
|
||||
@ -503,9 +503,9 @@ const useBearStore = create<BearState>()(
|
||||
}),
|
||||
{
|
||||
name: 'bear-storage',
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ type UseContextStore<S extends StoreApi<unknown>> = {
|
||||
(): ExtractState<S>
|
||||
<U>(
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn?: (a: U, b: U) => boolean
|
||||
equalityFn?: (a: U, b: U) => boolean,
|
||||
): U
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ type WithoutCallSignature<T> = { [K in keyof T]: T[K] }
|
||||
function createContext<S extends StoreApi<unknown>>() {
|
||||
if (import.meta.env?.MODE !== 'production') {
|
||||
console.warn(
|
||||
"[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180."
|
||||
"[DEPRECATED] `context` will be removed in a future version. Instead use `import { createStore, useStore } from 'zustand'`. See: https://github.com/pmndrs/zustand/discussions/1180.",
|
||||
)
|
||||
}
|
||||
const ZustandContext = reactCreateContext<S | undefined>(undefined)
|
||||
@ -61,24 +61,24 @@ function createContext<S extends StoreApi<unknown>>() {
|
||||
return createElement(
|
||||
ZustandContext.Provider,
|
||||
{ value: storeRef.current },
|
||||
children
|
||||
children,
|
||||
)
|
||||
}
|
||||
|
||||
const useContextStore: UseContextStore<S> = <StateSlice = ExtractState<S>>(
|
||||
selector?: (state: ExtractState<S>) => StateSlice,
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean,
|
||||
) => {
|
||||
const store = useContext(ZustandContext)
|
||||
if (!store) {
|
||||
throw new Error(
|
||||
'Seems like you have not used zustand provider as an ancestor.'
|
||||
'Seems like you have not used zustand provider as an ancestor.',
|
||||
)
|
||||
}
|
||||
return useStoreWithEqualityFn(
|
||||
store,
|
||||
selector as (state: ExtractState<S>) => StateSlice,
|
||||
equalityFn
|
||||
equalityFn,
|
||||
)
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ function createContext<S extends StoreApi<unknown>>() {
|
||||
const store = useContext(ZustandContext)
|
||||
if (!store) {
|
||||
throw new Error(
|
||||
'Seems like you have not used zustand provider as an ancestor.'
|
||||
'Seems like you have not used zustand provider as an ancestor.',
|
||||
)
|
||||
}
|
||||
return useMemo<WithoutCallSignature<S>>(() => ({ ...store }), [store])
|
||||
|
||||
@ -9,7 +9,7 @@ type Combine = <
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
initialState: T,
|
||||
additionalStateCreator: StateCreator<T, Mps, Mcs, U>
|
||||
additionalStateCreator: StateCreator<T, Mps, Mcs, U>,
|
||||
) => StateCreator<Write<T, U>, Mps, Mcs>
|
||||
|
||||
export const combine: Combine =
|
||||
|
||||
@ -72,7 +72,7 @@ type Devtools = <
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
initializer: StateCreator<T, [...Mps, ['zustand/devtools', never]], Mcs>,
|
||||
devtoolsOptions?: DevtoolsOptions
|
||||
devtoolsOptions?: DevtoolsOptions,
|
||||
) => StateCreator<T, Mps, [['zustand/devtools', never], ...Mcs]>
|
||||
|
||||
declare module '../vanilla' {
|
||||
@ -84,7 +84,7 @@ declare module '../vanilla' {
|
||||
|
||||
type DevtoolsImpl = <T>(
|
||||
storeInitializer: StateCreator<T, [], []>,
|
||||
devtoolsOptions?: DevtoolsOptions
|
||||
devtoolsOptions?: DevtoolsOptions,
|
||||
) => StateCreator<T, [], []>
|
||||
|
||||
export type NamedSet<T> = WithDevtools<StoreApi<T>>['setState']
|
||||
@ -102,12 +102,12 @@ type ConnectionInformation = {
|
||||
const trackedConnections: Map<ConnectionName, ConnectionInformation> = new Map()
|
||||
|
||||
const getTrackedConnectionState = (
|
||||
name: string | undefined
|
||||
name: string | undefined,
|
||||
): Record<string, any> => {
|
||||
const api = trackedConnections.get(name)
|
||||
if (!api) return {}
|
||||
return Object.fromEntries(
|
||||
Object.entries(api.stores).map(([key, api]) => [key, api.getState()])
|
||||
Object.entries(api.stores).map(([key, api]) => [key, api.getState()]),
|
||||
)
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ const extractConnectionInformation = (
|
||||
extensionConnector: NonNullable<
|
||||
(typeof window)['__REDUX_DEVTOOLS_EXTENSION__']
|
||||
>,
|
||||
options: Omit<DevtoolsOptions, 'enabled' | 'anonymousActionType' | 'store'>
|
||||
options: Omit<DevtoolsOptions, 'enabled' | 'anonymousActionType' | 'store'>,
|
||||
) => {
|
||||
if (store === undefined) {
|
||||
return {
|
||||
@ -160,7 +160,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
if (!extensionConnector) {
|
||||
if (import.meta.env?.MODE !== 'production' && enabled) {
|
||||
console.warn(
|
||||
'[zustand devtools middleware] Please install/enable Redux devtools extension'
|
||||
'[zustand devtools middleware] Please install/enable Redux devtools extension',
|
||||
)
|
||||
}
|
||||
return fn(set, get, api)
|
||||
@ -191,7 +191,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
{
|
||||
...getTrackedConnectionState(options.name),
|
||||
[store]: api.getState(),
|
||||
}
|
||||
},
|
||||
)
|
||||
return r
|
||||
}
|
||||
@ -215,8 +215,8 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
key === connectionInformation.store
|
||||
? initialState
|
||||
: store.getState(),
|
||||
])
|
||||
)
|
||||
]),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -234,7 +234,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
) {
|
||||
console.warn(
|
||||
'[zustand devtools middleware] "__setState" action type is reserved ' +
|
||||
'to set state from the devtools. Avoid using it.'
|
||||
'to set state from the devtools. Avoid using it.',
|
||||
)
|
||||
didWarnAboutReservedActionType = true
|
||||
}
|
||||
@ -246,7 +246,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
connection as unknown as {
|
||||
// FIXME https://github.com/reduxjs/redux-devtools/issues/1097
|
||||
subscribe: (
|
||||
listener: (message: Message) => void
|
||||
listener: (message: Message) => void,
|
||||
) => (() => void) | undefined
|
||||
}
|
||||
).subscribe((message: any) => {
|
||||
@ -254,7 +254,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
case 'ACTION':
|
||||
if (typeof message.payload !== 'string') {
|
||||
console.error(
|
||||
'[zustand devtools middleware] Unsupported action format'
|
||||
'[zustand devtools middleware] Unsupported action format',
|
||||
)
|
||||
return
|
||||
}
|
||||
@ -272,7 +272,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
[zustand devtools middleware] Unsupported __setState action format.
|
||||
When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),
|
||||
and value of this only key should be a state object. Example: { "type": "__setState", "state": { "abc123Store": { "foo": "bar" } } }
|
||||
`
|
||||
`,
|
||||
)
|
||||
}
|
||||
const stateFromDevtools = (action.state as S)[store]
|
||||
@ -294,7 +294,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
if (!(api as any).dispatchFromDevtools) return
|
||||
if (typeof (api as any).dispatch !== 'function') return
|
||||
;(api as any).dispatch(action)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
case 'DISPATCH':
|
||||
@ -351,7 +351,7 @@ const devtoolsImpl: DevtoolsImpl =
|
||||
}
|
||||
connection?.send(
|
||||
null as any, // FIXME no-any
|
||||
nextLiftedState
|
||||
nextLiftedState,
|
||||
)
|
||||
return
|
||||
}
|
||||
@ -374,7 +374,7 @@ const parseJsonThen = <T>(stringified: string, f: (parsed: T) => void) => {
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'[zustand devtools middleware] Could not parse the received json',
|
||||
e
|
||||
e,
|
||||
)
|
||||
}
|
||||
if (parsed !== undefined) f(parsed as T)
|
||||
|
||||
@ -7,7 +7,7 @@ type Immer = <
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
initializer: StateCreator<T, [...Mps, ['zustand/immer', never]], Mcs>
|
||||
initializer: StateCreator<T, [...Mps, ['zustand/immer', never]], Mcs>,
|
||||
) => StateCreator<T, Mps, [['zustand/immer', never], ...Mcs]>
|
||||
|
||||
declare module '../vanilla' {
|
||||
@ -50,7 +50,7 @@ type StoreImmer<S> = S extends {
|
||||
: never
|
||||
|
||||
type ImmerImpl = <T>(
|
||||
storeInitializer: StateCreator<T, [], []>
|
||||
storeInitializer: StateCreator<T, [], []>,
|
||||
) => StateCreator<T, [], []>
|
||||
|
||||
const immerImpl: ImmerImpl = (initializer) => (set, get, store) => {
|
||||
|
||||
@ -17,7 +17,7 @@ export type StorageValue<S> = {
|
||||
|
||||
export interface PersistStorage<S> {
|
||||
getItem: (
|
||||
name: string
|
||||
name: string,
|
||||
) => StorageValue<S> | null | Promise<StorageValue<S> | null>
|
||||
setItem: (name: string, value: StorageValue<S>) => void | Promise<void>
|
||||
removeItem: (name: string) => void | Promise<void>
|
||||
@ -30,7 +30,7 @@ type JsonStorageOptions = {
|
||||
|
||||
export function createJSONStorage<S>(
|
||||
getStorage: () => StateStorage,
|
||||
options?: JsonStorageOptions
|
||||
options?: JsonStorageOptions,
|
||||
): PersistStorage<S> | undefined {
|
||||
let storage: StateStorage | undefined
|
||||
try {
|
||||
@ -56,7 +56,7 @@ export function createJSONStorage<S>(
|
||||
setItem: (name, newValue) =>
|
||||
(storage as StateStorage).setItem(
|
||||
name,
|
||||
JSON.stringify(newValue, options?.replacer)
|
||||
JSON.stringify(newValue, options?.replacer),
|
||||
),
|
||||
removeItem: (name) => (storage as StateStorage).removeItem(name),
|
||||
}
|
||||
@ -92,7 +92,7 @@ export interface PersistOptions<S, PersistedState = S> {
|
||||
* @default JSON.parse
|
||||
*/
|
||||
deserialize?: (
|
||||
str: string
|
||||
str: string,
|
||||
) => StorageValue<PersistedState> | Promise<StorageValue<PersistedState>>
|
||||
/**
|
||||
* Use a custom persist storage.
|
||||
@ -115,7 +115,7 @@ export interface PersistOptions<S, PersistedState = S> {
|
||||
* The returned function will be called after the state rehydration or when an error occurred.
|
||||
*/
|
||||
onRehydrateStorage?: (
|
||||
state: S
|
||||
state: S,
|
||||
) => ((state?: S, error?: unknown) => void) | void
|
||||
/**
|
||||
* If the stored state's version mismatch the one specified here, the storage will not be used.
|
||||
@ -160,16 +160,16 @@ type StorePersist<S, Ps> = {
|
||||
|
||||
type Thenable<Value> = {
|
||||
then<V>(
|
||||
onFulfilled: (value: Value) => V | Promise<V> | Thenable<V>
|
||||
onFulfilled: (value: Value) => V | Promise<V> | Thenable<V>,
|
||||
): Thenable<V>
|
||||
catch<V>(
|
||||
onRejected: (reason: Error) => V | Promise<V> | Thenable<V>
|
||||
onRejected: (reason: Error) => V | Promise<V> | Thenable<V>,
|
||||
): Thenable<V>
|
||||
}
|
||||
|
||||
const toThenable =
|
||||
<Result, Input>(
|
||||
fn: (input: Input) => Result | Promise<Result> | Thenable<Result>
|
||||
fn: (input: Input) => Result | Promise<Result> | Thenable<Result>,
|
||||
) =>
|
||||
(input: Input): Thenable<Result> => {
|
||||
try {
|
||||
@ -227,12 +227,12 @@ const oldImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
return config(
|
||||
(...args) => {
|
||||
console.warn(
|
||||
`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
|
||||
`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`,
|
||||
)
|
||||
set(...args)
|
||||
},
|
||||
get,
|
||||
api
|
||||
api,
|
||||
)
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ const oldImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
let errorInSync: Error | undefined
|
||||
const thenable = thenableSerialize({ state, version: options.version })
|
||||
.then((serializedValue) =>
|
||||
(storage as StateStorage).setItem(options.name, serializedValue)
|
||||
(storage as StateStorage).setItem(options.name, serializedValue),
|
||||
)
|
||||
.catch((e) => {
|
||||
errorInSync = e
|
||||
@ -268,7 +268,7 @@ const oldImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
void setItem()
|
||||
},
|
||||
get,
|
||||
api
|
||||
api,
|
||||
)
|
||||
|
||||
// a workaround to solve the issue of not storing rehydrated state in sync storage
|
||||
@ -302,11 +302,11 @@ const oldImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
if (options.migrate) {
|
||||
return options.migrate(
|
||||
deserializedStorageValue.state,
|
||||
deserializedStorageValue.version
|
||||
deserializedStorageValue.version,
|
||||
)
|
||||
}
|
||||
console.error(
|
||||
`State loaded from storage couldn't be migrated since no migrate function was provided`
|
||||
`State loaded from storage couldn't be migrated since no migrate function was provided`,
|
||||
)
|
||||
} else {
|
||||
return deserializedStorageValue.state
|
||||
@ -316,7 +316,7 @@ const oldImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
.then((migratedState) => {
|
||||
stateFromStorage = options.merge(
|
||||
migratedState as S,
|
||||
get() ?? configResult
|
||||
get() ?? configResult,
|
||||
)
|
||||
|
||||
set(stateFromStorage as S, true)
|
||||
@ -392,12 +392,12 @@ const newImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
return config(
|
||||
(...args) => {
|
||||
console.warn(
|
||||
`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
|
||||
`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`,
|
||||
)
|
||||
set(...args)
|
||||
},
|
||||
get,
|
||||
api
|
||||
api,
|
||||
)
|
||||
}
|
||||
|
||||
@ -422,7 +422,7 @@ const newImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
void setItem()
|
||||
},
|
||||
get,
|
||||
api
|
||||
api,
|
||||
)
|
||||
|
||||
// a workaround to solve the issue of not storing rehydrated state in sync storage
|
||||
@ -456,11 +456,11 @@ const newImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
if (options.migrate) {
|
||||
return options.migrate(
|
||||
deserializedStorageValue.state,
|
||||
deserializedStorageValue.version
|
||||
deserializedStorageValue.version,
|
||||
)
|
||||
}
|
||||
console.error(
|
||||
`State loaded from storage couldn't be migrated since no migrate function was provided`
|
||||
`State loaded from storage couldn't be migrated since no migrate function was provided`,
|
||||
)
|
||||
} else {
|
||||
return deserializedStorageValue.state
|
||||
@ -470,7 +470,7 @@ const newImpl: PersistImpl = (config, baseOptions) => (set, get, api) => {
|
||||
.then((migratedState) => {
|
||||
stateFromStorage = options.merge(
|
||||
migratedState as S,
|
||||
get() ?? configResult
|
||||
get() ?? configResult,
|
||||
)
|
||||
|
||||
set(stateFromStorage as S, true)
|
||||
@ -546,7 +546,7 @@ const persistImpl: PersistImpl = (config, baseOptions) => {
|
||||
) {
|
||||
if (import.meta.env?.MODE !== 'production') {
|
||||
console.warn(
|
||||
'[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.'
|
||||
'[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.',
|
||||
)
|
||||
}
|
||||
return oldImpl(config, baseOptions)
|
||||
@ -561,7 +561,7 @@ type Persist = <
|
||||
U = T,
|
||||
>(
|
||||
initializer: StateCreator<T, [...Mps, ['zustand/persist', unknown]], Mcs>,
|
||||
options: PersistOptions<T, U>
|
||||
options: PersistOptions<T, U>,
|
||||
) => StateCreator<T, Mps, [['zustand/persist', U], ...Mcs]>
|
||||
|
||||
declare module '../vanilla' {
|
||||
@ -578,7 +578,7 @@ type WithPersist<S, A> = S extends { getState: () => infer T }
|
||||
|
||||
type PersistImpl = <T>(
|
||||
storeInitializer: StateCreator<T, [], []>,
|
||||
options: PersistOptions<T, T>
|
||||
options: PersistOptions<T, T>,
|
||||
) => StateCreator<T, [], []>
|
||||
|
||||
export const persist = persistImpl as unknown as Persist
|
||||
|
||||
@ -22,7 +22,7 @@ type Redux = <
|
||||
Cms extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
reducer: (state: T, action: A) => T,
|
||||
initialState: T
|
||||
initialState: T,
|
||||
) => StateCreator<Write<T, ReduxState<A>>, Cms, [['zustand/redux', A]]>
|
||||
|
||||
declare module '../vanilla' {
|
||||
@ -33,7 +33,7 @@ declare module '../vanilla' {
|
||||
|
||||
type ReduxImpl = <T, A extends Action>(
|
||||
reducer: (state: T, action: A) => T,
|
||||
initialState: T
|
||||
initialState: T,
|
||||
) => StateCreator<T & ReduxState<A>, [], []>
|
||||
|
||||
const reduxImpl: ReduxImpl = (reducer, initial) => (set, _get, api) => {
|
||||
|
||||
@ -9,7 +9,7 @@ type SubscribeWithSelector = <
|
||||
T,
|
||||
[...Mps, ['zustand/subscribeWithSelector', never]],
|
||||
Mcs
|
||||
>
|
||||
>,
|
||||
) => StateCreator<T, Mps, [['zustand/subscribeWithSelector', never], ...Mcs]>
|
||||
|
||||
type Write<T, U> = Omit<T, keyof U> & U
|
||||
@ -34,13 +34,13 @@ type StoreSubscribeWithSelector<T> = {
|
||||
options?: {
|
||||
equalityFn?: (a: U, b: U) => boolean
|
||||
fireImmediately?: boolean
|
||||
}
|
||||
},
|
||||
): () => void
|
||||
}
|
||||
}
|
||||
|
||||
type SubscribeWithSelectorImpl = <T extends object>(
|
||||
storeInitializer: StateCreator<T, [], []>
|
||||
storeInitializer: StateCreator<T, [], []>,
|
||||
) => StateCreator<T, [], []>
|
||||
|
||||
const subscribeWithSelectorImpl: SubscribeWithSelectorImpl =
|
||||
|
||||
22
src/react.ts
22
src/react.ts
@ -29,12 +29,12 @@ type WithReact<S extends ReadonlyStoreApi<unknown>> = S & {
|
||||
let didWarnAboutEqualityFn = false
|
||||
|
||||
export function useStore<S extends WithReact<StoreApi<unknown>>>(
|
||||
api: S
|
||||
api: S,
|
||||
): ExtractState<S>
|
||||
|
||||
export function useStore<S extends WithReact<StoreApi<unknown>>, U>(
|
||||
api: S,
|
||||
selector: (state: ExtractState<S>) => U
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
): U
|
||||
|
||||
/**
|
||||
@ -44,13 +44,13 @@ export function useStore<S extends WithReact<StoreApi<unknown>>, U>(
|
||||
export function useStore<S extends WithReact<StoreApi<unknown>>, U>(
|
||||
api: S,
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn: ((a: U, b: U) => boolean) | undefined
|
||||
equalityFn: ((a: U, b: U) => boolean) | undefined,
|
||||
): U
|
||||
|
||||
export function useStore<TState, StateSlice>(
|
||||
api: WithReact<StoreApi<TState>>,
|
||||
selector: (state: TState) => StateSlice = api.getState as any,
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean,
|
||||
) {
|
||||
if (
|
||||
import.meta.env?.MODE !== 'production' &&
|
||||
@ -58,7 +58,7 @@ export function useStore<TState, StateSlice>(
|
||||
!didWarnAboutEqualityFn
|
||||
) {
|
||||
console.warn(
|
||||
"[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"
|
||||
"[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937",
|
||||
)
|
||||
didWarnAboutEqualityFn = true
|
||||
}
|
||||
@ -67,7 +67,7 @@ export function useStore<TState, StateSlice>(
|
||||
api.getState,
|
||||
api.getServerState || api.getState,
|
||||
selector,
|
||||
equalityFn
|
||||
equalityFn,
|
||||
)
|
||||
useDebugValue(slice)
|
||||
return slice
|
||||
@ -81,16 +81,16 @@ export type UseBoundStore<S extends WithReact<ReadonlyStoreApi<unknown>>> = {
|
||||
*/
|
||||
<U>(
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn: (a: U, b: U) => boolean
|
||||
equalityFn: (a: U, b: U) => boolean,
|
||||
): U
|
||||
} & S
|
||||
|
||||
type Create = {
|
||||
<T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
): UseBoundStore<Mutate<StoreApi<T>, Mos>>
|
||||
<T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
) => UseBoundStore<Mutate<StoreApi<T>, Mos>>
|
||||
/**
|
||||
* @deprecated Use `useStore` hook to bind store
|
||||
@ -104,7 +104,7 @@ const createImpl = <T>(createState: StateCreator<T, [], []>) => {
|
||||
typeof createState !== 'function'
|
||||
) {
|
||||
console.warn(
|
||||
"[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`."
|
||||
"[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.",
|
||||
)
|
||||
}
|
||||
const api =
|
||||
@ -127,7 +127,7 @@ export const create = (<T>(createState: StateCreator<T, [], []> | undefined) =>
|
||||
export default ((createState: any) => {
|
||||
if (import.meta.env?.MODE !== 'production') {
|
||||
console.warn(
|
||||
"[DEPRECATED] Default export is deprecated. Instead use `import { create } from 'zustand'`."
|
||||
"[DEPRECATED] Default export is deprecated. Instead use `import { create } from 'zustand'`.",
|
||||
)
|
||||
}
|
||||
return create(createState)
|
||||
|
||||
@ -10,7 +10,7 @@ import { shallow } from './vanilla/shallow.ts'
|
||||
export default ((objA, objB) => {
|
||||
if (import.meta.env?.MODE !== 'production') {
|
||||
console.warn(
|
||||
"[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`."
|
||||
"[DEPRECATED] Default export is deprecated. Instead use `import { shallow } from 'zustand/shallow'`.",
|
||||
)
|
||||
}
|
||||
return shallow(objA, objB)
|
||||
|
||||
@ -27,7 +27,7 @@ type WithReact<S extends ReadonlyStoreApi<unknown>> = S & {
|
||||
}
|
||||
|
||||
export function useStoreWithEqualityFn<S extends WithReact<StoreApi<unknown>>>(
|
||||
api: S
|
||||
api: S,
|
||||
): ExtractState<S>
|
||||
|
||||
export function useStoreWithEqualityFn<
|
||||
@ -36,20 +36,20 @@ export function useStoreWithEqualityFn<
|
||||
>(
|
||||
api: S,
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn?: (a: U, b: U) => boolean
|
||||
equalityFn?: (a: U, b: U) => boolean,
|
||||
): U
|
||||
|
||||
export function useStoreWithEqualityFn<TState, StateSlice>(
|
||||
api: WithReact<StoreApi<TState>>,
|
||||
selector: (state: TState) => StateSlice = api.getState as any,
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean
|
||||
equalityFn?: (a: StateSlice, b: StateSlice) => boolean,
|
||||
) {
|
||||
const slice = useSyncExternalStoreWithSelector(
|
||||
api.subscribe,
|
||||
api.getState,
|
||||
api.getServerState || api.getState,
|
||||
selector,
|
||||
equalityFn
|
||||
equalityFn,
|
||||
)
|
||||
useDebugValue(slice)
|
||||
return slice
|
||||
@ -61,30 +61,30 @@ export type UseBoundStoreWithEqualityFn<
|
||||
(): ExtractState<S>
|
||||
<U>(
|
||||
selector: (state: ExtractState<S>) => U,
|
||||
equalityFn?: (a: U, b: U) => boolean
|
||||
equalityFn?: (a: U, b: U) => boolean,
|
||||
): U
|
||||
} & S
|
||||
|
||||
type CreateWithEqualityFn = {
|
||||
<T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean,
|
||||
): UseBoundStoreWithEqualityFn<Mutate<StoreApi<T>, Mos>>
|
||||
<T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean,
|
||||
) => UseBoundStoreWithEqualityFn<Mutate<StoreApi<T>, Mos>>
|
||||
}
|
||||
|
||||
const createWithEqualityFnImpl = <T>(
|
||||
createState: StateCreator<T, [], []>,
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean,
|
||||
) => {
|
||||
const api = createStore(createState)
|
||||
|
||||
const useBoundStoreWithEqualityFn: any = (
|
||||
selector?: any,
|
||||
equalityFn = defaultEqualityFn
|
||||
equalityFn = defaultEqualityFn,
|
||||
) => useStoreWithEqualityFn(api, selector, equalityFn)
|
||||
|
||||
Object.assign(useBoundStoreWithEqualityFn, api)
|
||||
@ -94,7 +94,7 @@ const createWithEqualityFnImpl = <T>(
|
||||
|
||||
export const createWithEqualityFn = (<T>(
|
||||
createState: StateCreator<T, [], []> | undefined,
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean
|
||||
defaultEqualityFn?: <U>(a: U, b: U) => boolean,
|
||||
) =>
|
||||
createState
|
||||
? createWithEqualityFnImpl(createState, defaultEqualityFn)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
type SetStateInternal<T> = {
|
||||
_(
|
||||
partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],
|
||||
replace?: boolean | undefined
|
||||
replace?: boolean | undefined,
|
||||
): void
|
||||
}['_']
|
||||
|
||||
@ -33,7 +33,7 @@ export type StateCreator<
|
||||
> = ((
|
||||
setState: Get<Mutate<StoreApi<T>, Mis>, 'setState', never>,
|
||||
getState: Get<Mutate<StoreApi<T>, Mis>, 'getState', never>,
|
||||
store: Mutate<StoreApi<T>, Mis>
|
||||
store: Mutate<StoreApi<T>, Mis>,
|
||||
) => U) & { $$storeMutators?: Mos }
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface
|
||||
@ -42,11 +42,11 @@ export type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>
|
||||
|
||||
type CreateStore = {
|
||||
<T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
): Mutate<StoreApi<T>, Mos>
|
||||
|
||||
<T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
) => Mutate<StoreApi<T>, Mos>
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ type CreateStoreImpl = <
|
||||
T,
|
||||
Mos extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
initializer: StateCreator<T, [], Mos>
|
||||
initializer: StateCreator<T, [], Mos>,
|
||||
) => Mutate<StoreApi<T>, Mos>
|
||||
|
||||
const createStoreImpl: CreateStoreImpl = (createState) => {
|
||||
@ -91,7 +91,7 @@ const createStoreImpl: CreateStoreImpl = (createState) => {
|
||||
const destroy: StoreApi<TState>['destroy'] = () => {
|
||||
if (import.meta.env?.MODE !== 'production') {
|
||||
console.warn(
|
||||
'[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected.'
|
||||
'[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected.',
|
||||
)
|
||||
}
|
||||
listeners.clear()
|
||||
@ -111,7 +111,7 @@ export const createStore = ((createState) =>
|
||||
export default ((createState) => {
|
||||
if (import.meta.env?.MODE !== 'production') {
|
||||
console.warn(
|
||||
"[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'."
|
||||
"[DEPRECATED] Default export is deprecated. Instead use import { createStore } from 'zustand/vanilla'.",
|
||||
)
|
||||
}
|
||||
return createStore(createState)
|
||||
@ -165,7 +165,7 @@ export type Subscribe<T extends State> = {
|
||||
export type SetState<T extends State> = {
|
||||
_(
|
||||
partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],
|
||||
replace?: boolean | undefined
|
||||
replace?: boolean | undefined,
|
||||
): void
|
||||
}['_']
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ it('uses the store with no args', async () => {
|
||||
const { findByText } = render(
|
||||
<>
|
||||
<Counter />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -83,7 +83,7 @@ it('uses the store with selectors', async () => {
|
||||
const { findByText } = render(
|
||||
<>
|
||||
<Counter />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -92,7 +92,7 @@ it('uses the store with selectors', async () => {
|
||||
it('uses the store with a selector and equality checker', async () => {
|
||||
const useBoundStore = createWithEqualityFn(
|
||||
() => ({ item: { value: 0 } }),
|
||||
Object.is
|
||||
Object.is,
|
||||
)
|
||||
const { setState } = useBoundStore
|
||||
let renderCount = 0
|
||||
@ -101,7 +101,7 @@ it('uses the store with a selector and equality checker', async () => {
|
||||
// Prevent re-render if new value === 1.
|
||||
const item = useBoundStore(
|
||||
(s) => s.item,
|
||||
(_, newItem) => newItem.value === 1
|
||||
(_, newItem) => newItem.value === 1,
|
||||
)
|
||||
return (
|
||||
<div>
|
||||
@ -113,7 +113,7 @@ it('uses the store with a selector and equality checker', async () => {
|
||||
const { findByText } = render(
|
||||
<>
|
||||
<Component />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('renderCount: 1, value: 0')
|
||||
@ -151,7 +151,7 @@ it('only re-renders if selected state has changed', async () => {
|
||||
<>
|
||||
<Counter />
|
||||
<Control />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
fireEvent.click(getByText('button'))
|
||||
@ -182,7 +182,7 @@ it('can batch updates', async () => {
|
||||
const { findByText } = render(
|
||||
<>
|
||||
<Counter />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('count: 2')
|
||||
@ -203,14 +203,14 @@ it('can update the selector', async () => {
|
||||
const { findByText, rerender } = render(
|
||||
<StrictMode>
|
||||
<Component selector={(s) => s.one} />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('one')
|
||||
|
||||
rerender(
|
||||
<StrictMode>
|
||||
<Component selector={(s) => s.two} />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('two')
|
||||
})
|
||||
@ -220,7 +220,7 @@ it('can update the equality checker', async () => {
|
||||
type Props = { equalityFn: (a: State, b: State) => boolean }
|
||||
const useBoundStore = createWithEqualityFn<State>(
|
||||
() => ({ value: 0 }),
|
||||
Object.is
|
||||
Object.is,
|
||||
)
|
||||
const { setState } = useBoundStore
|
||||
const selector = (s: State) => s
|
||||
@ -239,7 +239,7 @@ it('can update the equality checker', async () => {
|
||||
const { findByText, rerender } = render(
|
||||
<>
|
||||
<Component equalityFn={() => false} />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
// This will cause a re-render due to the equality checker.
|
||||
@ -250,7 +250,7 @@ it('can update the equality checker', async () => {
|
||||
rerender(
|
||||
<>
|
||||
<Component equalityFn={() => true} />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
// This will NOT cause a re-render due to the equality checker.
|
||||
@ -267,7 +267,7 @@ it('can call useBoundStore with progressively more arguments', async () => {
|
||||
|
||||
const useBoundStore = createWithEqualityFn<State>(
|
||||
() => ({ value: 0 }),
|
||||
Object.is
|
||||
Object.is,
|
||||
)
|
||||
const { setState } = useBoundStore
|
||||
|
||||
@ -285,7 +285,7 @@ it('can call useBoundStore with progressively more arguments', async () => {
|
||||
const { findByText, rerender } = render(
|
||||
<>
|
||||
<Component />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
await findByText('renderCount: 1, value: {"value":0}')
|
||||
|
||||
@ -293,7 +293,7 @@ it('can call useBoundStore with progressively more arguments', async () => {
|
||||
rerender(
|
||||
<>
|
||||
<Component selector={(s) => s.value} />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
await findByText('renderCount: 2, value: 0')
|
||||
|
||||
@ -304,7 +304,7 @@ it('can call useBoundStore with progressively more arguments', async () => {
|
||||
selector={(s) => s.value}
|
||||
equalityFn={(oldV, newV) => oldV > newV}
|
||||
/>
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
// Should not cause a re-render because new value is less than previous.
|
||||
@ -352,7 +352,7 @@ it('can throw an error in selector', async () => {
|
||||
<ErrorBoundary>
|
||||
<Component />
|
||||
</ErrorBoundary>
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('no error')
|
||||
|
||||
@ -400,7 +400,7 @@ it('can throw an error in equality checker', async () => {
|
||||
<ErrorBoundary>
|
||||
<Component />
|
||||
</ErrorBoundary>
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('no error')
|
||||
|
||||
@ -464,7 +464,7 @@ it('can set the store without merging', () => {
|
||||
const { setState, getState } = create<{ a: number } | { b: number }>(
|
||||
(_set) => ({
|
||||
a: 1,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
// Should override the state instead of merging.
|
||||
@ -512,7 +512,7 @@ it('only calls selectors when necessary', async () => {
|
||||
const { rerender, findByText } = render(
|
||||
<>
|
||||
<Component />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
await findByText('inline: 1')
|
||||
await findByText('static: 1')
|
||||
@ -520,7 +520,7 @@ it('only calls selectors when necessary', async () => {
|
||||
rerender(
|
||||
<>
|
||||
<Component />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
await findByText('inline: 2')
|
||||
await findByText('static: 1')
|
||||
@ -571,7 +571,7 @@ it('ensures parent components subscribe before children', async () => {
|
||||
const { getByText, findByText } = render(
|
||||
<StrictMode>
|
||||
<Parent />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
fireEvent.click(getByText('change state'))
|
||||
@ -614,7 +614,7 @@ it('ensures the correct subscriber is removed on unmount', async () => {
|
||||
const { findAllByText } = render(
|
||||
<>
|
||||
<Component />
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
expect((await findAllByText('count: 1')).length).toBe(2)
|
||||
@ -643,14 +643,14 @@ it('ensures a subscriber is not mistakenly overwritten', async () => {
|
||||
const { findAllByText, rerender } = render(
|
||||
<StrictMode>
|
||||
<Count1 />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
// Replace 1st subscriber with another.
|
||||
rerender(
|
||||
<StrictMode>
|
||||
<Count2 />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
// Add 2 additional subscribers.
|
||||
@ -659,7 +659,7 @@ it('ensures a subscriber is not mistakenly overwritten', async () => {
|
||||
<Count2 />
|
||||
<Count1 />
|
||||
<Count1 />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
// Call all subscribers
|
||||
@ -686,7 +686,7 @@ it('works with non-object state', async () => {
|
||||
const { getByText, findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
|
||||
@ -43,7 +43,7 @@ it('creates and uses context store', async () => {
|
||||
<Provider createStore={createStore}>
|
||||
<Counter />
|
||||
</Provider>
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -70,7 +70,7 @@ it('uses context store with selectors', async () => {
|
||||
<Provider createStore={createStore}>
|
||||
<Counter />
|
||||
</Provider>
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -82,7 +82,7 @@ it('uses context store api', async () => {
|
||||
subscribeWithSelector((set) => ({
|
||||
count: 0,
|
||||
inc: () => set((state) => ({ count: state.count + 1 })),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
|
||||
type CustomStore = ReturnType<typeof createStore>
|
||||
@ -95,9 +95,9 @@ it('uses context store api', async () => {
|
||||
() =>
|
||||
storeApi.subscribe(
|
||||
(state) => state.count,
|
||||
() => setCount(storeApi.getState().count)
|
||||
() => setCount(storeApi.getState().count),
|
||||
),
|
||||
[storeApi]
|
||||
[storeApi],
|
||||
)
|
||||
useEffect(() => {
|
||||
storeApi.setState({ count: storeApi.getState().count + 1 })
|
||||
@ -116,7 +116,7 @@ it('uses context store api', async () => {
|
||||
<Provider createStore={createStore}>
|
||||
<Counter />
|
||||
</Provider>
|
||||
</>
|
||||
</>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -152,7 +152,7 @@ it('throws error when not using provider', async () => {
|
||||
<ErrorBoundary>
|
||||
<Component />
|
||||
</ErrorBoundary>
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('errored')
|
||||
})
|
||||
@ -169,6 +169,6 @@ const expectAreTypesEqual = <A, B>() => ({
|
||||
toBe: (
|
||||
_: (<T>() => T extends B ? 1 : 0) extends <T>() => T extends A ? 1 : 0
|
||||
? true
|
||||
: false
|
||||
: false,
|
||||
) => {},
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
set((state) => {
|
||||
state.count = get().count + 1
|
||||
}),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -58,7 +58,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
TestComponent
|
||||
|
||||
const _testSubtyping: StoreApi<object> = createStore(
|
||||
immer(() => ({ count: 0 }))
|
||||
immer(() => ({ count: 0 })),
|
||||
)
|
||||
})
|
||||
|
||||
@ -73,8 +73,8 @@ describe('counter state spec (single middleware)', () => {
|
||||
return state
|
||||
}
|
||||
},
|
||||
{ count: 0 }
|
||||
)
|
||||
{ count: 0 },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -86,7 +86,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
TestComponent
|
||||
|
||||
const _testSubtyping: StoreApi<object> = createStore(
|
||||
redux((x) => x, { count: 0 })
|
||||
redux((x) => x, { count: 0 }),
|
||||
)
|
||||
})
|
||||
|
||||
@ -97,8 +97,8 @@ describe('counter state spec (single middleware)', () => {
|
||||
count: 0,
|
||||
inc: () => set({ count: get().count + 1 }, false, 'inc'),
|
||||
}),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -113,7 +113,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
TestComponent
|
||||
|
||||
const _testSubtyping: StoreApi<object> = createStore(
|
||||
devtools(() => ({ count: 0 }))
|
||||
devtools(() => ({ count: 0 })),
|
||||
)
|
||||
})
|
||||
|
||||
@ -122,7 +122,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
subscribeWithSelector((set, get) => ({
|
||||
count: 1,
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -133,14 +133,14 @@ describe('counter state spec (single middleware)', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
return <></>
|
||||
}
|
||||
TestComponent
|
||||
|
||||
const _testSubtyping: StoreApi<object> = createStore(
|
||||
subscribeWithSelector(() => ({ count: 0 }))
|
||||
subscribeWithSelector(() => ({ count: 0 })),
|
||||
)
|
||||
})
|
||||
|
||||
@ -148,7 +148,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
const useBoundStore = create(
|
||||
combine({ count: 1 }, (set, get) => ({
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -162,7 +162,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
TestComponent
|
||||
|
||||
const _testSubtyping: StoreApi<object> = createStore(
|
||||
combine({ count: 0 }, () => ({}))
|
||||
combine({ count: 0 }, () => ({})),
|
||||
)
|
||||
})
|
||||
|
||||
@ -173,8 +173,8 @@ describe('counter state spec (single middleware)', () => {
|
||||
count: 1,
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -189,7 +189,7 @@ describe('counter state spec (single middleware)', () => {
|
||||
TestComponent
|
||||
|
||||
const _testSubtyping: StoreApi<object> = createStore(
|
||||
persist(() => ({ count: 0 }), { name: 'prefix' })
|
||||
persist(() => ({ count: 0 }), { name: 'prefix' }),
|
||||
)
|
||||
})
|
||||
|
||||
@ -200,8 +200,8 @@ describe('counter state spec (single middleware)', () => {
|
||||
count: 1,
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}),
|
||||
{ name: 'prefix', partialize: (s) => s.count }
|
||||
)
|
||||
{ name: 'prefix', partialize: (s) => s.count },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -227,8 +227,8 @@ describe('counter state spec (single middleware)', () => {
|
||||
count: 1,
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -256,12 +256,12 @@ describe('counter state spec (double middleware)', () => {
|
||||
state.count = get().count + 1
|
||||
},
|
||||
false,
|
||||
{ type: 'inc', by: 1 }
|
||||
{ type: 'inc', by: 1 },
|
||||
),
|
||||
}),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -288,10 +288,10 @@ describe('counter state spec (double middleware)', () => {
|
||||
return state
|
||||
}
|
||||
},
|
||||
{ count: 0 }
|
||||
{ count: 0 },
|
||||
),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -310,8 +310,8 @@ describe('counter state spec (double middleware)', () => {
|
||||
combine({ count: 1 }, (set, get) => ({
|
||||
inc: () => set({ count: get().count + 1 }, false, 'inc'),
|
||||
})),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -331,8 +331,8 @@ describe('counter state spec (double middleware)', () => {
|
||||
subscribeWithSelector(
|
||||
combine({ count: 1 }, (set, get) => ({
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}))
|
||||
)
|
||||
})),
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -343,7 +343,7 @@ describe('counter state spec (double middleware)', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
return <></>
|
||||
}
|
||||
@ -357,8 +357,8 @@ describe('counter state spec (double middleware)', () => {
|
||||
count: 1,
|
||||
inc: () => set({ count: get().count + 1 }, false, 'inc'),
|
||||
})),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -369,7 +369,7 @@ describe('counter state spec (double middleware)', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
useBoundStore.setState({ count: 0 }, false, 'reset')
|
||||
return <></>
|
||||
@ -385,10 +385,10 @@ describe('counter state spec (double middleware)', () => {
|
||||
count: 1,
|
||||
inc: () => set({ count: get().count + 1 }, false, 'inc'),
|
||||
}),
|
||||
{ name: 'count' }
|
||||
{ name: 'count' },
|
||||
),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -417,10 +417,10 @@ describe('counter state spec (triple middleware)', () => {
|
||||
state.count = get().count + 1
|
||||
}),
|
||||
})),
|
||||
{ name: 'count' }
|
||||
{ name: 'count' },
|
||||
),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -442,10 +442,10 @@ describe('counter state spec (triple middleware)', () => {
|
||||
subscribeWithSelector(
|
||||
combine({ count: 1 }, (set, get) => ({
|
||||
inc: () => set({ count: get().count + 1 }, false, 'inc'),
|
||||
}))
|
||||
})),
|
||||
),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -456,7 +456,7 @@ describe('counter state spec (triple middleware)', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
useBoundStore.setState({ count: 0 }, false, 'reset')
|
||||
return <></>
|
||||
@ -473,11 +473,11 @@ describe('counter state spec (triple middleware)', () => {
|
||||
count: 0,
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}),
|
||||
{ name: 'count' }
|
||||
)
|
||||
{ name: 'count' },
|
||||
),
|
||||
),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -488,7 +488,7 @@ describe('counter state spec (triple middleware)', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
useBoundStore.setState({ count: 0 }, false, 'reset')
|
||||
useBoundStore.persist.hasHydrated()
|
||||
@ -511,11 +511,11 @@ describe('counter state spec (quadruple middleware)', () => {
|
||||
state.count = get().count + 1
|
||||
}),
|
||||
})),
|
||||
{ name: 'count' }
|
||||
)
|
||||
{ name: 'count' },
|
||||
),
|
||||
),
|
||||
{ name: 'prefix' }
|
||||
)
|
||||
{ name: 'prefix' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -526,7 +526,7 @@ describe('counter state spec (quadruple middleware)', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
useBoundStore.setState({ count: 0 }, false, 'reset')
|
||||
useBoundStore.persist.hasHydrated()
|
||||
@ -544,9 +544,9 @@ describe('more complex state spec with subscribeWithSelector', () => {
|
||||
() => ({
|
||||
foo: true,
|
||||
}),
|
||||
{ name: 'name' }
|
||||
)
|
||||
)
|
||||
{ name: 'name' },
|
||||
),
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.foo)
|
||||
@ -554,7 +554,7 @@ describe('more complex state spec with subscribeWithSelector', () => {
|
||||
useBoundStore.getState().foo
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.foo,
|
||||
(foo) => console.log(foo)
|
||||
(foo) => console.log(foo),
|
||||
)
|
||||
useBoundStore.persist.hasHydrated()
|
||||
return <></>
|
||||
@ -571,8 +571,8 @@ describe('more complex state spec with subscribeWithSelector', () => {
|
||||
() =>
|
||||
({
|
||||
foo: 1,
|
||||
}) as MyState // NOTE: Asserting the entire state works too.
|
||||
)
|
||||
}) as MyState, // NOTE: Asserting the entire state works too.
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.foo)
|
||||
@ -580,7 +580,7 @@ describe('more complex state spec with subscribeWithSelector', () => {
|
||||
useBoundStore.getState().foo
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.foo,
|
||||
(foo) => console.log(foo)
|
||||
(foo) => console.log(foo),
|
||||
)
|
||||
return <></>
|
||||
}
|
||||
@ -602,8 +602,8 @@ describe('more complex state spec with subscribeWithSelector', () => {
|
||||
set({ authenticated: true })
|
||||
},
|
||||
}),
|
||||
{ name: 'auth-store' }
|
||||
)
|
||||
{ name: 'auth-store' },
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.authenticated)
|
||||
@ -633,9 +633,9 @@ describe('create with explicitly annotated mutators', () => {
|
||||
count: 0,
|
||||
inc: () => set({ count: get().count + 1 }, false),
|
||||
}),
|
||||
{ name: 'count' }
|
||||
)
|
||||
)
|
||||
{ name: 'count' },
|
||||
),
|
||||
),
|
||||
)
|
||||
const TestComponent = () => {
|
||||
useBoundStore((s) => s.count) * 2
|
||||
@ -646,7 +646,7 @@ describe('create with explicitly annotated mutators', () => {
|
||||
useBoundStore.getState().inc()
|
||||
useBoundStore.subscribe(
|
||||
(state) => state.count,
|
||||
(count) => console.log(count * 2)
|
||||
(count) => console.log(count * 2),
|
||||
)
|
||||
useBoundStore.setState({ count: 0 }, false)
|
||||
useBoundStore.persist.hasHydrated()
|
||||
|
||||
@ -62,8 +62,8 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -78,14 +78,14 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 0, name: empty')
|
||||
await findByText('count: 42, name: test-storage')
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
{ count: 42, name: 'test-storage' },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
|
||||
@ -105,7 +105,7 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -116,14 +116,14 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 0')
|
||||
await waitFor(() => {
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
undefined,
|
||||
new Error('getItem error')
|
||||
new Error('getItem error'),
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -138,7 +138,7 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
return { useBoundStore, onRehydrateStorageSpy }
|
||||
}
|
||||
@ -154,7 +154,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('count: 0')
|
||||
await waitFor(() => {
|
||||
@ -166,7 +166,7 @@ describe('persist middleware with async configuration', () => {
|
||||
await findByText('count: 42')
|
||||
expect(setItemSpy).toBeCalledWith(
|
||||
'test-storage',
|
||||
JSON.stringify({ state: { count: 42 }, version: 0 })
|
||||
JSON.stringify({ state: { count: 42 }, version: 0 }),
|
||||
)
|
||||
|
||||
// Create the same store a second time and check if the persisted state
|
||||
@ -183,7 +183,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText: findByText2 } = render(
|
||||
<StrictMode>
|
||||
<Counter2 />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText2('count: 42')
|
||||
await waitFor(() => {
|
||||
@ -213,7 +213,7 @@ describe('persist middleware with async configuration', () => {
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
migrate: migrateSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -224,7 +224,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 0')
|
||||
@ -235,7 +235,7 @@ describe('persist middleware with async configuration', () => {
|
||||
JSON.stringify({
|
||||
state: { count: 99 },
|
||||
version: 13,
|
||||
})
|
||||
}),
|
||||
)
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith({ count: 99 }, undefined)
|
||||
})
|
||||
@ -266,8 +266,8 @@ describe('persist middleware with async configuration', () => {
|
||||
{
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
function Component() {
|
||||
@ -286,7 +286,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Component />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 42')
|
||||
@ -296,7 +296,7 @@ describe('persist middleware with async configuration', () => {
|
||||
expect.objectContaining({
|
||||
count: 42,
|
||||
name: 'test',
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@ -319,7 +319,7 @@ describe('persist middleware with async configuration', () => {
|
||||
version: 13,
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -330,7 +330,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 0')
|
||||
@ -363,7 +363,7 @@ describe('persist middleware with async configuration', () => {
|
||||
throw new Error('migrate error')
|
||||
},
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -374,14 +374,14 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 0')
|
||||
await waitFor(() => {
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
undefined,
|
||||
new Error('migrate error')
|
||||
new Error('migrate error'),
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -400,7 +400,7 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
/**
|
||||
@ -423,7 +423,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -455,7 +455,7 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -466,14 +466,14 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 0')
|
||||
await waitFor(() => {
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
{ count: 1, unstorableMethod },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
})
|
||||
@ -507,7 +507,7 @@ describe('persist middleware with async configuration', () => {
|
||||
...persistedState,
|
||||
}
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -518,7 +518,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -546,7 +546,7 @@ describe('persist middleware with async configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -557,7 +557,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 1')
|
||||
@ -579,7 +579,7 @@ describe('persist middleware with async configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
storage.getItem = async () => storageValue
|
||||
@ -600,11 +600,11 @@ describe('persist middleware with async configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
expect(useBoundStore.persist.hasHydrated()).toBe(false)
|
||||
await new Promise((resolve) =>
|
||||
useBoundStore.persist.onFinishHydration(resolve)
|
||||
useBoundStore.persist.onFinishHydration(resolve),
|
||||
)
|
||||
expect(useBoundStore.persist.hasHydrated()).toBe(true)
|
||||
|
||||
@ -634,8 +634,8 @@ describe('persist middleware with async configuration', () => {
|
||||
storage: storage,
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
skipHydration: true,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({
|
||||
@ -659,7 +659,7 @@ describe('persist middleware with async configuration', () => {
|
||||
})
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
{ count: 42, name: 'test-storage' },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
|
||||
@ -680,8 +680,8 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => (s) => s?.inc(),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
function Counter() {
|
||||
@ -692,7 +692,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<Counter />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('count: 2')
|
||||
@ -721,8 +721,8 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage, { replacer, reviver }),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
function MapDisplay() {
|
||||
@ -733,13 +733,13 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<MapDisplay />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
await findByText('map: bar')
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
{ map: new Map([['foo', 'bar']]) },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
|
||||
@ -754,7 +754,7 @@ describe('persist middleware with async configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage, { replacer, reviver }),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
return { useBoundStore, onRehydrateStorageSpy }
|
||||
}
|
||||
@ -770,7 +770,7 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText } = render(
|
||||
<StrictMode>
|
||||
<MapDisplay />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText('map-content:')
|
||||
await waitFor(() => {
|
||||
@ -787,7 +787,7 @@ describe('persist middleware with async configuration', () => {
|
||||
JSON.stringify({
|
||||
state: { map: { type: 'Map', value: [['foo', 'bar']] } },
|
||||
version: 0,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
// Create the same store a second time and check if the persisted state
|
||||
@ -804,13 +804,13 @@ describe('persist middleware with async configuration', () => {
|
||||
const { findByText: findByText2 } = render(
|
||||
<StrictMode>
|
||||
<MapDisplay2 />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
)
|
||||
await findByText2('map-content: bar')
|
||||
await waitFor(() => {
|
||||
expect(onRehydrateStorageSpy2).toBeCalledWith(
|
||||
{ map: updatedMap },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -58,8 +58,8 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({
|
||||
@ -68,7 +68,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
})
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
{ count: 42, name: 'test-storage' },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
|
||||
@ -87,7 +87,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => spy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(spy).toBeCalledWith(undefined, new Error('getItem error'))
|
||||
@ -103,7 +103,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
return { useBoundStore, onRehydrateStorageSpy }
|
||||
}
|
||||
@ -118,7 +118,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
expect(useBoundStore.getState()).toEqual({ count: 42 })
|
||||
expect(setItemSpy).toBeCalledWith(
|
||||
'test-storage',
|
||||
JSON.stringify({ state: { count: 42 }, version: 0 })
|
||||
JSON.stringify({ state: { count: 42 }, version: 0 }),
|
||||
)
|
||||
|
||||
// Create the same store a second time and check if the persisted state
|
||||
@ -153,7 +153,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
migrate: migrateSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({ count: 99 })
|
||||
@ -163,7 +163,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
JSON.stringify({
|
||||
state: { count: 99 },
|
||||
version: 13,
|
||||
})
|
||||
}),
|
||||
)
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith({ count: 99 }, undefined)
|
||||
})
|
||||
@ -187,7 +187,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
version: 13,
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({ count: 0 })
|
||||
@ -217,13 +217,13 @@ describe('persist middleware with sync configuration', () => {
|
||||
throw new Error('migrate error')
|
||||
},
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({ count: 0 })
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
undefined,
|
||||
new Error('migrate error')
|
||||
new Error('migrate error'),
|
||||
)
|
||||
})
|
||||
|
||||
@ -241,7 +241,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
/**
|
||||
@ -281,7 +281,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
const expectedState = { count: 1, unstorableMethod }
|
||||
@ -319,7 +319,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
...persistedState,
|
||||
}
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({
|
||||
@ -346,7 +346,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({
|
||||
@ -393,8 +393,8 @@ describe('persist middleware with sync configuration', () => {
|
||||
array: state.array.filter((e) => e.value !== '1'),
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
useBoundStore.setState({})
|
||||
@ -415,7 +415,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
],
|
||||
},
|
||||
version: 0,
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@ -430,7 +430,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
expect(useBoundStore.persist.getOptions().name).toBeDefined()
|
||||
expect(useBoundStore.persist.getOptions().name).toBe('test-storage')
|
||||
@ -450,26 +450,26 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
partialize: (s) => s as Partial<typeof s>,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
useBoundStore.setState({})
|
||||
expect(setItemSpy).toBeCalledWith(
|
||||
'test-storage',
|
||||
'{"state":{"count":0},"version":0}'
|
||||
'{"state":{"count":0},"version":0}',
|
||||
)
|
||||
|
||||
useBoundStore.persist.setOptions({
|
||||
name: 'test-storage-2',
|
||||
partialize: (state) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(state).filter(([key]) => key !== 'count')
|
||||
Object.entries(state).filter(([key]) => key !== 'count'),
|
||||
),
|
||||
})
|
||||
useBoundStore.setState({})
|
||||
expect(setItemSpy).toBeCalledWith(
|
||||
'test-storage-2',
|
||||
'{"state":{},"version":0}'
|
||||
'{"state":{},"version":0}',
|
||||
)
|
||||
})
|
||||
|
||||
@ -486,7 +486,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
useBoundStore.persist.clearStorage()
|
||||
@ -506,7 +506,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
storage.getItem = () => storageValue
|
||||
@ -527,7 +527,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
expect(useBoundStore.persist.hasHydrated()).toBe(true)
|
||||
@ -555,14 +555,14 @@ describe('persist middleware with sync configuration', () => {
|
||||
persist(() => ({ count: 0 }), {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
const hydrateUnsub1 = useBoundStore.persist.onHydrate(onHydrateSpy1)
|
||||
useBoundStore.persist.onHydrate(onHydrateSpy2)
|
||||
|
||||
const finishHydrationUnsub1 = useBoundStore.persist.onFinishHydration(
|
||||
onFinishHydrationSpy1
|
||||
onFinishHydrationSpy1,
|
||||
)
|
||||
useBoundStore.persist.onFinishHydration(onFinishHydrationSpy2)
|
||||
|
||||
@ -606,8 +606,8 @@ describe('persist middleware with sync configuration', () => {
|
||||
storage: storage,
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
skipHydration: true,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState()).toEqual({
|
||||
@ -631,7 +631,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
})
|
||||
expect(onRehydrateStorageSpy).toBeCalledWith(
|
||||
{ count: 42, name: 'test-storage' },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
|
||||
@ -652,8 +652,8 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => (s) => s?.inc(),
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
expect(useBoundStore.getState().count).toEqual(2)
|
||||
@ -680,8 +680,8 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
const updatedMap = map.set('foo', 'bar')
|
||||
@ -702,7 +702,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
name: 'test-storage',
|
||||
storage: createJSONStorage(() => storage, { replacer, reviver }),
|
||||
onRehydrateStorage: () => onRehydrateStorageSpy,
|
||||
})
|
||||
}),
|
||||
)
|
||||
return { useBoundStore, onRehydrateStorageSpy }
|
||||
}
|
||||
@ -723,7 +723,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
JSON.stringify({
|
||||
state: { map: { type: 'Map', value: [['foo', 'bar']] } },
|
||||
version: 0,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
// Create the same store a second time and check if the persisted state
|
||||
@ -735,7 +735,7 @@ describe('persist middleware with sync configuration', () => {
|
||||
expect(useBoundStore2.getState()).toEqual({ map: updatedMap })
|
||||
expect(onRehydrateStorageSpy2).toBeCalledWith(
|
||||
{ map: updatedMap },
|
||||
undefined
|
||||
undefined,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -24,7 +24,7 @@ describe('types', () => {
|
||||
const Component = () => {
|
||||
const refetchTimestamp = useBoundStore(
|
||||
(state) => state.refetchTimestamp,
|
||||
shallow
|
||||
shallow,
|
||||
)
|
||||
return <>{refetchTimestamp.toUpperCase()}</>
|
||||
}
|
||||
@ -61,7 +61,7 @@ describe('useShallow', () => {
|
||||
|
||||
it('input and output selectors always return shallow equal values', () => {
|
||||
const res = render(
|
||||
<TestUseShallowSimple state={{ a: 1, b: 2 }} selector={Object.keys} />
|
||||
<TestUseShallowSimple state={{ a: 1, b: 2 }} selector={Object.keys} />,
|
||||
)
|
||||
|
||||
expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(0)
|
||||
@ -77,7 +77,7 @@ describe('useShallow', () => {
|
||||
<TestUseShallowSimple
|
||||
state={{ a: 1, b: 2, c: 3 }}
|
||||
selector={Object.keys}
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(res.getByTestId('test-shallow'))
|
||||
@ -92,7 +92,7 @@ describe('useShallow', () => {
|
||||
it('returns the previously computed instance when possible', () => {
|
||||
const state = { a: 1, b: 2 }
|
||||
const res = render(
|
||||
<TestUseShallowSimple state={state} selector={Object.keys} />
|
||||
<TestUseShallowSimple state={state} selector={Object.keys} />,
|
||||
)
|
||||
|
||||
fireEvent.click(res.getByTestId('test-shallow'))
|
||||
@ -106,7 +106,7 @@ describe('useShallow', () => {
|
||||
<TestUseShallowSimple
|
||||
state={state}
|
||||
selector={(state) => Object.keys(state)}
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(res.getByTestId('test-shallow'))
|
||||
@ -122,7 +122,7 @@ describe('useShallow', () => {
|
||||
it('only re-renders if selector output has changed according to shallow', () => {
|
||||
let countRenders = 0
|
||||
const useMyStore = create(
|
||||
(): Record<string, unknown> => ({ a: 1, b: 2, c: 3 })
|
||||
(): Record<string, unknown> => ({ a: 1, b: 2, c: 3 }),
|
||||
)
|
||||
const TestShallow = ({
|
||||
selector = (state) => Object.keys(state).sort(),
|
||||
@ -158,18 +158,19 @@ describe('useShallow', () => {
|
||||
|
||||
it('does not cause stale closure issues', () => {
|
||||
const useMyStore = create(
|
||||
(): Record<string, unknown> => ({ a: 1, b: 2, c: 3 })
|
||||
(): Record<string, unknown> => ({ a: 1, b: 2, c: 3 }),
|
||||
)
|
||||
const TestShallowWithState = () => {
|
||||
const [count, setCount] = useState(0)
|
||||
const output = useMyStore(
|
||||
useShallow((state) => Object.keys(state).concat([count.toString()]))
|
||||
useShallow((state) => Object.keys(state).concat([count.toString()])),
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="test-shallow"
|
||||
onClick={() => setCount((prev) => ++prev)}>
|
||||
onClick={() => setCount((prev) => ++prev)}
|
||||
>
|
||||
{output.join(',')}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -23,7 +23,7 @@ function Counter() {
|
||||
({ bears, increasePopulation }) => ({
|
||||
bears,
|
||||
increasePopulation,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@ -39,13 +39,13 @@ describe.skipIf(!React.version.startsWith('18'))(
|
||||
it('should handle different states between server and client correctly', async () => {
|
||||
const { hydrateRoot } =
|
||||
await vi.importActual<typeof import('react-dom/client')>(
|
||||
'react-dom/client'
|
||||
'react-dom/client',
|
||||
)
|
||||
|
||||
const markup = renderToString(
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<Counter />
|
||||
</React.Suspense>
|
||||
</React.Suspense>,
|
||||
)
|
||||
|
||||
const container = document.createElement('div')
|
||||
@ -59,7 +59,7 @@ describe.skipIf(!React.version.startsWith('18'))(
|
||||
container,
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<Counter />
|
||||
</React.Suspense>
|
||||
</React.Suspense>,
|
||||
)
|
||||
})
|
||||
|
||||
@ -67,5 +67,5 @@ describe.skipIf(!React.version.startsWith('18'))(
|
||||
expect(bearCountText).not.toBeNull()
|
||||
document.body.removeChild(container)
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@ -27,7 +27,7 @@ describe('subscribe()', () => {
|
||||
const spy = vi.fn()
|
||||
const initialState = { value: 1, other: 'a' }
|
||||
const { setState, subscribe } = create(
|
||||
subscribeWithSelector(() => initialState)
|
||||
subscribeWithSelector(() => initialState),
|
||||
)
|
||||
|
||||
subscribe((s) => s.value, spy)
|
||||
@ -39,7 +39,7 @@ describe('subscribe()', () => {
|
||||
const spy = vi.fn()
|
||||
const initialState = { value: 1, other: 'a' }
|
||||
const { setState, subscribe } = create(
|
||||
subscribeWithSelector(() => initialState)
|
||||
subscribeWithSelector(() => initialState),
|
||||
)
|
||||
|
||||
subscribe((s) => s.value, spy)
|
||||
@ -52,7 +52,7 @@ describe('subscribe()', () => {
|
||||
const spy = vi.fn()
|
||||
const initialState = { value: 1, other: 'a' }
|
||||
const { setState, subscribe } = create(
|
||||
subscribeWithSelector(() => initialState)
|
||||
subscribeWithSelector(() => initialState),
|
||||
)
|
||||
|
||||
subscribe((s) => s, spy, { equalityFn: () => true })
|
||||
@ -64,7 +64,7 @@ describe('subscribe()', () => {
|
||||
const spy = vi.fn()
|
||||
const initialState = { value: 1, other: 'a' }
|
||||
const { setState, subscribe } = create(
|
||||
subscribeWithSelector(() => initialState)
|
||||
subscribeWithSelector(() => initialState),
|
||||
)
|
||||
|
||||
subscribe((s) => s.value, spy, { equalityFn: () => false })
|
||||
@ -77,7 +77,7 @@ describe('subscribe()', () => {
|
||||
const spy = vi.fn()
|
||||
const initialState = { value: 1, other: 'a' }
|
||||
const { setState, subscribe } = create(
|
||||
subscribeWithSelector(() => initialState)
|
||||
subscribeWithSelector(() => initialState),
|
||||
)
|
||||
|
||||
const unsub = subscribe((s) => s.value, spy)
|
||||
@ -94,7 +94,7 @@ describe('subscribe()', () => {
|
||||
const spy = vi.fn()
|
||||
const initialState = { value: 1, other: 'a' }
|
||||
const { getState, setState, subscribe } = create(
|
||||
subscribeWithSelector(() => initialState)
|
||||
subscribeWithSelector(() => initialState),
|
||||
)
|
||||
|
||||
const isRoughEqual = (x: number, y: number) => Math.abs(x - y) < 1
|
||||
|
||||
@ -5,7 +5,7 @@ type ReplacedMap = {
|
||||
|
||||
export const replacer = (
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): ReplacedMap | unknown => {
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
|
||||
@ -30,7 +30,7 @@ it('can use exposed types', () => {
|
||||
numGet: () => 2,
|
||||
}
|
||||
const partialFn: (state: ExampleState) => Partial<ExampleState> = (
|
||||
state
|
||||
state,
|
||||
) => ({
|
||||
...state,
|
||||
num: 2,
|
||||
@ -82,7 +82,7 @@ it('can use exposed types', () => {
|
||||
_destroy: StoreApi<ExampleState>['destroy'],
|
||||
_equalityFn: (a: ExampleState, b: ExampleState) => boolean,
|
||||
_stateCreator: StateCreator<ExampleState>,
|
||||
_useBoundStore: UseBoundStore<StoreApi<ExampleState>>
|
||||
_useBoundStore: UseBoundStore<StoreApi<ExampleState>>,
|
||||
) {
|
||||
expect(true).toBeTruthy()
|
||||
}
|
||||
@ -99,7 +99,7 @@ it('can use exposed types', () => {
|
||||
storeApi.destroy,
|
||||
equalityFn,
|
||||
stateCreator,
|
||||
useBoundStore
|
||||
useBoundStore,
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@ it('can set the store without merging', () => {
|
||||
const { setState, getState } = createStore<{ a: number } | { b: number }>(
|
||||
(_set) => ({
|
||||
a: 1,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
// Should override the state instead of merging.
|
||||
|
||||
@ -15,15 +15,15 @@ describe('shallow', () => {
|
||||
|
||||
it('compares objects', () => {
|
||||
expect(shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123 })).toBe(
|
||||
true
|
||||
true,
|
||||
)
|
||||
|
||||
expect(
|
||||
shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', foobar: true })
|
||||
shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', foobar: true }),
|
||||
).toBe(false)
|
||||
|
||||
expect(
|
||||
shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123, foobar: true })
|
||||
shallow({ foo: 'bar', asd: 123 }, { foo: 'bar', asd: 123, foobar: true }),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
@ -33,7 +33,7 @@ describe('shallow', () => {
|
||||
expect(shallow([1, 2, 3], [2, 3, 4])).toBe(false)
|
||||
|
||||
expect(
|
||||
shallow([{ foo: 'bar' }, { asd: 123 }], [{ foo: 'bar' }, { asd: 123 }])
|
||||
shallow([{ foo: 'bar' }, { asd: 123 }], [{ foo: 'bar' }, { asd: 123 }]),
|
||||
).toBe(false)
|
||||
|
||||
expect(shallow([{ foo: 'bar' }], [{ foo: 'bar', asd: 123 }])).toBe(false)
|
||||
@ -47,22 +47,22 @@ describe('shallow', () => {
|
||||
expect(
|
||||
shallow(
|
||||
createMap({ foo: 'bar', asd: 123 }),
|
||||
createMap({ foo: 'bar', asd: 123 })
|
||||
)
|
||||
createMap({ foo: 'bar', asd: 123 }),
|
||||
),
|
||||
).toBe(true)
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
createMap({ foo: 'bar', asd: 123 }),
|
||||
createMap({ foo: 'bar', foobar: true })
|
||||
)
|
||||
createMap({ foo: 'bar', foobar: true }),
|
||||
),
|
||||
).toBe(false)
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
createMap({ foo: 'bar', asd: 123 }),
|
||||
createMap({ foo: 'bar', asd: 123, foobar: true })
|
||||
)
|
||||
createMap({ foo: 'bar', asd: 123, foobar: true }),
|
||||
),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
@ -72,7 +72,7 @@ describe('shallow', () => {
|
||||
expect(shallow(new Set(['bar', 123]), new Set(['bar', 2]))).toBe(false)
|
||||
|
||||
expect(shallow(new Set(['bar', 123]), new Set(['bar', 123, true]))).toBe(
|
||||
false
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
@ -98,8 +98,8 @@ describe('unsupported cases', () => {
|
||||
expect(
|
||||
shallow(
|
||||
new Date('2022-07-19T00:00:00.000Z'),
|
||||
new Date('2022-07-20T00:00:00.000Z')
|
||||
)
|
||||
new Date('2022-07-20T00:00:00.000Z'),
|
||||
),
|
||||
).not.toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user