Getting Started with Anchor for React
This guide will quickly get you up and running with Anchor in your React project. You'll learn how to install Anchor, create your first reactive state, and connect it to your React components to build dynamic and performant UIs.
Installation
To begin, install the @anchorlib/react
package using your preferred package manager:
npm install @anchorlib/react
yarn add @anchorlib/react
pnpm add @anchorlib/react
bun add @anchorlib/react
Basic Usage
To get started quickly, import useAnchor
from @anchorlib/react
and observable
from @anchorlib/react/components
, then wrap your component with observable
HoC.
Your First Reactive Component
import { useAnchor } from '@anchorlib/react';
import { observable } from '@anchorlib/react/components';
const Counter = observable(() => {
const [counter] = useAnchor({ count: 0 });
return (
<div>
<h1>Counter: {counter.count}</h1>
<button onClick={() => counter.count++}>Increment</button>
<button onClick={() => counter.count--}>Decrement</button>
<button onClick={() => (counter.count = 0)}>Reset</button>
</div>
);
});
export default Counter;
Try It Yourself
In this example:
observable
HOC: By wrappingCounter
withobservable()
, you tell Anchor to automatically track any reactive state properties accessed insideCounter
. When those properties change,Counter
will re-render.useAnchor
Hook: This hook initializes your state ({ count: 0 }
) and makes it reactive. Thestate
variable you get back is a special state that aware of observer and subscriber.- Direct Mutation: Notice
state.count++
andstate.count--
. With Anchor, you directly modify your state. There's no need for immutable updates (likesetState(prev => ({...prev, count: prev.count + 1}))
) or dispatching actions. - Automatic UI Updates: Because
Counter
isobservable
, whenstate.count
changes, the component automatically re-renders to display the new value.
Why Anchor is different:
Direct Mutation, Safe Execution: Unlike other state management libraries, you can directly modify your state ( e.g.,
counter.count++
) without worrying about stale closures.Smart Reactivity: Anchor only re-renders components that actually use the changed data, not your entire app. This means better performance without manual optimizations.
No Boilerplate: Say goodbye to action creators, reducers, and complex update patterns. Just change your state directly and let Anchor handle the rest.
Reactivity Principles
While Anchor provides a suite of React hooks, most of them serve as initializers. Their primary purpose is to create reactive states and cache them, ensuring consistency and efficiency across renders. To dive deeper into the Anchor's Reactivity Model, we recommend reading the Reactivity section.
Why they don't trigger re-render?
Anchor's core principle is to deliver the best possible UX and DX (AX principle). If every hook were to trigger a re-render, components would constantly re-render whenever a state changes, leading to uncontrolled and inefficient rendering. This goes against our core principle of optimized performance.
Observation and Derivation
The primary mechanisms to trigger a re-render when a state changes are the Observation HOC and hooks, or the Derivation hooks. These tools are specifically designed to observe a state and derive a value from it, subsequently triggering a re-render only when the observed or derived value changes.
This approach allows us to meticulously control the re-rendering process, thereby avoiding unnecessary updates. We can selectively re-render only the necessary parts of a component, leaving static sections untouched, all without the need to break down the UI into numerous smaller components.
Ref System
Anchor introduces the Ref System to the React ecosystem. A Ref is a reactive state that holds a reference to a value, enabling primitives to be observed. It also allows you to pass the ref to a child component, granting them access and control over the value without the need to pass a setter function.
This approach allows each component to control when it updates itself based on changes to the ref value, eliminating the need for the entire component tree to re-render.
Basic Ref Usage
import { useVariable } from '@anchorlib/react';
import { observe } from '@anchorlib/react/components';
import { UserAccount } from './UserAccount.jsx';
export const UserProfile = () => {
const [userRef] = useVariable({
name: 'John Doe',
age: 30,
account: {
email: 'john@example.com',
username: 'johndoe',
},
});
const changeUser = () => {
userRef.value = {
name: 'Jane Doe',
age: Math.floor(Math.random() * 100),
account: {
email: 'jane@example.com',
username: 'janedoe',
},
};
};
const ProfileView = observe(() => {
const { name, age, account } = userRef.value;
return (
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
);
});
const AccountView = observe(() => {
const { email, username } = userRef.value.account;
return (
<div>
<p>Email: {email}</p>
<p>Username: {username}</p>
</div>
);
});
return (
<div>
<h1>Profile</h1>
<ProfileView />
<UserAccount userRef={userRef} />
<button onClick={() => userRef.value.age++}>Happy Birthday!</button>
<button onClick={changeUser}>Reset Profile</button>
</div>
);
};
import { observable } from '@anchorlib/react/components';
export const UserAccount = observable(({ userRef }) => {
const { account } = userRef.value;
const { email, username } = account;
return (
<div>
<label>
<span>Email:</span>
<input value={email} onChange={(e) => (account.email = e.target.value)} />
</label>
<label>
<span>Username:</span>
<input value={username} onChange={(e) => (account.username = e.target.value)} />
</label>
</div>
);
});
In this example (The DSV Pattern):
This example demonstrates the Data-State-View (DSV) pattern, a core concept in Anchor for building performant UIs.
The "State" (
UserProfile.jsx
): TheUserProfile
component acts as the State layer.- It uses the
useVariable
hook to create and hold the reactiveuserRef
(the Data). - Crucially,
UserProfile
itself does not observe theuserRef
's value in its render path. As a result, it * *never re-renders** when the user data changes. It purely manages the state and orchestrates the UI.
- It uses the
The "View" (
ProfileView
andUserAccount.jsx
): These components represent the View layer.ProfileView
is wrapped inobserve
. It reads data fromuserRef.value
and will only re-render when the specific values it uses (name
,age
,email
,username
) change.UserAccount
is wrapped inobservable
and also accessesuserRef
. It will re-render only when theaccount
properties it observes are modified.- They are responsible for presenting the data and capturing user input, but they don't own the state itself.
Decoupled and Efficient:
- By passing the
userRef
down, the "State" (UserProfile
) provides access to the data without creating a rendering dependency. - The "View" components (
ProfileView
,UserAccount
) subscribe to only the data they need, leading to highly optimized re-renders. The parent component is not involved in the update process of its children.
- By passing the
Direct State Mutation: Notice how state is still modified directly (e.g.,
userRef.value.age++
). Anchor's reactivity system ensures that even with direct mutation, the correct "View" components will update automatically.
Try It Yourself
Notes
Red flashes means the component is first rendered. Blue flashes means the component is re-rendered.
This DSV approach allows you to build complex UIs where state management is centralized and rendering is granular and efficient, avoiding the common pitfall of cascading re-renders in large component trees.
This covers the fundamental aspects of getting started with Anchor in your React applications. In the next sections, we will delve deeper into more advanced features like initialization options, observation patterns, derivation, and component integration.