Skip to main content

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 compare
  • b: U: The second value to compare

Return Value

  • boolean: true if values are shallowly equal, false otherwise

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 properties
  • objectIs: O(1) constant time operation
  • Automatic batching: All updates batched with queueMicrotask

Best Practices

✅ Do:

  • Use shallow for object selections from multiple properties
  • Use default Object.is for primitive values
  • Trust SoulState's automatic microtask batching
  • Import utilities from 'soulstate/utils'

❌ Don't:

  • Use shallow for primitive values
  • Expect shallow to 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.