@@ -97,6 +97,18 @@ function teardownEntry(entry: RenderedEntry, domNodeId: string): void {
9797 }
9898}
9999
100+ function teardownErrorLabel ( entry : RenderedEntry , domNodeId : string ) : string {
101+ if ( entry . kind === 'renderer' ) {
102+ return `Error in renderer teardown for dom node "${ domNodeId } ":` ;
103+ }
104+
105+ if ( entry . kind === 'scheduled' ) {
106+ return `Error canceling scheduled render for dom node "${ domNodeId } ":` ;
107+ }
108+
109+ return `Error unmounting component for dom node "${ domNodeId } ":` ;
110+ }
111+
100112function initializeStore ( el : Element , railsContext : RailsContext ) : void {
101113 const name = el . getAttribute ( REACT_ON_RAILS_STORE_ATTRIBUTE ) || '' ;
102114 const props = el . textContent !== null ? ( JSON . parse ( el . textContent ) as Record < string , unknown > ) : { } ;
@@ -130,6 +142,9 @@ function hydrateOnForEl(el: Element): HydrateOnMode {
130142
131143function scheduleWhenVisible ( domNode : Element , callback : ( ) => void ) : ( ) => void {
132144 if ( typeof IntersectionObserver === 'undefined' ) {
145+ console . warn (
146+ '[react-on-rails] IntersectionObserver is not available. Falling back to immediate hydration for hydrate_on: :visible.' ,
147+ ) ;
133148 const timeoutId = window . setTimeout ( callback , 0 ) ;
134149 return ( ) => window . clearTimeout ( timeoutId ) ;
135150 }
@@ -166,7 +181,7 @@ function scheduleWhenIdle(callback: () => void): () => void {
166181 } ;
167182 }
168183
169- const timeoutId = window . setTimeout ( callback , 1 ) ;
184+ const timeoutId = window . setTimeout ( callback , 50 ) ;
170185 return ( ) => window . clearTimeout ( timeoutId ) ;
171186}
172187
@@ -183,11 +198,19 @@ function scheduleHydration(hydrateOn: HydrateOnMode, domNode: Element, callback:
183198 return ( ) => { } ;
184199}
185200
186- function raiseRenderError ( componentName : string , error : unknown ) : never {
201+ function prepareRenderError ( componentName : string , error : unknown ) : Error {
187202 const renderError = error as Error ;
188203 console . error ( renderError . message ) ;
189204 renderError . message = `ReactOnRails encountered an error while rendering component: ${ componentName } . See above error message.` ;
190- throw renderError ;
205+ return renderError ;
206+ }
207+
208+ function raiseRenderError ( componentName : string , error : unknown ) : never {
209+ throw prepareRenderError ( componentName , error ) ;
210+ }
211+
212+ function reportRenderError ( componentName : string , error : unknown ) : void {
213+ console . error ( prepareRenderError ( componentName , error ) . message ) ;
191214}
192215
193216// Result of attempting renderer delegation. Core carries the raw RendererResult (which may still be
@@ -339,11 +362,7 @@ function renderElement(el: Element, railsContext: RailsContext): void {
339362 // Surface the failure unconditionally (matching unmountAllComponents) so a teardown/unmount
340363 // error on node replacement is as visible as one on page unload, using the same greppable
341364 // labels. We still continue: the old mount may leak, but the new node must be rendered.
342- const label =
343- existing . kind === 'renderer'
344- ? `Error in renderer teardown for dom node "${ domNodeId } ":`
345- : `Error unmounting component for dom node "${ domNodeId } ":` ;
346- console . error ( label , unmountError ) ;
365+ console . error ( teardownErrorLabel ( existing , domNodeId ) , unmountError ) ;
347366 }
348367 renderedRoots . delete ( domNodeId ) ;
349368 }
@@ -411,7 +430,7 @@ You should return a React.Component always for the client side entry point.`);
411430 mountReactRoot ( ) ;
412431 } catch ( scheduledError ) {
413432 renderedRoots . delete ( domNodeId ) ;
414- raiseRenderError ( name , scheduledError ) ;
433+ reportRenderError ( name , scheduledError ) ;
415434 }
416435 } ;
417436
@@ -486,15 +505,7 @@ function unmountAllComponents(): void {
486505 try {
487506 teardownEntry ( entry , domNodeId ) ;
488507 } catch ( error ) {
489- // Use the same label as the async-rejection path so renderer-teardown failures are greppable
490- // whether the teardown threw synchronously (here) or rejected (invokeRendererTeardown).
491- let label = `Error unmounting component for dom node "${ domNodeId } ":` ;
492- if ( entry . kind === 'renderer' ) {
493- label = `Error in renderer teardown for dom node "${ domNodeId } ":` ;
494- } else if ( entry . kind === 'scheduled' ) {
495- label = `Error canceling scheduled render for dom node "${ domNodeId } ":` ;
496- }
497- console . error ( label , error ) ;
508+ console . error ( teardownErrorLabel ( entry , domNodeId ) , error ) ;
498509 }
499510 } ) ;
500511 renderedRoots . clear ( ) ;
0 commit comments