Skip to content

Initialization

Initialization in Anchor for React is the process of creating reactive state objects that can be observed and mutated. Understanding how to properly initialize state is crucial for building efficient and maintainable applications with Anchor.

State Types

In Anchor, states are categorized into two main types based on their scope and usage pattern:

Global State

Global states are declared outside component bodies, typically in separate files, and are shared across multiple components. These states are always declared using Anchor's Core APIs and persist throughout the application lifecycle.

Global states are ideal for:

  • Application-wide data (user profile, settings, etc.)
  • Data shared across multiple components
  • Complex business logic that needs to be accessed from different parts of the application
Global State
ts
// lib/app.ts
import { anchor } from '@anchorlib/core';

// Global state is declared outside component body.
export const appState = anchor.immutable({
  currentUser: {
    name: 'John Doe',
    age: 30,
  },
});

Global State Recommendations

When working with global states, it's always recommended to use immutable states. This ensures that your application remains predictable and your states remains stable.

When working with a critical data structure, it's also recommended to combine immutable with schema to ensure the data shape remains consistent.

Local State

Local states are declared inside component bodies using React hooks and are primarily used for UI behavior logic such as toggle states, tab states, form inputs, etc. These states are created when a component mounts and are destroyed when it unmounts.

Local states are ideal for:

  • Component-specific UI state
  • Temporary data that doesn't need to persist
  • Simple interactions within a component
Local State
tsx
// components/UserProfile.tsx
import { useAnchor } from '@anchorlib/react';

export const UserProfile = () => {
  // Local state is declared inside component body.
  const [tab] = useAnchor({
    buttons: ['Tab 1', 'Tab 2', 'Tab 3'],
    current: 0,
  });
};

Core State APIs

These are the primary APIs for creating reactive state in your React components.

useAnchor(init, options?)

The primary hook for creating and managing reactive state within React components. It's most suitable for objects and complex data structures. The state object can be directly mutated, and changes will be automatically tracked.

Params

  • init - The initial value for the state.
  • schema (optional) - Zod schema to validate and structure the state.
  • options (optional) - Configuration options for the state.

API Reference

Usage

To use useAnchor, call it within your component body with an initial object value:

Object State
jsx
import { useAnchor } from '@anchorlib/react';

const UserProfile = () => {
  const [user] = useAnchor({
    name: 'John Doe',
    email: 'john.doe@example.com',
    age: 30,
    preferences: {
      theme: 'dark',
      notifications: true,
    },
  });

  // Direct mutation - intuitive and natural
  const updateTheme = () => {
    user.preferences.theme = 'light';
  };

  const updateName = () => {
    user.name = 'Jane Doe';
  };
};
With Zod Schema
jsx
import { useAnchor } from '@anchorlib/react';
import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(1),
  age: z.number().min(0),
  email: z.string().email(),
  preferences: z.object({
    theme: z.enum(['light', 'dark']),
    notifications: z.boolean(),
  }),
});

const UserProfile = () => {
  const [user] = useAnchor(
    {
      name: 'John Doe',
      age: 30,
      email: 'john.doe@example.com',
      preferences: {
        theme: 'dark',
        notifications: true,
      },
    },
    userSchema
  );

  // Schema ensures data integrity while allowing direct mutations
  const updateProfile = () => {
    user.name = 'Jane Doe';
    user.preferences.theme = 'light';
  };
};

When to use it?

Use useAnchor when you need a fully reactive object state that can be directly mutated. This is the most common hook for creating complex state objects in Anchor, especially when you need to mutate nested properties.

useVariable(init, deps?, constant?)

Creates a reactive variable with update capabilities. This hook is most suitable for primitive values like numbers, strings, and booleans.

Params

  • init - Initial value or initializer function.
  • deps (optional) - Dependencies that trigger updates.
  • constant (optional) - Whether to treat as constant.

API Reference

Usage

Primitive State
jsx
import { useVariable } from '@anchorlib/react';

const Counter = () => {
  const [count] = useVariable(0);

  // Direct mutation - simple and intuitive syntax
  const increment = () => {
    count.value++;
  };

  const decrement = () => {
    count.value--;
  };

  const reset = () => {
    count.value = 0;
  };
};
Computed Primitive
jsx
import { useVariable } from '@anchorlib/react';

const ProductCard = ({ user }) => {
  // The value is automatically updated when user changes, or manually assigned.
  const [userId] = useVariable(() => user.id, [user]);

  const switchUser = () => {
    userId.value = 'some-id';
  };
};

When to use it?

Use useVariable when you need a simple reactive primitive value (number, string, boolean) that can be directly mutated. It's the go-to hook for basic state values like counters, flags, or simple text values.

