Skip to content

hostConfig missing shouldAttemptEagerTransition — crashes on every transition/navigation (plus full audit vs reconciler 0.31) #23

Description

@joemckie

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions