Immutability in Anchor for React
Immutability is a cornerstone of robust, predictable React applications. It ensures that state objects cannot be changed once created, preventing unintended side effects and making your application easier to reason about. Anchor provides powerful immutability features that work seamlessly with React's component model.

Why Anchor's Immutability Matters in React
React's rendering and reconciliation system relies on the principle of immutability to efficiently detect when state changes occur. However, maintaining true immutability in complex applications presents several challenges:
- Performance Overhead: Traditional immutable patterns require creating new object instances for every state change, even for deeply nested structures. This leads to expensive deep cloning operations that consume memory and CPU resources.
- Complex Update Logic: Updating nested state requires careful handling of object spreads and array operations to maintain immutability, resulting in verbose and error-prone code.
- Developer Experience: The verbosity of immutable update patterns can obscure the intent of the code and make it harder to maintain.
- Memory Management: Creating new objects for every state change can lead to increased garbage collection pressure and memory usage.
Traditional approaches to immutability in React involve creating new objects for every change:
Verbosity, Hard to Maintain
Traditional React - complex immutable updates
setState((prev) => ({
...prev,
user: {
...prev.user,
profile: {
...prev.user.profile,
name: 'New Name',
},
},
}));
For arrays, even more complex patterns are needed:
setTodos((prev) => [...prev.slice(0, index), { ...prev[index], completed: true }, ...prev.slice(index + 1)]);
Anchor addresses these challenges by providing true immutability without the performance costs:
Intuitive, Easy to Maintain
// With Anchor - direct mutation syntax with true immutability
state.user.profile.name = 'New Name';
state.todos[1].completed = true;
True Immutability in Anchor for React
Anchor's approach to immutability provides the benefits of immutable state without the complexity:
- Direct Mutation Syntax: Write code that looks like direct mutation but maintains immutability guarantees
- Performance: No expensive deep cloning operations
- Type Safety: Strong TypeScript support prevents accidental mutations at compile time
- Controlled Mutations: Write contracts ensure only authorized changes occur
Catch Illegal Mutation Early!

Developer Friendly Error!

