Skip to content

Reactivity

The reactive system maintains a directed acyclic graph (DAG) of dependencies and updates are processed in topological order to ensure consistency:

  1. signal: Updated first.
  2. computed: Recalculated in dependency order.
  3. effect: Run last, after all computations are complete.

This guarantees that effects always see the final, consistent state of all dependencies.

import { signal, computed, effect } from '@hellajs/core';
// Example dependency topology:
// first (signal) & last (signal) → fullName (computed) → display (effect)
const first = signal('John');
const last = signal('Doe');
const fullName = computed(() => `${first()} ${last()}`);
effect(() => {
console.log('Display:', fullName()); // Depends on fullName
});
first('Jane'); // Triggers: fullName → display effect

You don’t need to use computed inside a component. A function that uses a signal is sufficient.

import { signal } from '@hellajs/core';
const Counter = () => {
const count = signal(1);
const doubleCount = () => count() * 2;
return (
<div>
<p>Counter: {count}</p>
<p>Double Count: {doubleCount}</p>
</div>
);
};

Stores are a great way to manage state in a more structured way. They allow you to create deeply reactive objects with nested properties.

import { store } from '@hellajs/store';
const userStore = store({
name: 'John Doe',
age: 30,
address: {
street: '123 Main St',
city: 'New York',
},
});
console.log(userStore.name()); // 'John Doe'
console.log(userStore.address.city()); // 'New York'
userStore.address.city("San Francisco");
console.log(userStore.address.city()); // 'San Francisco'

Resources provide reactive async data fetching with automatic caching, loading states, and error handling. They integrate seamlessly with the reactive system.

import { signal } from '@hellajs/core';
import { resource } from '@hellajs/resource';
const userId = signal('1');
// Resource automatically refetches when userId changes
const userResource = resource(
(id: string) => fetch(`/api/user/${id}`).then(r => r.json()),
{
key: userId,
cacheTime: 60000, // Cache for 1 minute
}
);
// All resource properties are reactive
effect(() => {
if (userResource.loading()) {
console.log('Loading user...');
} else if (userResource.error()) {
console.log('Error:', userResource.error());
} else if (userResource.data()) {
console.log('User data:', userResource.data());
}
});
// Trigger fetch when component mounts
userResource.fetch();
// Changing userId automatically invalidates cache and refetches
userId('2'); // Will trigger new fetch for user 2