API: Utilities
SoulState provides utility functions to enhance state management and optimize React component re-renders.
shallow
The shallow utility performs a shallow comparison of two objects, checking if their top-level properties are equal. It's primarily used with useStore or store.subscribe() when selectors return new objects.
Summary
shallow compares the top-level properties of two objects using strict equality (Object.is). It returns true if all properties are equal, false otherwise.
Function Signature
function shallow<T, U>(a: T, b: U): boolean;
Parameters
a: T: The first value to compareb: U: The second value to compare
Return Value
boolean:trueif values are shallowly equal,falseotherwise
Usage Examples
With useStore
Use shallow as the third argument to useStore when selecting multiple properties.
import { useStore } from 'soulstate/react';
import { shallow } from 'soulstate/utils';
import { userStore } from '../stores/userStore';
function UserProfile() {
const { name, email } = useStore(
userStore,
(state) => ({ name: state.name, email: state.email }),
shallow // Only re-render when name or email changes
);
return (
<div>
<p>Name: {name}</p>
<p>Email: {email}</p>
</div>
);
}
With store.subscribe()
Use shallow with the low-level subscription API.
import { userStore } from '../stores/userStore';
import { shallow } from 'soulstate/utils';
const unsubscribe = userStore.subscribe(
(state) => ({ name: state.name, email: state.email }),
(newData, prevData) => {
console.log('User data changed:', newData);
},
{ equalityFn: shallow }
);
// Clean up
unsubscribe();
Comparison Examples
import { shallow } from 'soulstate/utils';
// Primitives - same as Object.is
shallow(1, 1); // true
shallow(1, 2); // false
// Objects with same properties
shallow({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
shallow({ a: 1, b: 2 }, { a: 1, b: 3 }); // false
// Different property counts
shallow({ a: 1 }, { a: 1, b: 2 }); // false
// Nested objects (only top-level compared)
shallow({ a: { b: 1 } }, { a: { b: 1 } }); // false (different a references)
objectIs
The default equality function used by SoulState, providing strict reference equality checking.
Function Signature
function objectIs(a: any, b: any): boolean;
Usage
import { objectIs } from 'soulstate/utils';
// Same as Object.is
objectIs(1, 1); // true
objectIs(NaN, NaN); // true
objectIs(0, -0); // false
// Reference equality for objects
const obj = {};
objectIs(obj, obj); // true
objectIs({}, {}); // false
soulBatch
Utility for batching multiple state updates (Note: SoulState automatically batches updates with microtasks, so this is primarily for advanced use cases).
Function Signature
function createBatch(): {
batch: (callback: () => void, store: any) => void;
};
const soulBatch = createBatch();
Usage
import { soulBatch } from 'soulstate/utils';
// Manual batching (rarely needed due to automatic microtask batching)
soulBatch.batch(() => {
store.set({ count: 1 });
store.set({ user: 'John' });
store.set({ active: true });
}, store);
devtools
Utility for connecting SoulState stores to Redux DevTools Extension.
Function Signature
function devtools(
store: any,
config?: DevToolsConfig
): any;
interface DevToolsConfig {
name?: string;
trace?: boolean;
features?: any;
}
Usage
import { createStore } from 'soulstate';
import { devtools } from 'soulstate/utils';
// Create store
const store = createStore({ count: 0 });
// Enhance with DevTools
const storeWithDevTools = devtools(store, {
name: 'CounterStore',
trace: true
});
// All updates will appear in Redux DevTools
storeWithDevTools.set({ count: 1 });
Anti-patterns
Using shallow Unnecessarily
// ❌ Unnecessary - Object.is is sufficient for primitives
const count = useStore(store, state => state.count, shallow);
// ✅ Better - use default equality for primitives
const count = useStore(store, state => state.count);
Expecting Deep Comparison
// ❌ shallow only compares top-level properties
const user = useStore(
store,
state => ({ profile: state.user.profile }),
shallow
);
// Won't detect changes in state.user.profile.name
// ✅ Select nested properties directly
const profileName = useStore(store, state => state.user.profile.name);
Performance Characteristics
shallow: O(n) where n is number of propertiesobjectIs: O(1) constant time operation- Automatic batching: All updates batched with
queueMicrotask
Best Practices
✅ Do:
- Use
shallowfor object selections from multiple properties - Use default
Object.isfor primitive values - Trust SoulState's automatic microtask batching
- Import utilities from
'soulstate/utils'
❌ Don't:
- Use
shallowfor primitive values - Expect
shallowto compare nested objects - Manually batch updates unless necessary
- Use utilities for simple property access
Import Path
All utilities are exported from 'soulstate/utils', not from the main package or React bindings.
Automatic Batching
SoulState automatically batches all state updates using microtasks. The soulBatch utility is rarely needed in practice.
Key Takeaway
SoulState's utilities provide optimized equality checking and developer tools integration. Use shallow for efficient object comparisons and trust the built-in performance optimizations.