useConstant(init, deps?)

Creates a constant reference that never changes its value or only updates when dependencies change. This hook is perfect for computed values that depend on props or other changing values.

Params

  • init - The initial value or initializer function.
  • deps (optional) - Dependency array that determines when the constant should be recalculated.

API Reference

Usage

Computed Constant
jsx
import { useConstant } from '@anchorlib/react';

const UserProfile = ({ userId }) => {
  const [apiEndpoint] = useConstant(() => `/api/users/${userId}/profile`, [userId]);
};
Complex Computed Value
jsx
import { useConstant } from '@anchorlib/react';

const DataProcessor = ({ rawData, multiplier }) => {
  const [processedData] = useConstant(() => {
    return rawData.map((item) => ({
      ...item,
      computedValue: item.baseValue * multiplier,
    }));
  }, [rawData, multiplier]);
};

When to use it?

Use useConstant when you need a stable reference to a computed value that depends on changing inputs. It's especially useful for derived values that would otherwise need to be recomputed on every render.

Immutability APIs

These APIs provide immutability features for your state, ensuring controlled mutations.

useImmutable(init, options?)

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.

Params

  • init - The initial linkable object to make immutable.
  • schema (optional) - Zod schema to apply to the model input.
  • options (optional) - Optional anchor configuration options.

API Reference

Usage

Immutable Data
jsx
import { useImmutable, useWriter } from '@anchorlib/react';

const Dashboard = () => {
  const [reportData] = useImmutable({
    generatedAt: new Date(),
    metrics: {
      users: 1250,
      revenue: 35000,
      conversion: 0.08,
    },
  });

  // Use a writer to make changes.
  const writer = useWriter(reportData);

  // For mutations, use a writer
  const refreshData = (newMetrics) => {
    writer.generatedAt = new Date();
    writer.metrics = newMetrics;
  };
};

When to use it?

Use useImmutable when you want to create a state that is immutable by default but still reactive. This is useful for protecting important data from accidental mutations while still allowing controlled changes through writers.

useWriter(state, contracts?)

A React hook that creates a mutation gateway of an immutable state. This allows controlled mutations of otherwise immutable states.

Params

  • state - The immutable state to create a writer for.
  • contracts (optional) - Mutation key contracts that define allowed mutations.

API Reference

Usage

Controlled Mutations
jsx
import { useImmutable, useWriter } from '@anchorlib/react';

const DocumentEditor = () => {
  const [document] = useImmutable({
    title: 'My Document',
    content: 'Document content...',
    lastModified: new Date(),
  });

  // Full writer allows all mutations
  const writer = useWriter(document);

  const updateTitle = (newTitle) => {
    writer.title = newTitle;
    writer.lastModified = new Date();
  };

  // Contracted writer only allows specific mutations
  const contentWriter = useWriter(document, ['content', 'lastModified']);

  const updateContent = (newContent) => {
    contentWriter.content = newContent;
    contentWriter.lastModified = new Date();
    // writer.title = 'New Title'; // This would be restricted
  };
};

When to use it?

Use useWriter when you need to mutate an immutable state. This provides a controlled way to make changes while maintaining the immutability guarantees for the rest of your application.

Data Integrity APIs

These APIs provide schema-based validation and data integrity features for your state using Zod schemas.

useModel(schema, init, options?)

Creates a reactive model based on the provided Zod schema and initial data. This ensures your state conforms to a specific structure and validates data according to the schema.

Params

  • 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.

API Reference

Usage

Form Model
jsx
import { useModel } from '@anchorlib/react';
import { z } from 'zod';

const userFormSchema = z.object({
  name: z.string().min(1, 'Name is required'),
  email: z.string().email('Invalid email format'),
  age: z.number().min(18, 'Must be at least 18 years old'),
});

const UserForm = () => {
  const [form] = useModel(userFormSchema, {
    name: '',
    email: '',
    age: 18,
  });

  // Direct mutations with automatic validation
  const updateName = (newName) => {
    form.name = newName; // Will be validated automatically
  };

  const updateEmail = (newEmail) => {
    form.email = newEmail; // Validation errors will be caught
  };
};

When to use it?

Use useModel when you need to ensure your state conforms to a specific structure with validation using Zod schemas. This is especially useful for form data or any state that must follow a strict format with validation requirements.

useImmutableModel(schema, init, options?)

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.

Params

  • 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.

API Reference

Usage

Immutable Model
jsx
import { useImmutableModel, useWriter } from '@anchorlib/react';
import { z } from 'zod';

const configSchema = z.object({
  theme: z.enum(['light', 'dark']),
  language: z.string(),
  notifications: z.boolean(),
});

