You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
compilePackages: class extending React.PureComponent doesn't inherit isReactComponent on its prototype (2-level chain) → react-reconciler treats error boundaries as function components → children dropped (blocks ink) #5024
A class that extends React's PureComponent (i.e. two prototype levels up from React.Component) does not inherit the isReactComponent marker on its .prototype. react-reconciler's shouldConstruct(type) reads type.prototype.isReactComponent to decide ClassComponent (fiber tag 1) vs FunctionComponent (tag 0); when it reads undefined, the class is misclassified as a function component, its render() is never invoked as a class, and its children are dropped — the component renders nothing.
This is the wall after the Context.Provider classification fix (commit 81cfc6981, the labeled-break-from-nested-switch fix): ink's <ErrorBoundary> (class ErrorBoundary extends PureComponent) wraps the app's children, so the whole ink tree renders empty.
Node prints object for all four isReactComponent reads.
The anomaly
React.Component.prototype.isReactComponent reads undefined directly, yet class X extends React.Componentdoes observe it (object) via the chain. So the value is reachable one level up but:
a direct read on Component.prototype misses it, and
a two-level chain walk (Y → PureComponent → Component) misses it,
while a one-level walk (X → Component) finds it. The prototype links are all correct (getPrototypeOf checks pass). This points at a property-storage / prototype-chain-GET inconsistency specific to react's compiled component prototypes (PureComponent.prototype = Object.create(Component.prototype) shape), not generic inheritance — a plain user-land class Sub extends Base with Base.prototype.marker set does inherit correctly through two levels.
ink's App wraps children in <ErrorBoundary> (extends PureComponent). react-reconciler's shouldConstruct(ErrorBoundary) → ErrorBoundary.prototype.isReactComponent → undefined → shouldConstruct returns false → createFiberFromTypeAndProps leaves fiberTag = 0 (FunctionComponent) → ErrorBoundary is invoked as a function, its class render() never runs, this.props.children is never returned → the entire app subtree is dropped. Verified: return children from App renders; <ErrorBoundary>{children}</ErrorBoundary> renders nothing.
Impact
Every class component that extends PureComponent (and likely any ≥2-level React component subclass) — error boundaries, many older libraries. They silently render as no-op function components.
Summary
A class that extends React's
PureComponent(i.e. two prototype levels up fromReact.Component) does not inherit theisReactComponentmarker on its.prototype. react-reconciler'sshouldConstruct(type)readstype.prototype.isReactComponentto decide ClassComponent (fiber tag 1) vs FunctionComponent (tag 0); when it readsundefined, the class is misclassified as a function component, itsrender()is never invoked as a class, and its children are dropped — the component renders nothing.This is the wall after the Context.Provider classification fix (commit 81cfc6981, the labeled-break-from-nested-switch fix): ink's
<ErrorBoundary>(class ErrorBoundary extends PureComponent) wraps the app's children, so the whole ink tree renders empty.Minimal repro
Node prints
objectfor all fourisReactComponentreads.The anomaly
React.Component.prototype.isReactComponentreadsundefineddirectly, yetclass X extends React.Componentdoes observe it (object) via the chain. So the value is reachable one level up but:Component.prototypemisses it, andY → PureComponent → Component) misses it,while a one-level walk (
X → Component) finds it. The prototype links are all correct (getPrototypeOfchecks pass). This points at a property-storage / prototype-chain-GET inconsistency specific to react's compiled component prototypes (PureComponent.prototype = Object.create(Component.prototype)shape), not generic inheritance — a plain user-landclass Sub extends BasewithBase.prototype.markerset does inherit correctly through two levels.How it surfaced (ink, #348)
ink's
Appwraps children in<ErrorBoundary>(extends PureComponent). react-reconciler'sshouldConstruct(ErrorBoundary)→ErrorBoundary.prototype.isReactComponent→undefined→shouldConstructreturns false →createFiberFromTypeAndPropsleavesfiberTag = 0(FunctionComponent) → ErrorBoundary is invoked as a function, its classrender()never runs,this.props.childrenis never returned → the entire app subtree is dropped. Verified:return childrenfrom App renders;<ErrorBoundary>{children}</ErrorBoundary>renders nothing.Impact
PureComponent(and likely any ≥2-level React component subclass) — error boundaries, many older libraries. They silently render as no-op function components.ink(React-based TUI framework) end-to-end viaperry.compilePackages#348): the wall after Context.Provider; ink lays out + renders natively otherwise (native taffy yoga + provider fix both working — text/boxes/colors render through bare/function/Fragment/Provider wrappers).Related
ink(React-based TUI framework) end-to-end viaperry.compilePackages#348 (ink end-to-end)module.exports = classloses the class method table — static AND prototype methods readundefined(breaks stack-utils → ink) #4933 / fix(cjs_wrap): flat-emit module.exports = <Class> that closes over a top-level binding (#4933) #4947 (compiled-package class method-table loss — adjacent class/prototype family)