shallow is a utility function that performs shallow equality comparison between two values.
Import
import { shallow } from 'zustand/shallow'
Signature
function shallow<T>(valueA: T, valueB: T): boolean
Parameters
The first value to compare
The second value to compare
Returns
Returns true if the values are shallowly equal, false otherwise
Usage
Comparing primitives
import { shallow } from 'zustand/shallow'
shallow('John Doe', 'John Doe') // true
shallow(10, 10) // true
shallow(true, true) // true
shallow(1n, 1n) // true
Comparing objects
const objA = { firstName: 'John', lastName: 'Doe', age: 30 }
const objB = { firstName: 'John', lastName: 'Doe', age: 30 }
Object.is(objA, objB) // false (different references)
shallow(objA, objB) // true (same properties and values)
Comparing nested objects (limitation)
const objA = {
name: 'John',
address: { city: 'New York', zip: '10001' }
}
const objB = {
name: 'John',
address: { city: 'New York', zip: '10001' }
}
shallow(objA, objB) // false (nested objects have different references)
Shallow comparison only checks top-level properties. Nested objects are compared by reference, not by value.
Comparing arrays
const arrA = [1, 2, 3]
const arrB = [1, 2, 3]
shallow(arrA, arrB) // true (same length and elements)
Comparing Sets
const setA = new Set([1, 2, 3])
const setB = new Set([1, 2, 3])
shallow(setA, setB) // true (same elements)
Comparing Maps
const mapA = new Map([['a', 1], ['b', 2]])
const mapB = new Map([['a', 1], ['b', 2]])
shallow(mapA, mapB) // true (same key-value pairs)
Use with zustand
As an equality function
import { create } from 'zustand'
import { shallow } from 'zustand/shallow'
type State = {
user: { name: string; age: number }
settings: { theme: string }
}
const useStore = create<State>()(() => ({
user: { name: 'John', age: 30 },
settings: { theme: 'dark' },
}))
function Component() {
const user = useStore(
(state) => state.user,
shallow // Prevents re-render if user object has same properties
)
}
With useStoreWithEqualityFn
import { useStoreWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'
function Component() {
const { name, age } = useStoreWithEqualityFn(
store,
(state) => ({ name: state.user.name, age: state.user.age }),
shallow
)
}
How it works
- Reference equality: First checks if
valueA and valueB are the same reference using Object.is
- Type checking: Returns
false if either value is not an object or is null
- Prototype check: Returns
false if objects have different prototypes
- Iterable comparison: For iterables (arrays, Sets, Maps), compares elements/entries
- Object comparison: For plain objects, compares all enumerable properties
Prototype checking
const a = Object.create({}) // prototype is {}
const b = {} // prototype is Object.prototype
shallow(a, b) // false (different prototypes)
Objects created with {} or new Object() inherit from Object.prototype, while Object.create(proto) can have a custom prototype.
Notes
- Shallow comparison is fast and suitable for most cases
- Only compares top-level properties by value
- Nested objects/arrays are compared by reference
- For deep equality, consider using a deep equality library like
lodash.isEqual
- Commonly used with
useStoreWithEqualityFn to optimize re-renders