Guide
Reactive System
TSone's reactivity system lets you create reactive state and automatically update the UI when it changes.
Core Concepts
Reactive Objects
reactive creates reactive objects that automatically track dependencies when properties are read and changed.
Effect Functions
effect creates effect functions that run again when dependencies change.
Dependency Tracking
The framework records reactive properties accessed while an effect runs.
Core APIs
reactive
import { reactive } from '@geektech/tsone';
const state = reactive({
count: 0,
user: { name: 'John', age: 30 },
});
console.log(state.count);
state.count = 1;effect
import { effect, reactive } from '@geektech/tsone';
const state = reactive({ count: 0 });
effect(() => {
console.log(`Count: ${state.count}`);
});
state.count = 1;
state.count = 2;computed
import { computed, reactive } from '@geektech/tsone';
const state = reactive({ count: 0 });
const doubleCount = computed(() => state.count * 2);
console.log(doubleCount.value);
state.count = 1;
console.log(doubleCount.value);readonly
import { reactive, readonly } from '@geektech/tsone';
const original = reactive({ count: 0 });
const readOnlyState = readonly(original);
console.log(readOnlyState.count);
original.count = 1;ref
import { effect, ref } from '@geektech/tsone';
const count = ref(0);
effect(() => {
console.log(`Count: ${count.value}`);
});
count.value += 1;How Reactivity Works
TSone uses JavaScript Proxy to intercept get/set operations, collect dependencies, and dispatch updates.
Dependency Tracking Process
- Run effect and mark it as the active effect
- Read reactive or ref values inside effect
- Record the dependency relationship
- Schedule the corresponding effect again when values change
Advanced Usage
Nested Reactive Objects
const state = reactive({
user: { name: 'John', address: { city: 'New York' } },
});
effect(() => {
console.log(`${state.user.name} lives in ${state.user.address.city}`);
});
state.user.address.city = 'Los Angeles';Reactive Arrays
const state = reactive({ items: ['Item 1', 'Item 2'] });
effect(() => {
console.log(state.items.join(', '));
});
state.items.push('Item 3');
state.items[0] = 'Updated Item 1';Conditional Dependencies
const state = reactive({ showCount: true, count: 0, message: 'Hello' });
effect(() => {
if (state.showCount) {
console.log(`Count: ${state.count}`);
} else {
console.log(state.message);
}
});Stop an Effect
import { effect, reactive, stop } from '@geektech/tsone';
const state = reactive({ count: 0 });
const runner = effect(() => {
console.log(`Count: ${state.count}`);
});
stop(runner);watch Listeners
watch(source, callback, options) listens to a reactive source (a getter or a ref) and runs a callback when it changes, returning a stop function. The callback receives the new value, the old value, and an onCleanup parameter for registering cleanup. By default callbacks are batched with the scheduler, so multiple changes in one batch run the callback once.
import { reactive, ref, watch } from '@geektech/tsone';
const count = ref(0);
const stop = watch(count, (value, oldValue) => {
console.log(`count: ${oldValue} -> ${value}`);
});
const state = reactive({ profile: { name: 'John' } });
watch(() => state.profile.name, (name) => {
console.log('name changed to', name);
}, { deep: true });
stop();immediate: run the callback once right away (oldValue is undefined)deep: track nested property changes of the getter resultsync: invoke the callback synchronously on change (default is scheduler batching)
Best Practices
State Design
- Put only data that drives the UI into reactive objects
- Organize state by business module and avoid meaningless deep nesting
- Prefer computed for complex derived values
Performance Optimization
- Use computed caching for expensive calculations
- Avoid expensive calculations or chained side-effect writes in effect
- Use readonly for data that should not be written
Common Pitfalls
- Do not replace an entire reactive object directly
- Do not unconditionally modify dependency state inside effect
- Nested objects become reactive automatically; do not call reactive again manually
Integration with Components
class CounterComponent extends Component<object, { count: number }> {
protected initState() {
return { count: 0 };
}
protected initStyles(): void {}
protected render(): VNode {
return {
tag: 'button',
children: ['Count: '],
listeners: { click: () => this.state.count++ },
};
}
}Summary
With reactive, effect, computed, readonly, ref, and stop, TSone provides a unified reactive foundation for component state and standalone data models.