@rbxts/signals
createRoot
Creates a new reactive root to manage computations and their lifecycle.
Signature
function createRoot<T>(fn: (dispose: () => void) => T): T;
Description
The createRoot()
function creates a new computation node that serves as the root of a reactive computation tree. It provides a way to contain and manage reactive computations, ensuring they can be properly disposed when no longer needed.
The function runs the provided callback immediately and passes a dispose function as its argument. When this dispose function is called, it cleans up all reactive computations created within the root, preventing memory leaks.
Using createRoot
is required to run any effect or signal-tracking elements without memory leak
Examples
Basic usage
const result = createRoot((dispose) => { const count = createSignal(0);
// Create computations that will be cleaned up when dispose is called createEffect(() => { print(`Count: ${count()}`); });
// Increment count after a delay task.delay(2, () => { count(1);
// Dispose all reactive computations after another delay task.delay(2, () => { dispose();
// This won't trigger the effect because it's been disposed count(2); }); });
return "Root created";});
print(result); // Prints: "Root created"// After 2 seconds, prints: "Count: 1"// After 4 seconds, all computations are disposed
Managing multiple reactive systems
// Create a function that sets up a timer and returns a dispose functionfunction createTimer(interval: number, callback: (time: number) => void) { return createRoot((dispose) => { const time = createSignal(0);
createEffect(() => { callback(time()); });
// Set up the interval const intervalId = setInterval(() => { time(time() + interval); }, interval);
// Clean up the interval when disposed onCleanup(() => { clearInterval(intervalId); });
return dispose; });}
// Create multiple timersconst disposeTimer1 = createTimer(1000, (time) => { print(`Timer 1: ${time}ms`);});
const disposeTimer2 = createTimer(500, (time) => { print(`Timer 2: ${time}ms`);});
// Later, dispose the timers when no longer neededtask.delay(5, () => { disposeTimer1(); // Dispose timer 1 print("Timer 1 disposed");});
task.delay(10, () => { disposeTimer2(); // Dispose timer 2 print("Timer 2 disposed");});
Creating isolated reactive systems
function createUserProfile(userId: number) { return createRoot((dispose) => { const user = createSignal<{ id: number, name: string } | null>(null); const loading = createSignal(true); const error = createSignal<string | null>(null);
// Fetch user data createEffect(() => { task.spawn(async () => { try { loading(true); error(null);
// Simulate API call await task.wait(1);
// Mock response if (userId > 0) { user({ id: userId, name: `User ${userId}` }); } else { error("Invalid user ID"); } } catch (err) { error(`Failed to load user: ${err}`); } finally { loading(false); } }); });
// Return both the reactive state and the dispose function return { getUser: () => user(), isLoading: () => loading(), getError: () => error(), dispose }; });}
// Create an isolated user profile systemconst userProfile = createUserProfile(1);
// Check user data periodicallyconst checkInterval = setInterval(() => { if (userProfile.isLoading()) { print("Still loading..."); } else if (userProfile.getError()) { print(`Error: ${userProfile.getError()}`); } else { const user = userProfile.getUser(); print(`User loaded: ${user?.name}`); }}, 500);
// Dispose when donetask.delay(5, () => { clearInterval(checkInterval); userProfile.dispose(); print("User profile disposed");});