Skip to content

Commit

Permalink
Merge branch 'main' into v5
Browse files Browse the repository at this point in the history
  • Loading branch information
dai-shi committed Dec 12, 2023
2 parents 8030aee + 9baf0a5 commit ee248eb
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 122 deletions.
4 changes: 2 additions & 2 deletions docs/integrations/immer-middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,5 @@ Zustand will skip calling the subscriptions.

## CodeSandbox Demo

- [Basic](https://codesandbox.io/s/zustand-updating-draft-states-basic-demo-zkp22g),
- [Advanced](https://codesandbox.io/s/zustand-updating-draft-states-advanced-demo-3znqzk).
- [Basic](https://codesandbox.io/p/sandbox/zustand-updating-draft-states-basic-demo-forked-96mkdw),
- [Advanced](https://codesandbox.io/p/sandbox/zustand-updating-draft-states-advanced-demo-forked-phkzzg).
5 changes: 3 additions & 2 deletions tests/shallow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { act, fireEvent, render } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/vanilla/shallow'

describe('types', () => {
it('works with useBoundStore and array selector (#1107)', () => {
const useBoundStore = create(() => ({
const useBoundStore = createWithEqualityFn(() => ({
villages: [] as { name: string }[],
}))
const Component = () => {
Expand All @@ -18,7 +19,7 @@ describe('types', () => {
})

it('works with useBoundStore and string selector (#1107)', () => {
const useBoundStore = create(() => ({
const useBoundStore = createWithEqualityFn(() => ({
refetchTimestamp: '',
}))
const Component = () => {
Expand Down
122 changes: 4 additions & 118 deletions tests/subscribe.test.tsx
Original file line number Diff line number Diff line change
@@ -1,123 +1,9 @@
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it } from 'vitest'
import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'

describe('subscribe()', () => {
it('should not be called if new state identity is the same', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = create(() => initialState)

subscribe(spy)
setState(initialState)
expect(spy).not.toHaveBeenCalled()
})

it('should be called if new state identity is different', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, getState, subscribe } = create(() => initialState)

subscribe(spy)
setState({ ...getState() })
expect(spy).toHaveBeenCalledWith(initialState, initialState)
})

it('should not be called when state slice is the same', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = create(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s.value, spy)
setState({ other: 'b' })
expect(spy).not.toHaveBeenCalled()
})

it('should be called when state slice changes', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = create(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s.value, spy)
setState({ value: initialState.value + 1 })
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(initialState.value + 1, initialState.value)
})

it('should not be called when equality checker returns true', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = create(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s, spy, { equalityFn: () => true })
setState({ value: initialState.value + 2 })
expect(spy).not.toHaveBeenCalled()
})

it('should be called when equality checker returns false', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = create(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s.value, spy, { equalityFn: () => false })
setState({ value: initialState.value + 2 })
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(initialState.value + 2, initialState.value)
})

it('should unsubscribe correctly', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = create(
subscribeWithSelector(() => initialState),
)

const unsub = subscribe((s) => s.value, spy)

setState({ value: initialState.value + 1 })
unsub()
setState({ value: initialState.value + 2 })

expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(initialState.value + 1, initialState.value)
})

it('should keep consistent behavior with equality check', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { getState, setState, subscribe } = create(
subscribeWithSelector(() => initialState),
)

const isRoughEqual = (x: number, y: number) => Math.abs(x - y) < 1
setState({ value: 0 })
spy.mockReset()
const spy2 = vi.fn()
let prevValue = getState().value
const unsub = subscribe((s) => {
if (isRoughEqual(prevValue, s.value)) {
// skip assuming values are equal
return
}
spy(s.value, prevValue)
prevValue = s.value
})
const unsub2 = subscribe((s) => s.value, spy2, { equalityFn: isRoughEqual })
setState({ value: 0.5 })
setState({ value: 1 })
unsub()
unsub2()
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(1, 0)
expect(spy2).toHaveBeenCalledTimes(1)
expect(spy2).toHaveBeenCalledWith(1, 0)
it('should correctly have access to subscribe', () => {
const { subscribe } = create(() => ({ value: 1 }))
expect(typeof subscribe).toBe('function')
})
})
123 changes: 123 additions & 0 deletions tests/vanilla/subscribe.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { describe, expect, it, vi } from 'vitest'
import { subscribeWithSelector } from 'zustand/middleware'
import { createStore } from 'zustand/vanilla'

describe('subscribe()', () => {
it('should not be called if new state identity is the same', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = createStore(() => initialState)

subscribe(spy)
setState(initialState)
expect(spy).not.toHaveBeenCalled()
})

it('should be called if new state identity is different', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, getState, subscribe } = createStore(() => initialState)

subscribe(spy)
setState({ ...getState() })
expect(spy).toHaveBeenCalledWith(initialState, initialState)
})

it('should not be called when state slice is the same', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = createStore(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s.value, spy)
setState({ other: 'b' })
expect(spy).not.toHaveBeenCalled()
})

it('should be called when state slice changes', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = createStore(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s.value, spy)
setState({ value: initialState.value + 1 })
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(initialState.value + 1, initialState.value)
})

it('should not be called when equality checker returns true', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = createStore(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s, spy, { equalityFn: () => true })
setState({ value: initialState.value + 2 })
expect(spy).not.toHaveBeenCalled()
})

it('should be called when equality checker returns false', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = createStore(
subscribeWithSelector(() => initialState),
)

subscribe((s) => s.value, spy, { equalityFn: () => false })
setState({ value: initialState.value + 2 })
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(initialState.value + 2, initialState.value)
})

it('should unsubscribe correctly', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { setState, subscribe } = createStore(
subscribeWithSelector(() => initialState),
)

const unsub = subscribe((s) => s.value, spy)

setState({ value: initialState.value + 1 })
unsub()
setState({ value: initialState.value + 2 })

expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(initialState.value + 1, initialState.value)
})

it('should keep consistent behavior with equality check', () => {
const spy = vi.fn()
const initialState = { value: 1, other: 'a' }
const { getState, setState, subscribe } = createStore(
subscribeWithSelector(() => initialState),
)

const isRoughEqual = (x: number, y: number) => Math.abs(x - y) < 1
setState({ value: 0 })
spy.mockReset()
const spy2 = vi.fn()
let prevValue = getState().value
const unsub = subscribe((s) => {
if (isRoughEqual(prevValue, s.value)) {
// skip assuming values are equal
return
}
spy(s.value, prevValue)
prevValue = s.value
})
const unsub2 = subscribe((s) => s.value, spy2, { equalityFn: isRoughEqual })
setState({ value: 0.5 })
setState({ value: 1 })
unsub()
unsub2()
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalledWith(1, 0)
expect(spy2).toHaveBeenCalledTimes(1)
expect(spy2).toHaveBeenCalledWith(1, 0)
})
})

0 comments on commit ee248eb

Please sign in to comment.