Summary
Following on from #21 — that issue reported four missing Offscreen methods, but the underlying problem is broader: hostConfig is missing several methods that react-reconciler@0.31 calls unconditionally. Each one surfaces as a TypeError: <name> is not a function at a different point in the app lifecycle, so they get found one crash at a time.
The one that bit me next is shouldAttemptEagerTransition, which crashes on the first transition-driven update — i.e. the first navigation in any router-based app.
Reproduction
npm install @orchetron/storm@0.2.0 react@19
// repro.tsx
import { render, Box, Text } from "@orchetron/storm";
import React, { startTransition, useState } from "react";
function App() {
const [n, setN] = useState(0);
React.useEffect(() => {
setTimeout(() => startTransition(() => setN(1)), 100);
}, []);
return <Box><Text>screen {n}</Text></Box>;
}
render(<App />);
Uncaught exception: TypeError: shouldAttemptEagerTransition is not a function
at processRootScheduleInMicrotask (…/react-reconciler.development.js:2027:10)
at Immediate.performWorkUntilDeadline (…/scheduler.development.js:44:48)
at process.processImmediate (node:internal/timers:504:21)
processRootScheduleInMicrotask calls it whenever currentEventTransitionLane !== 0:
0 !== currentEventTransitionLane &&
(shouldAttemptEagerTransition() &&
(syncTransitionLanes = currentEventTransitionLane),
(currentEventTransitionLane = 0));
So it's every startTransition, and every navigation in React Router / any router built on transitions.
Full audit
Rather than keep finding these one at a time, I diffed every $$$config.* the reconciler references against hostConfig, then excluded the groups gated behind flags storm has off (supportsHydration, supportsPersistence, supportsResources, supportsSingletons, supportsTestSelectors, supportsMicrotasks). What's left:
| method |
status |
notes |
shouldAttemptEagerTransition |
crashes |
every transition / navigation |
hideInstance, unhideInstance, hideTextInstance, unhideTextInstance |
crashes |
every <Suspense> resolve — #21 |
commitMount |
latent |
only reached if finalizeInitialChildren() returns true; currently always false |
resetTextContent |
latent |
only reached via the ContentReset flag, which needs shouldSetTextContent() to return true; currently always false |
HostTransitionContext |
latent |
read and assigned through in popHostContext for transition-aware host components; storm never reports one |
resolveEventType, resolveEventTimeStamp |
unreachable |
referenced as bare statements, never invoked in 0.31 |
bindToConsole |
unreachable |
only on the RSC error.environmentName path |
extraDevToolsConfig, rendererVersion, rendererPackageName, warnsIfNotActing |
unreachable |
devtools metadata, read as values |
The two "crashes" rows are the ones that matter. The "latent" rows are safe only because of current return values elsewhere in the config — if finalizeInitialChildren or shouldSetTextContent ever changes, they become live crashes, so they're worth stubbing regardless.
Suggested fix
// Flags — explicit rather than undefined, so the absent method groups are documented
supportsResources: false,
supportsSingletons: false,
supportsTestSelectors: false,
supportsMicrotasks: false,
warnsIfNotActing: false,
// react-dom returns true only to avoid tearing on native form controls;
// a TUI has no such case, so transitions stay concurrent.
shouldAttemptEagerTransition() {
return false;
},
commitMount() {},
resetTextContent() {},
HostTransitionContext: {
$$typeof: Symbol.for("react.context"),
Provider: null,
Consumer: null,
_currentValue: null,
_currentValue2: null,
_threadCount: 0,
},
rendererPackageName: "@orchetron/storm",
rendererVersion: "0.2.0",
I'm reasonably confident about shouldAttemptEagerTransition returning false — that's the non-eager path, which is the normal concurrent behaviour. Less confident about the HostTransitionContext placeholder: it's unreachable today, so I can't test it, and if you ever do add form/transition-aware host components it'll need to be a real context object rather than this shape. You may prefer to leave it out entirely rather than ship something untested.
Verified against dist/ via pnpm patch in a real app (React Router + Apollo, several routed pages): navigation, suspense fallbacks and back-navigation all work. I haven't run your test suite.
Environment
@orchetron/storm 0.2.0 (latest on npm)
react 19.2.8, react-reconciler 0.31.0
- Node 24.15.0, Linux
Summary
Following on from #21 — that issue reported four missing Offscreen methods, but the underlying problem is broader:
hostConfigis missing several methods thatreact-reconciler@0.31calls unconditionally. Each one surfaces as aTypeError: <name> is not a functionat a different point in the app lifecycle, so they get found one crash at a time.The one that bit me next is
shouldAttemptEagerTransition, which crashes on the first transition-driven update — i.e. the first navigation in any router-based app.Reproduction
processRootScheduleInMicrotaskcalls it whenevercurrentEventTransitionLane !== 0:So it's every
startTransition, and every navigation in React Router / any router built on transitions.Full audit
Rather than keep finding these one at a time, I diffed every
$$$config.*the reconciler references againsthostConfig, then excluded the groups gated behind flags storm has off (supportsHydration,supportsPersistence,supportsResources,supportsSingletons,supportsTestSelectors,supportsMicrotasks). What's left:shouldAttemptEagerTransitionhideInstance,unhideInstance,hideTextInstance,unhideTextInstance<Suspense>resolve — #21commitMountfinalizeInitialChildren()returns true; currently always falseresetTextContentshouldSetTextContent()to return true; currently always falseHostTransitionContextpopHostContextfor transition-aware host components; storm never reports oneresolveEventType,resolveEventTimeStampbindToConsoleerror.environmentNamepathextraDevToolsConfig,rendererVersion,rendererPackageName,warnsIfNotActingThe two "crashes" rows are the ones that matter. The "latent" rows are safe only because of current return values elsewhere in the config — if
finalizeInitialChildrenorshouldSetTextContentever changes, they become live crashes, so they're worth stubbing regardless.Suggested fix
I'm reasonably confident about
shouldAttemptEagerTransitionreturningfalse— that's the non-eager path, which is the normal concurrent behaviour. Less confident about theHostTransitionContextplaceholder: it's unreachable today, so I can't test it, and if you ever do add form/transition-aware host components it'll need to be a real context object rather than this shape. You may prefer to leave it out entirely rather than ship something untested.Verified against
dist/viapnpm patchin a real app (React Router + Apollo, several routed pages): navigation, suspense fallbacks and back-navigation all work. I haven't run your test suite.Environment
@orchetron/storm0.2.0 (latest on npm)react19.2.8,react-reconciler0.31.0