Try It Yourself!
Shared Immutable States
Global states are declared outside component bodies and shared across multiple components. These states are always declared using Anchor's Core APIs and persist throughout the application lifecycle.
Creating Global Immutable States
To create a global immutable state, use the anchor.immutable
method from @anchorlib/core
:
Global State
// lib/state.ts
import { anchor } from '@anchorlib/core';
// Create a global immutable state
export const userState = anchor.immutable({
id: 1,
name: 'John Doe',
email: 'john@example.com',
preferences: {
theme: 'dark',
notifications: true,
},
});
// Create a writer for controlled mutations
export const userWriter = anchor.writable(userState, ['name']);
export const preferenceWriter = anchor.writable(userState.preferences, ['theme']);
Using Global Immutable States in Components
To use global immutable states in React components, import them and access their values directly:
Using Global Immutable States in Components
// components/UserProfile.tsx
import { observe } from '@anchorlib/react/components';
import { userState, preferenceWriter } from '../lib/state';
const UserProfile = observe(() => {
return (
<div>
<h1>{userState.name}</h1>
<p>{userState.email}</p>
<p>Theme: {preferenceWriter.theme}</p>
<button onClick={() => (preferenceWriter.theme = preferenceWriter.theme === 'dark' ? 'light' : 'dark')}>
Toggle Theme
</button>
</div>
);
});
export default UserProfile;
Try It Yourself!
Local Immutable States
Local states are declared inside component bodies using React hooks and are primarily used for component-specific data. These states are created when a component mounts and are destroyed when it unmounts.
Creating Local Immutable States
To create a local immutable state, use the useImmutable hook:
Local Immutable States
import { useImmutable } from '@anchorlib/react';
const UserProfile = () => {
// Create a local immutable state
const [user] = useImmutable({
name: 'John Doe',
email: 'john@example.com',
preferences: {
theme: 'dark',
notifications: true,
},
});
// Reading properties works normally
console.log(user.name); // 'John Doe'
// Direct mutations are prevented
// user.name = 'Jane Doe'; // This will be trapped and produce an error
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
};
Using Writers with Local Immutable States
To modify local immutable states, create a writer using the useWriter hook:
Using Writers
import { useImmutable, useWriter } from '@anchorlib/react';
const SettingsPanel = () => {
// Create local immutable state
const [settings] = useImmutable({
theme: 'dark',
language: 'en',
notifications: true,
});
// Create writer with contracts
const writer = useWriter(settings, ['theme', 'notifications']);
return (
<div>
<p>Theme: {settings.theme}</p>
<p>Language: {settings.language}</p>
<p>Notifications: {settings.notifications ? 'On' : 'Off'}</p>
<button onClick={() => (writer.theme = settings.theme === 'dark' ? 'light' : 'dark')}>Toggle Theme</button>
<button onClick={() => (writer.notifications = !settings.notifications)}>Toggle Notifications</button>
{/* This won't work - language is not in the contract */}
{/* <button onClick={() => writer.language = 'es'}>Change Language</button> */}
</div>
);
};
Immutable with Schema
You can also create immutable models with schema validation using useImmutableModel for local states or anchor.immutable
with a schema for global states:
Global Immutable Models
Global Immutable Models
// lib/models.ts
import { anchor } from '@anchorlib/core';
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(0).max(150),
});
// Create a global immutable model
export const userState = anchor.immutable(
{
name: 'John Doe',
email: 'john@example.com',
age: 30,
},
userSchema
);
// Create a writer for mutations
export const userWriter = anchor.writable(userState);
Local Immutable Models
Local Immutable Models
import { useImmutableModel, useWriter } from '@anchorlib/react';
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(0).max(150),
});
const UserEditor = () => {
const [user] = useImmutableModel(userSchema, {
name: 'John Doe',
email: 'john@example.com',
age: 30,
});
// Create a writer for mutations
const writer = useWriter(user);
const updateName = (newName) => {
writer.name = newName; // Valid mutation
};
return (
<div>
<p>Name: {user.name}</p>
<input value={user.name} onChange={(e) => updateName(e.target.value)} />
</div>
);
};
Benefits of True Immutability in React
1. Performance
Anchor's immutability system avoids the performance penalties of deep cloning while maintaining immutability guarantees.
2. Developer Experience
Write code that feels natural while maintaining immutability:
// Complex nested updates become simple with writers
const addTodo = (todosWriter, text) => {
todosWriter.push({ id: Date.now(), text, completed: false });
};
// Array mutations work naturally
const removeTodo = (todosWriter, id) => {
const index = todosWriter.findIndex((todo) => todo.id === id);
if (index !== -1) {
todosWriter.splice(index, 1);
}
};
3. Predictability
With true immutability, you get predictable state management:
// State changes are always explicit and controlled
const writer = useWriter(userState, ['name', 'email']);
// These are the ONLY properties that can be changed
writer.name = 'New Name'; // ✅ Allowed
writer.email = 'new@email.com'; // ✅ Allowed
// writer.id = 123; // ❌ Not allowed - not in contract
Best Practices for Immutability in React
1. Use Global Immutable States for Shared Data
Create global immutable states for data that should be shared across components:
// lib/appState.ts
import { anchor } from '@anchorlib/core';
// Good: Configuration data that should remain consistent
export const appConfig = anchor.immutable({
apiUrl: process.env.REACT_APP_API_URL,
version: '1.0.0',
features: {
darkMode: true,
notifications: true,
},
});
// components/App.tsx
import { observe } from '@anchorlib/react/components';
import { appConfig } from '../lib/appState';
const Header = observe(() => (
<header>
<h1>App v{appConfig.version}</h1>
</header>
));
const Main = observe(() => <main>{/* Content using appConfig */}</main>);
const App = () => {
return (
<div>
<Header />
<Main />
</div>
);
};
2. Create Specific Write Contracts
Always define specific contracts for writers:
// lib/userProfile.ts
import { anchor } from '@anchorlib/core';
// Global immutable state
export const userProfile = anchor.immutable({
personal: { name: 'John', email: 'john@example.com' },
preferences: { theme: 'dark', lang: 'en' },
});
// Create specific writers for different parts of the app
export const profileWriter = anchor.writable(userProfile, ['personal']);
export const preferencesWriter = anchor.writable(userProfile, ['preferences']);
// components/ProfileEditor.tsx
import { observe } from '@anchorlib/react/components';
import { userProfile, profileWriter } from '../lib/userProfile';
const ProfileEditor = observe(() => (
<input value={userProfile.personal.name} onChange={(e) => (profileWriter.personal.name = e.target.value)} />
));
// components/Preferences.tsx
import { observe } from '@anchorlib/react/components';
import { userProfile, preferencesWriter } from '../lib/userProfile';
const Preferences = observe(() => (
<select
value={userProfile.preferences.theme}
onChange={(e) => (preferencesWriter.preferences.theme = e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
));
3. Combine with Reactivity Patterns
Use immutability with Anchor's reactivity patterns for optimal performance:
import { useImmutable, useWriter } from '@anchorlib/react';
import { observe } from '@anchorlib/react/components';
const TodoApp = () => {
// Local immutable state
const [todos] = useImmutable([]);
// Writer for mutations
const todosWriter = useWriter(todos);
// View that only re-renders when todos change
const TodoList = observe(() => (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
));
const addTodo = (text) => {
// Natural array mutation
todosWriter.push({ id: Date.now(), text, completed: false });
};
return (
<div>
<TodoList />
<button onClick={() => addTodo('New Todo')}>Add Todo</button>
</div>
);
};
API Reference
anchor.immutable(init, schema?)
(Core API)
Creates an immutable reactive state from the provided initial value using Anchor's Core API.
Parameters:
init
: The initial value for the stateschema
(optional): Zod schema to validate and structure the state
anchor.writable(state, contracts?)
(Core API)
Creates a writable version of an immutable state with optional contracts using Anchor's Core API.
Parameters:
state
: The immutable state to make writablecontracts
(optional): Array of allowed mutation keys
useImmutable(init, options?)
(React Hook)
A React hook that creates an immutable state from a linkable object or model input. The resulting state is read-only and requires special writers for mutations.
Parameters:
init
: The initial linkable object to make immutable.schema
(optional): Zod schema to apply to the model input.options
(optional): Optional anchor configuration options.
useWriter(state, contracts?)
(React Hook)
A React hook that creates a mutation gateway of an immutable state. This allows controlled mutations of otherwise immutable states.
Parameters:
state
: The immutable state to create a writer for.contracts
(optional): Mutation key contracts that define allowed mutations.
useImmutableModel(schema, init, options?)
(React Hook)
Creates an immutable reactive model based on the provided Zod schema and initial data. The resulting state is read-only and requires special writers for mutations.
Parameters:
schema
: The Zod schema defining the structure and types of the model.init
: The initial data for the model.options
(optional): Optional configuration for the model state.
Next Steps
To learn more about immutability in Anchor for React:
- Review the Getting Started guide for basic usage
- Explore Reactivity to understand how immutable state works with observation