Lifecycle and Cleanup
onMount and onCleanup — the two lifecycle hooks Solid needs, given that components only run once.
3 phút đọc
React's lifecycle is spread across mount, update, and unmount phases, often reasoned about through useEffect's dependency array and cleanup return. Solid needs far fewer concepts here, precisely because components run once: there's no repeated "update" phase to model, so lifecycle boils down to two moments — when something is first attached, and when it's torn down.
onMount: run once, after the DOM exists
import { onMount } from "solid-js";
function Chart(props) {
let canvasRef;
onMount(() => {
const ctx = canvasRef.getContext("2d");
drawChart(ctx, props.data);
});
return <canvas ref={canvasRef} />;
}onMount runs its callback once, after the component's elements have been created and attached to the DOM — which is exactly why it's the right place for work that needs a real DOM node to exist, like initializing a canvas, a third-party widget, or measuring an element's size. Note that onMount is really just createEffect without dependency tracking (it never re-runs), so in practice you'll reach for it specifically when you want "run once, after mount" and nothing more.
The ref pattern
let canvasRef; followed by ref={canvasRef} is Solid's version of React's useRef, but notably simpler: it's a plain local variable, assigned directly by Solid when the element is created — no .current, no special hook. Because the component function runs once, this variable is stable for the component's entire lifetime, so a plain let is all it takes.
onCleanup: run before removal
import { onCleanup } from "solid-js";
function LiveClock() {
const [time, setTime] = createSignal(new Date());
const id = setInterval(() => setTime(new Date()), 1000);
onCleanup(() => clearInterval(id));
return <p>{time().toLocaleTimeString()}</p>;
}onCleanup registers a function that runs when the reactive scope it's called in is disposed — for a component, that means when it's removed from the DOM (say, inside a <Show> branch that becomes false, or a <For> item that's removed from the array). This is the direct equivalent of returning a cleanup function from a React useEffect, but declared as its own explicit call rather than a return statement buried inside an effect.
Cleanup inside effects, not just components
onCleanup isn't limited to component-level lifecycle — it's just as useful inside a createEffect that re-runs repeatedly, cleaning up before each subsequent run:
createEffect(() => {
const controller = new AbortController();
fetch(`/api/user/${userId()}`, { signal: controller.signal })
.then((r) => r.json())
.then(setUser);
onCleanup(() => controller.abort());
});Every time userId() changes, the effect re-runs — but first, onCleanup fires, aborting whatever fetch was still in flight from the previous run. This avoids a classic race condition (an old, slow request resolving after a newer one and overwriting fresher data) with two lines of code, tied directly to the effect that started the work in the first place.
The takeaway
Solid's lifecycle model is small on purpose: onMount for "once, after the DOM is ready," onCleanup for "right before this goes away," and everything else — updates, re-computation — is already handled by signals, memos, and effects reacting to changes. There's no separate "on update" hook to learn, because there's no update phase distinct from the reactive system you already know.