Describe the bug
In dev builds, STRICT_READ_UNTRACKED does not fire in two common cases, so the same component warns or stays silent depending on how the page was reached.
- Initial hydration. In
read() (packages/signals/src/core/core.ts), the snapshotCaptureActive && c._config & CONFIG_IN_SNAPSHOT_SCOPE branch returns the snapshot value before the if (__DEV__ && strictRead) warnStrictReadUntracked(...) check. Component bodies that run during the first hydration pass never warn.
lazy() components. lazy() (packages/solid/src/client/component.ts) renders the loaded component with untrack(() => Comp(props)) and no label. createComponent goes through observedComponent, which calls untrack(fn, "<Name>") and turns the check on. Reads in a lazy component's own body are never checked. File routes from @solidjs/router/fs are lazy(), so route components are affected. With solid-refresh enabled, client navigation still warns because the refresh proxy adds its own label, so this case shows up with hot: false or wherever refresh does not wrap the component.
The warning text also names only the scope (<Child>), not which value was read or where. Unnamed internal signals report as "signal" in the structured event.
Your Example Website or App
Minimal SSR app below (solid-js 2.0.0-rc.9, @solidjs/web 2.0.0-rc.9, @solidjs/router 2.0.0-next.30, @solidjs/vite-plugin 3.0.0-next.44 with ssr: true, start: { node: true }).
Steps to Reproduce the Bug or Issue
// App.tsx
import { createRouter } from "@solidjs/router";
import { createSignal, lazy } from "solid-js";
function Child(props: { count: () => number }) {
const n = props.count();
return <p>child body read {n}</p>;
}
function ChildPage() {
const [count] = createSignal(1, { name: "count" });
return <Child count={count} />;
}
const Router = createRouter({
routes: [
{ path: "/", component: () => <p>home</p> },
{ path: "/child", component: ChildPage },
{ path: "/lazy", component: lazy(() => import("./LazyRoute")) },
],
});
export default function App() {
return (
<Router>
{props => (
<>
<nav><a href="/">home</a> <a href="/child">child</a> <a href="/lazy">lazy</a></nav>
{props.children}
</>
)}
</Router>
);
}
// LazyRoute.tsx
import { createSignal } from "solid-js";
export default function LazyRoute() {
const [count] = createSignal(2, { name: "routeCount" });
const n = count();
return <p>lazy route body read {n}</p>;
}
- Run the dev server and open
/child directly (fresh SSR load). No warning.
- Open
/, then click the child link. STRICT_READ_UNTRACKED ... in <Child> is logged.
- Same for
/lazy: a fresh load is silent. With solid({ hot: false }), client navigation to /lazy is silent as well.
Console results in Chromium (Playwright), stock rc.9:
| Case |
Warnings |
fresh load /child |
0 |
fresh load /lazy |
0 |
client nav to /child |
1 |
client nav to /lazy (solid-refresh on) |
1 |
client nav to /lazy (hot: false) |
0 |
Expected behavior
As a user, I expected a component body that reads a signal directly to warn every time it runs in dev, whether it runs during hydration, after client navigation, or inside a lazy() component. Right now it only warns after client navigation or an HMR update, which is also when developers are least likely to be looking at a fresh console.
It would also help if the console line said which value was read and where, for example read of "count" at Child (App.tsx:5:13).
Platform
- OS: macOS
- Browser: Chromium (Playwright 1.62)
- Version: solid-js 2.0.0-rc.9, @solidjs/signals 2.0.0-rc.9
Describe the bug
In dev builds,
STRICT_READ_UNTRACKEDdoes not fire in two common cases, so the same component warns or stays silent depending on how the page was reached.read()(packages/signals/src/core/core.ts), thesnapshotCaptureActive && c._config & CONFIG_IN_SNAPSHOT_SCOPEbranch returns the snapshot value before theif (__DEV__ && strictRead) warnStrictReadUntracked(...)check. Component bodies that run during the first hydration pass never warn.lazy()components.lazy()(packages/solid/src/client/component.ts) renders the loaded component withuntrack(() => Comp(props))and no label.createComponentgoes throughobservedComponent, which callsuntrack(fn, "<Name>")and turns the check on. Reads in a lazy component's own body are never checked. File routes from@solidjs/router/fsarelazy(), so route components are affected. With solid-refresh enabled, client navigation still warns because the refresh proxy adds its own label, so this case shows up withhot: falseor wherever refresh does not wrap the component.The warning text also names only the scope (
<Child>), not which value was read or where. Unnamed internal signals report as"signal"in the structured event.Your Example Website or App
Minimal SSR app below (solid-js 2.0.0-rc.9, @solidjs/web 2.0.0-rc.9, @solidjs/router 2.0.0-next.30, @solidjs/vite-plugin 3.0.0-next.44 with
ssr: true, start: { node: true }).Steps to Reproduce the Bug or Issue
/childdirectly (fresh SSR load). No warning./, then click thechildlink.STRICT_READ_UNTRACKED ... in <Child>is logged./lazy: a fresh load is silent. Withsolid({ hot: false }), client navigation to/lazyis silent as well.Console results in Chromium (Playwright), stock rc.9:
/child/lazy/child/lazy(solid-refresh on)/lazy(hot: false)Expected behavior
As a user, I expected a component body that reads a signal directly to warn every time it runs in dev, whether it runs during hydration, after client navigation, or inside a
lazy()component. Right now it only warns after client navigation or an HMR update, which is also when developers are least likely to be looking at a fresh console.It would also help if the console line said which value was read and where, for example
read of "count" at Child (App.tsx:5:13).Platform