Repository: pmndrs/zustand
Pull request #1314 (2022-09-26 03:41:20)

fix: should use Object.is to compare two value
## Related Issues

Fixes #.

## Summary



## Check List

- [x] `yarn run prettier` for formatting code and docs


codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/pmndrs/zustand/pr/1314/builds/297203) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit a42b10a397638ca1f55d257cba739a65b1e4c2b5:

|Sandbox| Source |
|--|--|
|[React](https://codesandbox.io/s/react-9wmdc1)| Configuration |
|[React Typescript](https://codesandbox.io/s/react-typescript-ukkq66)| Configuration |
|[React Browserify](https://codesandbox.io/s/react-browserify-xzlbcv)| Configuration |
|[React Snowpack](https://codesandbox.io/s/react-snowpack-sqetui)| Configuration |
|[React Parcel](https://codesandbox.io/s/react-parcel-z1wtr3)| Configuration |
|[Next.js](https://codesandbox.io/s/next-js-0od1l9)| Configuration |


Patch:
diff --git a/docs/getting-started/comparison.md b/docs/getting-started/comparison.md
index ccebcdd..e017c12 100644
--- a/docs/getting-started/comparison.md
+++ b/docs/getting-started/comparison.md
@@ -15,8 +15,9 @@ differences and similarities between each.
 
 ### State Model
 
-There are no big differences between Zustand and Redux. Both are based on
-immutable state model. Also, Redux needs to wrap your app in context providers.
+Conceptually, Zustand and Redux are quite similar, both are based on an
+immutable state model. However, Redux, requires your app to be wrapped in
+context providers; Zustand does not.
 
 ```ts
 import create from 'zustand'
@@ -106,8 +107,8 @@ const countSlice = createSlice({
   initialState: { value: 0 },
   reducers: {
     incremented: (state, qty: number) => {
-      // Redux Toolkit does not mutate the state, it use Immer library behind
-      // scenes allow us to have something called "draft state".
+      // Redux Toolkit does not mutate the state, it uses the Immer library
+      // behind scenes, allowing us to have something called "draft state".
       state.value += qty
     },
     decremented: (state, qty: number) => {
@@ -125,6 +126,8 @@ When it comes to render optimizations within your app, there are no major
 differences in approach between Zustand and Redux. In both libraries it is
 recommended that you manually apply render optimizations by using selectors.
 
+**Zustand**
+
 ```ts
 import create from 'zustand'
 
@@ -151,6 +154,8 @@ const Component = () => {
 }
 ```
 
+**Redux**
+
 ```ts
 import { createStore } from 'redux'
 import { useSelector, useDispatch } from 'react-redux'
@@ -194,8 +199,8 @@ const countSlice = createSlice({
   initialState: { value: 0 },
   reducers: {
     incremented: (state, qty: number) => {
-      // Redux Toolkit does not mutate the state, it use Immer library behind
-      // scenes allow us to have something called "draft state".
+      // Redux Toolkit does not mutate the state, it uses the Immer library
+      // behind scenes, allowing us to have something called "draft state".
       state.value += qty
     },
     decremented: (state, qty: number) => {
@@ -222,8 +227,11 @@ const Component = () => {
 
 ### State Model
 
-There is a major difference between Zustand and Valtio. Zustand is based on
-the immutable state model, while Valtio is based on the mutable state model.
+Zustand and Valtio approach state management in a fundamentally different way.
+Zustand is based on the **immutable** state model, while Valtio is based on the
+**mutable** state model.
+
+**Zustand**
 
 ```ts
 import create from 'zustand'
@@ -237,6 +245,8 @@ const store = create<State>(() => ({ obj: { count: 0 } }))
 store.setState((prev) => ({ obj: { count: prev.obj.count + 1 } })
 ```
 
+**Valtio**
+
 ```ts
 import { proxy } from 'valtio'
 
@@ -248,9 +258,11 @@ state.obj.count += 1
 ### Render Optimization
 
 The other difference between Zustand and Valtio is Valtio makes render
-optimizations through property access. However, with Zustand it is recommended
+optimizations through property access. However, with Zustand, it is recommended
 that you manually apply render optimizations by using selectors.
 
+**Zustand**
+
 ```ts
 import create from 'zustand'
 
@@ -268,6 +280,8 @@ const Component = () => {
 }
 ```
 
+**Valtio**
+
 ```ts
 import { proxy, useSnapshot } from 'valtio'
 
@@ -285,11 +299,12 @@ const Component = () => {
 
 ### State Model
 
-There are two major differences between Zustand and Jotai. The first one is
-Zustand is a single store, while Jotai consists of primitive atoms and allows
-composing them together. The last one is Zustand store is global in memory, but
-Jotai atoms are not (are definitions that do not hold values) and that's why
-you can not use it outside React.
+There are two major differences between Zustand and Jotai. Firstly, Zustand is a
+single store, while Jotai consists of primitive atoms that can be composed
+together. Secondly, a Zustand store is an external store, making it more
+suitable when when access outside of React is required.
+
+**Zustand**
 
 ```ts
 import create from 'zustand'
@@ -311,6 +326,8 @@ const useCountStore = create<State & Actions>((set) => ({
 }))
 ```
 
+**Jotai**
+
 ```ts
 import { atom } from 'jotai'
 
@@ -319,9 +336,11 @@ const countAtom = atom<number>(0)
 
 ### Render Optimization
 
-The other difference between Zustand and Jotai is: Jotai makes render
-optimizations through atom dependency. However, with Zustand it is recommended that you
-manually apply render optimizations by using selectors.
+Jotai achieves render optimizations through atom dependency. However, with
+Zustand it is recommended that you manually apply render optimizations by using
+selectors.
+
+**Zustand**
 
 ```ts
 import create from 'zustand'
@@ -349,6 +368,8 @@ const Component = () => {
 }
 ```
 
+**Jotai**
+
 ```ts
 import { atom, useAtom } from 'jotai'
 
@@ -364,9 +385,11 @@ const Component = () => {
 
 ### State Model
 
-The major difference is the same as Zustand and Jotai is: Recoil depends on
-atom string keys instead of atom object referential identities. Also, Recoil
-needs to wrap your app in a context provider.
+The difference between Zustand and Recoil is similar to that between Zustand and
+Jotai. Recoil depends on atom string keys instead of atom object referential
+identities, additionally, Recoil needs to wrap your app in a context provider.
+
+**Zustand**
 
 ```ts
 import create from 'zustand'
@@ -386,6 +409,8 @@ const useCountStore = create<State & Actions>((set) => ({
 }))
 ```
 
+**Recoil**
+
 ```ts
 import { atom } from 'recoil'
 
@@ -397,10 +422,12 @@ const count = atom({
 
 ### Render Optimization
 
-The other difference between Zustand and Recoil is: Recoil makes render
-optimizations through atom dependency. However, with Zustand it is recommended that you
+Similar to previous optimization comparisons, Recoil makes render optimizations
+through atom dependency. Whereas, with Zustand, it is recommended that you
 manually apply render optimizations by using selectors.
 
+**Zustand**
+
 ```ts
 import create from 'zustand'
 
@@ -425,6 +452,8 @@ const Component = () => {
 }
 ```
 
+**Recoil**
+
 ```ts
 import { atom, useRecoilState } from 'recoil'
 
diff --git a/docs/getting-started/concepts.md b/docs/getting-started/concepts.md
index aa93887..31ffce5 100644
--- a/docs/getting-started/concepts.md
+++ b/docs/getting-started/concepts.md
@@ -4,4 +4,6 @@ description:
 nav: 1
 ---
 
-⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
+<Hint>
+  ⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
+</Hint>
diff --git a/docs/getting-started/introduction.mdx b/docs/getting-started/introduction.mdx
index d1366f1..f525d9b 100644
--- a/docs/getting-started/introduction.mdx
+++ b/docs/getting-started/introduction.mdx
@@ -4,22 +4,31 @@ description: How to use Zustand
 nav: 0
 ---
 
-⚠️ This doc is still under construction. https://github.com/pmndrs/zustand/discussions/1033
+<Hint>
+  ⚠️ This doc is still under construction.
+  https://github.com/pmndrs/zustand/discussions/1033
+</Hint>
 
-<p align="center">
-  <img width="600" src="https://github.com/pmndrs/zustand/raw/main/bear.jpg" />
-</p>
+<div class="flex justify-center mb-4">
+  <img src="https://github.com/pmndrs/zustand/raw/main/bear.jpg" />
+</div>
 
-A small, fast and scalable bearbones state-management solution. Has a comfy api based
-on hooks, isn't boilerplatey or opinionated, but still just enough to be explicit
-and flux-like.
+A small, fast and scalable bearbones state-management solution. Zustand has a comfy api based on hooks. It isn't boilerplatey or opinionated, but still has enough to be explicit and flux-like.
 
-Don't disregard it because it's cute. It has quite the claws, lots of time was spent to deal with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [react concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right.
+Don't disregard it because it's cute, it has claws! Lots of time was spent to deal with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [React concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right.
 
 You can try a live demo [here](https://codesandbox.io/s/dazzling-moon-itop4).
 
+## Installation
+
+Zustand is available as a package on NPM for use:
+
 ```bash
+# NPM
 npm install zustand
+
+# Yarn
+yarn add zustand
 ```
 
 ## First create a store
@@ -38,7 +47,7 @@ const useStore = create((set) => ({
 
 ## Then bind your components, and that's it!
 
-Use the hook anywhere, no providers needed. Select your state and the component will re-render on changes.
+You can use the hook anywhere, without the need of providers. Select your state and the consuming component will re-render when that state changes.
 
 ```jsx
 function BearCounter() {
diff --git a/docs/guides/immutable-state-and-merging.md b/docs/guides/immutable-state-and-merging.md
index 2abea07..4cc86bd 100644
--- a/docs/guides/immutable-state-and-merging.md
+++ b/docs/guides/immutable-state-and-merging.md
@@ -48,7 +48,7 @@ const useCountStore = create((set) => ({
 ```
 
 For complex use cases, consider using some libraries that help with immutable updates.
-You can refer to [Updating nested state object values](./updating-nested-state-object-values.md).
+You can refer to [Updating nested state object values](./updating-state.md#deeply-nested-object).
 
 ## Replace flag
 
diff --git a/docs/guides/slices-pattern.md b/docs/guides/slices-pattern.md
new file mode 100644
index 0000000..e4d4467
--- /dev/null
+++ b/docs/guides/slices-pattern.md
@@ -0,0 +1,90 @@
+---
+title: Slices Pattern
+nav: 15
+---
+
+## Slicing the store into smaller stores
+
+Your store can become bigger and bigger and tougher to maintain as you add more features.
+
+You can divide your main store into smaller individual stores to achieve modularity. This is simple to accomplish in Zustand!
+
+The first individual store:
+
+```js
+export const createFishSlice = (set) => ({
+  fishes: 0,
+  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
+})
+```
+
+Another individual store:
+
+```js
+export const createBearSlice = (set) => ({
+  bears: 0,
+  addBear: () => set((state) => ({ bears: state.bears + 1 })),
+  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
+})
+```
+
+You can now combine both the stores into **one bounded store**:
+
+```js
+import create from 'zustand'
+import { createBearSlice } from './bearSlice'
+import { createFishSlice } from './fishSlice'
+
+export const useBoundStore = create((...a) => ({
+  ...createBearSlice(...a),
+  ...createFishSlice(...a),
+}))
+```
+
+### Usage in a React component
+
+```jsx
+import { useBoundStore } from './stores/useBoundStore'
+
+function App() {
+  const bears = useBoundStore((state) => state.bears)
+  const fishes = useBoundStore((state) => state.fishes)
+  const addBear = useBoundStore((state) => state.addBear)
+  return (
+    <div>
+      <h2>Number of bears: {bears}</h2>
+      <h2>Number of fishes: {fishes}</h2>
+      <button onClick={() => addBear()}>Add a bear</button>
+    </div>
+  )
+}
+
+export default App
+```
+
+## Adding middlewares
+
+Adding middlewares to a combined store is the same as with other normal stores.
+
+Adding `persist` middleware to our `useBoundStore`:
+
+```js
+import create from 'zustand'
+import { createBearSlice } from './bearSlice'
+import { createFishSlice } from './fishSlice'
+import { persist } from 'zustand/middleware'
+
+export const useBoundStore = create(
+  persist(
+    (...a) => ({
+      ...createBearSlice(...a),
+      ...createFishSlice(...a),
+    }),
+    { name: 'bound-store' }
+  )
+)
+```
+
+## Usage with TypeScript
+
+A detailed guide on how to use the slice pattern in Zustand with TypeScript can be found [here](./typescript.md#slices-pattern).
diff --git a/docs/guides/typescript.md b/docs/guides/typescript.md
index 39228e1..e750b18 100644
--- a/docs/guides/typescript.md
+++ b/docs/guides/typescript.md
@@ -406,6 +406,8 @@ const useBoundStore = create<BearSlice & FishSlice>()((...a) => ({
 }))
 ```
 
+A detailed explanation on the slices pattern can be found [here](./slices-pattern.md).
+
 If you have some middlewares then replace `StateCreator<MyState, [], [], MySlice>` with `StateCreator<MyState, Mutators, [], MySlice>`. For example, if you are using `devtools` then it will be `StateCreator<MyState, [["zustand/devtools", never]], [], MySlice>`. See the ["Middlewares and their mutators reference"](#middlewares-and-their-mutators-reference) section for a list of all mutators.
 
 ## Middlewares and their mutators reference
diff --git a/docs/guides/updating-nested-state-object-values.md b/docs/guides/updating-nested-state-object-values.md
deleted file mode 100644
index f7a1315..0000000
--- a/docs/guides/updating-nested-state-object-values.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-title: Updating nested state object values
-nav: 3
----
-
-## Deeply nested object
-
-If you have a deep state object like this:
-
-```ts
-type State = {
-  deep: {
-    nested: {
-      obj: { count: number }
-    }
-  }
-}
-```
-
-It requires some effort to update the count value immutably.
-
-## Normal approach
-
-The normal approach is to copy state object with the spread operator `...` like so:
-
-```ts
-  normalInc: () =>
-    set((state) => ({
-      deep: {
-        ...state.deep,
-        nested: {
-          ...state.deep.nested,
-          obj: {
-            ...state.deep.nested.obj,
-            count: state.deep.nested.obj.count + 1
-          }
-        }
-      }
-    })),
-```
-
-This is very long!
-
-## With immer
-
-Many people use [immer](https://github.com/immerjs/immer) to update nested values. You can use immer to shorten your state updates for deeply nested object like this:
-
-```ts
-  immerInc: () =>
-    set(produce((state: State) => { ++state.deep.nested.obj.count })),
-```
-
-What a reduction!. [Please take note of the gotchas listed here](../integrations/updating-draft-states.md).
-
-## With optics-ts
-
-There is another option with [optics-ts](https://github.com/akheron/optics-ts/):
-
-```ts
-  opticsInc: () =>
-    set(O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)),
-```
-
-Unlike immer, optics-ts doesn't use proxies or mutation syntax.
-
-## With ramda
-
-You can also use [ramda](https://ramdajs.com/):
-
-```ts
-  ramdaInc: () =>
-    set(R.over(R.lensPath(["deep", "nested", "obj", "count"]), (c) => c + 1)),
-```
-
-Both ramda and optics-ts also work with types.
-
-## CodeSandbox Demo
-
-https://codesandbox.io/s/zustand-normal-immer-optics-ramda-updating-ynn3o?file=/src/App.tsx
diff --git a/docs/guides/updating-state.md b/docs/guides/updating-state.md
new file mode 100644
index 0000000..f865179
--- /dev/null
+++ b/docs/guides/updating-state.md
@@ -0,0 +1,141 @@
+---
+title: Updating state
+nav: 3
+---
+
+## Flat updates
+
+Updating state with Zustand is simple! Call the provided `set` function with
+the new state, and it will be shallowly merged with the existing state in the
+store. **Note** See next section for nested state.
+
+```tsx
+type State = {
+  firstName: string
+  lastName: string
+}
+
+type Action = {
+  updateFirstName: (firstName: State['firstName']) => void
+  updateLastName: (lastName: State['lastName']) => void
+}
+
+// Create your store, which includes both state and (optionally) actions
+const useStore = create<State & Action>((set) => ({
+  firstName: '',
+  lastName: '',
+  updateFirstName: (firstName) => set(() => ({ firstName: firstName })),
+  updateLastName: (lastName) => set(() => ({ lastName: lastName })),
+}))
+
+// In consuming app
+function App() {
+  // "select" the needed state and actions, in this case, the firstName value
+  // and the action updateFirstName
+  const [firstName, updateFirstName] = useStore(
+    (state) => [state.firstName, state.updateFirstName],
+    shallow
+  )
+
+  return (
+    <main>
+      <label>
+        First name
+        <input
+          // Update the "firstName" state
+          onChange={(e) => updateFirstName(e.currentTarget.value)}
+          value={firstName}
+        />
+      </label>
+
+      <p>
+        Hello, <strong>{firstName}!</strong>
+      </p>
+    </main>
+  )
+}
+```
+
+## Deeply nested object
+
+If you have a deep state object like this:
+
+```ts
+type State = {
+  deep: {
+    nested: {
+      obj: { count: number }
+    }
+  }
+}
+```
+
+Updating nested state requires some effort to ensure the process is completed
+immutably.
+
+### Normal approach
+
+Similar to React or Redux, the normal approach is to copy each level of the
+state object. This is done with the spread operator `...`, and by manually
+merging that in with the new state values. Like so:
+
+```ts
+  normalInc: () =>
+    set((state) => ({
+      deep: {
+        ...state.deep,
+        nested: {
+          ...state.deep.nested,
+          obj: {
+            ...state.deep.nested.obj,
+            count: state.deep.nested.obj.count + 1
+          }
+        }
+      }
+    })),
+```
+
+This is very long! Let's explore some alternatives that will make your life
+easier.
+
+### With immer
+
+Many people use [immer](https://github.com/immerjs/immer) to update nested
+values. Immer can be used anytime you need to update nested state such as in
+React, Redux and of course, Zustand!
+
+You can use immer to shorten your state updates for deeply nested object. Let's
+take a look at an example:
+
+```ts
+  immerInc: () =>
+    set(produce((state: State) => { ++state.deep.nested.obj.count })),
+```
+
+What a reduction!. [Please take note of the gotchas listed here](../integrations/updating-draft-states.md).
+
+### With optics-ts
+
+There is another option with [optics-ts](https://github.com/akheron/optics-ts/):
+
+```ts
+  opticsInc: () =>
+    set(O.modify(O.optic<State>().path("deep.nested.obj.count"))((c) => c + 1)),
+```
+
+Unlike immer, optics-ts doesn't use proxies or mutation syntax.
+
+### With Ramda
+
+You can also use [Ramda](https://ramdajs.com/):
+
+```ts
+  ramdaInc: () =>
+    set(R.over(R.lensPath(["deep", "nested", "obj", "count"]), (c) => c + 1)),
+```
+
+Both ramda and optics-ts also work with types.
+
+### CodeSandbox Demo
+
+https://codesandbox.io/s/zustand-normal-immer-optics-ramda-updating-ynn3o?file=/src/App.tsx
diff --git a/docs/integrations/updating-draft-states.md b/docs/integrations/updating-draft-states.md
index 6759caf..4130645 100644
--- a/docs/integrations/updating-draft-states.md
+++ b/docs/integrations/updating-draft-states.md
@@ -3,10 +3,20 @@ title: Immer middleware
 nav: 16
 ---
 
-The immer middleware enables you to use an immutable state in a more convenient
+The [Immer](https://github.com/immerjs/immer) middleware enables you to use an immutable state in a more convenient
 way. Also, with `Immer` you can simplify handling immutable data structures on
 `Zustand`.
 
+## Installation
+
+In order to use the Immer middleware in `Zustand`, you will need to install `Immer` as a direct dependency.
+
+```bash
+npm install immer
+```
+
+## Usage
+
 Updating simple states
 
 ```ts
diff --git a/docs/recipes/recipes.mdx b/docs/recipes/recipes.mdx
index b603751..4fd5cda 100644
--- a/docs/recipes/recipes.mdx
+++ b/docs/recipes/recipes.mdx
@@ -379,6 +379,8 @@ import { devtools } from 'zustand/middleware'
 const useStore = create(devtools(store))
 // Usage with a redux store, it will log full action types
 const useStore = create(devtools(redux(reducer, initialState)))
+// Disabling devtools (for instance in production build)
+const useStore = create(devtools(store, { enabled: false }))
 ```
 
 devtools takes the store function as its first argument, optionally you can name the store with a second argument: `devtools(store, "MyStore")`, which will be prefixed to your actions.
diff --git a/readme.md b/readme.md
index 6f88595..18c4d6e 100644
--- a/readme.md
+++ b/readme.md
@@ -277,7 +277,7 @@ const clearForest = useLushStore((state) => state.clearForest)
 clearForest()
 ```
 
-[Alternatively, there are some other solutions.](./docs/guides/updating-nested-state-object-values.md)
+[Alternatively, there are some other solutions.](./docs/guides/updating-state.md#with-immer)
 
 ## Middleware
 
@@ -430,6 +430,12 @@ If an action type is not provided, it is defaulted to "anonymous". You can custo
 devtools(..., { anonymousActionType: 'unknown', ... })
 ```
 
+If you wish to disable devtools (on production for instance). You can customize this setting by providing the `enabled` parameter:
+
+```jsx
+devtools(..., { enabled: false, ... })
+```
+
 ## React context
 
 The store created with `create` doesn't require context providers. In some cases, you may want to use contexts for dependency injection or if you want to initialize your store with props from a component. Because the normal store is a hook, passing it as a normal context value may violate rules of hooks.
diff --git a/src/context.ts b/src/context.ts
index 80bfab0..9ffd924 100644
--- a/src/context.ts
+++ b/src/context.ts
@@ -44,7 +44,7 @@ function createContext<S extends StoreApi<unknown>>() {
     )
   }
 
-  const useBoundStore = (<StateSlice = ExtractState<S>>(
+  const useContextStore: UseContextStore<S> = <StateSlice = ExtractState<S>>(
     selector?: (state: ExtractState<S>) => StateSlice,
     equalityFn?: (a: StateSlice, b: StateSlice) => boolean
   ) => {
@@ -59,7 +59,7 @@ function createContext<S extends StoreApi<unknown>>() {
       selector as (state: ExtractState<S>) => StateSlice,
       equalityFn
     )
-  }) as UseContextStore<S>
+  }
 
   const useStoreApi = () => {
     const store = useContext(ZustandContext)
@@ -68,21 +68,12 @@ function createContext<S extends StoreApi<unknown>>() {
         'Seems like you have not used zustand provider as an ancestor.'
       )
     }
-    return useMemo(
-      () =>
-        ({
-          getState: store.getState,
-          setState: store.setState,
-          subscribe: store.subscribe,
-          destroy: store.destroy,
-        } as WithoutCallSignature<S>),
-      [store]
-    )
+    return useMemo<WithoutCallSignature<S>>(() => ({ ...store }), [store])
   }
 
   return {
     Provider,
-    useStore: useBoundStore,
+    useStore: useContextStore,
     useStoreApi,
   }
 }
diff --git a/src/vanilla.ts b/src/vanilla.ts
index 34d6fac..65cc645 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -72,7 +72,7 @@ const createStoreImpl: CreateStoreImpl = (createState) => {
       typeof partial === 'function'
         ? (partial as (state: TState) => TState)(state)
         : partial
-    if (nextState !== state) {
+    if (!Object.is(nextState, state)) {
       const previousState = state
       state =
         replace ?? typeof nextState !== 'object'


Test patch:
diff --git a/tests/basic.test.tsx b/tests/basic.test.tsx
index 43729f4..24ecab7 100644
--- a/tests/basic.test.tsx
+++ b/tests/basic.test.tsx
@@ -437,6 +437,17 @@ it('can set the store', () => {
   expect(getState().value).toBe(5)
 })
 
+it('both NaN should not update', () => {
+  const { setState, subscribe } = create<number>(() => NaN)
+
+  const fn = jest.fn()
+  subscribe(fn)
+
+  setState(NaN)
+
+  expect(fn).not.toBeCalled()
+})
+
 it('can set the store without merging', () => {
   const { setState, getState } = create<{ a: number } | { b: number }>(
     (_set) => ({
diff --git a/tests/persistSync.test.tsx b/tests/persistSync.test.tsx
index 74ebd4a..7bbd2b3 100644
--- a/tests/persistSync.test.tsx
+++ b/tests/persistSync.test.tsx
@@ -1,7 +1,7 @@
 import create from 'zustand'
 import { persist } from 'zustand/middleware'
 
-const createPersistantStore = (initialValue: string | null) => {
+const createPersistentStore = (initialValue: string | null) => {
   let state = initialValue
 
   const getItem = (): string | null => {
@@ -92,7 +92,7 @@ describe('persist middleware with sync configuration', () => {
   })
 
   it('can persist state', () => {
-    const { storage, setItemSpy } = createPersistantStore(null)
+    const { storage, setItemSpy } = createPersistentStore(null)
 
     const createStore = () => {
       const onRehydrateStorageSpy = jest.fn()