const AppSettings = () => {
  const [config] = useImmutableModel(configSchema, {
    theme: 'light',
    language: 'en',
    notifications: true,
  });

  // Use a writer to make changes.
  const writer = useWriter(config);

  // Need to use a writer to make changes
  const updateTheme = () => {
    writer.theme = 'dark';
  };
};

When to use it?

Use useImmutableModel when you need a strictly validated state that should be immutable by default using Zod schemas. Changes require explicit writers, making mutations intentional and controlled.

Array APIs

These APIs provide specialized state management for array-based data.

useOrderedList(init, compare, options?)

Creates a reactive ordered list state that automatically maintains sort order based on the provided comparison function.

Params

  • init - The initial array state.
  • compare - A comparison function that defines the sort order.
  • options (optional) - Optional state configuration options.

API Reference

Usage

Sorted List
jsx
import { useOrderedList } from '@anchorlib/react';

const TaskList = () => {
  const [tasks] = useOrderedList(
    [
      { id: 1, title: 'Low priority task', priority: 'low' },
      { id: 2, title: 'High priority task', priority: 'high' },
      { id: 3, title: 'Medium priority task', priority: 'medium' },
    ],
    (a, b) => {
      const priorityOrder = { high: 3, medium: 2, low: 1 };
      return priorityOrder[b.priority] - priorityOrder[a.priority];
    }
  );

  // Tasks are automatically kept sorted by priority
  // Adding new tasks will insert them in the correct position
  const addTask = (newTask) => {
    tasks.push(newTask); // Will automatically sort into correct position
  };
};

When to use it?

Use useOrderedList when you need an array that automatically maintains a specific sort order. Elements are automatically positioned correctly when added, modified, or when the sort criteria change.

History APIs

These APIs provide undo/redo functionality for your state.

useHistory(state, options?)

Provides history management (undo/redo) for a given reactive state. This allows users to undo and redo changes to the state.

Params

  • state - The reactive state to track history for.
  • options (optional) - History configuration options.

API Reference

Usage

Document History
jsx
import { useAnchor, useHistory } from '@anchorlib/react';

const DocumentEditor = () => {
  const [document] = useAnchor({
    title: 'My Document',
    content: 'Document content...',
  });

  const history = useHistory(document, {
    maxHistory: 50,
    debounce: 300,
  });

  // Make changes to document
  const updateContent = (newContent) => {
    document.content = newContent;
    // Change is automatically tracked in history
  };

  // Navigate history intuitively
  const undo = () => {
    if (history.canBackward) {
      history.backward();
    }
  };

  const redo = () => {
    if (history.canForward) {
      history.forward();
    }
  };
};

When to use it?

Use useHistory when you want to provide undo/redo functionality for a state. Changes are automatically tracked, making it easy to implement history navigation without manual intervention.

Request APIs

These APIs provide reactive data fetching and streaming functionalities.

useFetch(init, options)

Provides reactive data fetching functionality, managing the state of an HTTP request. It handles loading states, errors, and data updates automatically.

Params

  • init - Initial data value.
  • options - Fetch configuration options.

API Reference

Usage

Data Fetching
jsx
import { useFetch } from '@anchorlib/react';

const UserList = () => {
  const [users] = useFetch([], {
    url: '/api/users',
    method: 'GET',
  });

  // Access request state intuitively
  const refresh = () => {
    users.fetch(); // Trigger a new request
  };

  const cancel = () => {
    users.abort(); // Cancel ongoing request
  };
};
POST Request
jsx
import { useFetch } from '@anchorlib/react';

const UserCreator = () => {
  const [response] = useFetch(null, {
    url: '/api/users',
    method: 'POST',
    deferred: true, // Defer request until fetch() is called.
  });

  const createUser = (userData) => {
    response.fetch({
      body: userData,
    });
  };
};

When to use it?

Use useFetch when you need to fetch data from an API and want automatic state management for loading, error, and success states. The hook provides a reactive way to handle HTTP requests with minimal boilerplate.

useStream(init, options)

Provides reactive streaming data fetch functionality, updating incrementally as chunks are received.

Params

  • init - Initial data value.
  • options - Stream configuration options.

API Reference

Usage

Streaming Data
jsx
import { useStream } from '@anchorlib/react';

const EventFeed = () => {
  const [events] = useStream([], {
    url: '/api/events/stream',
    method: 'GET',
    transform: (current, chunk) => [...current, ...chunk],
  });

  const disconnect = () => {
    events.abort();
  };
};

When to use it?

Use useStream when you need to handle streaming data, such as server-sent events or chunked responses. The state automatically updates as new data arrives, making real-time features simple to implement.