Components Only Run Once
Why a Solid component function body executes a single time, and what that means for how you write component code.
阅读需 2 分钟
This is the single most important mental shift when moving from React to Solid: a Solid component function runs exactly once, when it's first created. It does not re-run on state changes, prop changes, or anything else. Everything you're used to calling a "re-render" in React simply doesn't happen in Solid — updates happen at the level of individual DOM bindings, not the component function.
Seeing it directly
function Counter() {
console.log("Component body running");
const [count, setCount] = createSignal(0);
return (
<button onClick={() => setCount(count() + 1)}>
{count()}
</button>
);
}Click that button ten times, and "Component body running" logs exactly once — from the initial call. In React, the equivalent component would log on every click, because the function body itself is the render. In Solid, the function body is setup — it runs once to create signals, wire up JSX, and establish reactive bindings. After that, only the specific bindings (here, the text node showing count()) update.
Why this changes how you write code
Because the function body doesn't re-run, code inside it that isn't wrapped in a signal read, memo, or effect only ever sees the initial values:
function Greeting(props) {
// Runs once — captures whatever props.name is at creation time
const upperName = props.name.toUpperCase();
return <h1>Hello, {upperName}</h1>;
}If props.name changes later, upperName never updates — it was computed once, during the single execution of Greeting. This is the exact same trap as the "pre-computed string" mistake from the JSX lesson, and it's the reason the next lesson exists: props need to be read reactively (as props.name inside JSX, or inside a memo/effect), never unwrapped into a plain variable during setup.
Local variables are fine — for things that don't need to react
Not everything needs to be reactive. Values that are genuinely fixed for the component's lifetime are completely safe as plain variables:
function ProductCard(props) {
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
});
return <p>{props.name}: {formatter.format(props.price)}</p>;
}formatter is created once and never needs to change — that's exactly what "runs once" is good for. The rule isn't "avoid plain variables," it's "know which of your values need to track changes, and make sure only those go through a signal, memo, or reactive prop access."
No re-render also means no wasted work
The upside of this model is that you don't pay for re-renders you didn't ask for. There's no need for React.memo, useMemo, or useCallback purely to prevent unnecessary re-execution — there's no re-execution to prevent. The cost of "runs once" is that you have to be deliberate about what stays reactive; the benefit is that everything that isn't reactive costs nothing at all, forever.