11/* eslint-disable max-classes-per-file */ 
2- /* eslint-disable react/no-deprecated -- while we need to support React 16 */ 
2+ /* eslint-disable react/no-deprecated,@typescript-eslint/no-deprecated  -- while we need to support React 16 */ 
33
44import  *  as  ReactDOM  from  'react-dom' ; 
55import  type  {  ReactElement  }  from  'react' ; 
@@ -14,13 +14,13 @@ import { debugTurbolinks } from './turbolinksUtils';
1414
1515const  REACT_ON_RAILS_STORE_ATTRIBUTE  =  'data-js-react-on-rails-store' ; 
1616
17- function  delegateToRenderer ( 
17+ async   function  delegateToRenderer ( 
1818  componentObj : RegisteredComponent , 
19-   props : Record < string ,  string > , 
19+   props : Record < string ,  unknown > , 
2020  railsContext : RailsContext , 
2121  domNodeId : string , 
2222  trace : boolean , 
23- ) : boolean  { 
23+ ) : Promise < boolean >  { 
2424  const  {  name,  component,  isRenderer }  =  componentObj ; 
2525
2626  if  ( isRenderer )  { 
@@ -32,7 +32,7 @@ function delegateToRenderer(
3232      ) ; 
3333    } 
3434
35-     ( component  as  RenderFunction ) ( props ,  railsContext ,  domNodeId ) ; 
35+     await   ( component  as  RenderFunction ) ( props ,  railsContext ,  domNodeId ) ; 
3636    return  true ; 
3737  } 
3838
@@ -81,7 +81,7 @@ class ComponentRenderer {
8181    // This must match lib/react_on_rails/helper.rb 
8282    const  name  =  el . getAttribute ( 'data-component-name' )  ||  '' ; 
8383    const  {  domNodeId }  =  this ; 
84-     const  props  =  el . textContent  !==  null  ? JSON . parse ( el . textContent )  : { } ; 
84+     const  props  =  el . textContent  !==  null  ? ( JSON . parse ( el . textContent )   as   Record < string ,   unknown > )  : { } ; 
8585    const  trace  =  el . getAttribute ( 'data-trace' )  ===  'true' ; 
8686
8787    try  { 
@@ -92,7 +92,11 @@ class ComponentRenderer {
9292          return ; 
9393        } 
9494
95-         if  ( delegateToRenderer ( componentObj ,  props ,  railsContext ,  domNodeId ,  trace ) )  { 
95+         if  ( 
96+           ( await  delegateToRenderer ( componentObj ,  props ,  railsContext ,  domNodeId ,  trace ) )  || 
97+           // @ts -expect-error The state can change while awaiting delegateToRenderer 
98+           this . state  ===  'unmounted' 
99+         )  { 
96100          return ; 
97101        } 
98102
@@ -163,8 +167,8 @@ You should return a React.Component always for the client side entry point.`);
163167  } 
164168
165169  waitUntilRendered ( ) : Promise < void >  { 
166-     if  ( this . state  ===  'rendering' )  { 
167-       return  this . renderPromise ! ; 
170+     if  ( this . state  ===  'rendering'   &&   this . renderPromise )  { 
171+       return  this . renderPromise ; 
168172    } 
169173    return  Promise . resolve ( ) ; 
170174  } 
@@ -183,15 +187,18 @@ class StoreRenderer {
183187    } 
184188
185189    const  name  =  storeDataElement . getAttribute ( REACT_ON_RAILS_STORE_ATTRIBUTE )  ||  '' ; 
186-     const  props  =  storeDataElement . textContent  !==  null  ? JSON . parse ( storeDataElement . textContent )  : { } ; 
190+     const  props  = 
191+       storeDataElement . textContent  !==  null 
192+         ? ( JSON . parse ( storeDataElement . textContent )  as  Record < string ,  unknown > ) 
193+         : { } ; 
187194    this . hydratePromise  =  this . hydrate ( context ,  railsContext ,  name ,  props ) ; 
188195  } 
189196
190197  private  async  hydrate ( 
191198    context : Context , 
192199    railsContext : RailsContext , 
193200    name : string , 
194-     props : Record < string ,  string > , 
201+     props : Record < string ,  unknown > , 
195202  )  { 
196203    const  storeGenerator  =  await  context . ReactOnRails . getOrWaitForStoreGenerator ( name ) ; 
197204    if  ( this . state  ===  'unmounted' )  { 
@@ -204,8 +211,8 @@ class StoreRenderer {
204211  } 
205212
206213  waitUntilHydrated ( ) : Promise < void >  { 
207-     if  ( this . state  ===  'hydrating' )  { 
208-       return  this . hydratePromise ! ; 
214+     if  ( this . state  ===  'hydrating'   &&   this . hydratePromise )  { 
215+       return  this . hydratePromise ; 
209216    } 
210217    return  Promise . resolve ( ) ; 
211218  } 
@@ -217,26 +224,30 @@ class StoreRenderer {
217224
218225const  renderedRoots  =  new  Map < string ,  ComponentRenderer > ( ) ; 
219226
220- export  function  renderOrHydrateComponent ( domIdOrElement : string  |  Element ) :  ComponentRenderer   |   undefined  { 
227+ export  function  renderOrHydrateComponent ( domIdOrElement : string  |  Element )  { 
221228  const  domId  =  getDomId ( domIdOrElement ) ; 
222-   debugTurbolinks ( ` renderOrHydrateComponent  ${ domId } ` ) ; 
229+   debugTurbolinks ( ' renderOrHydrateComponent' ,   domId ) ; 
223230  let  root  =  renderedRoots . get ( domId ) ; 
224231  if  ( ! root )  { 
225232    root  =  new  ComponentRenderer ( domIdOrElement ) ; 
226233    renderedRoots . set ( domId ,  root ) ; 
227234  } 
228-   return  root ; 
235+   return  root . waitUntilRendered ( ) ; 
229236} 
230237
231- export  function  renderOrHydrateForceLoadedComponents ( ) : void { 
232-   const  els  =  document . querySelectorAll ( `.js-react-on-rails-component[data-force-load="true"]` ) ; 
233-   els . forEach ( ( el )  =>  renderOrHydrateComponent ( el ) ) ; 
238+ async  function  forAllElementsAsync ( 
239+   selector : string , 
240+   callback : ( el : Element )  =>  Promise < void > , 
241+ ) : Promise < void >  { 
242+   const  els  =  document . querySelectorAll ( selector ) ; 
243+   await  Promise . all ( Array . from ( els ) . map ( callback ) ) ; 
234244} 
235245
236- export  function  renderOrHydrateAllComponents ( ) : void { 
237-   const  els  =  document . querySelectorAll ( `.js-react-on-rails-component` ) ; 
238-   els . forEach ( ( el )  =>  renderOrHydrateComponent ( el ) ) ; 
239- } 
246+ export  const  renderOrHydrateForceLoadedComponents  =  ( )  => 
247+   forAllElementsAsync ( '.js-react-on-rails-component[data-force-load="true"]' ,  renderOrHydrateComponent ) ; 
248+ 
249+ export  const  renderOrHydrateAllComponents  =  ( )  => 
250+   forAllElementsAsync ( '.js-react-on-rails-component' ,  renderOrHydrateComponent ) ; 
240251
241252function  unmountAllComponents ( ) : void { 
242253  renderedRoots . forEach ( ( root )  =>  root . unmount ( ) ) ; 
@@ -267,15 +278,11 @@ export async function hydrateStore(storeNameOrElement: string | Element) {
267278  await  storeRenderer . waitUntilHydrated ( ) ; 
268279} 
269280
270- export  async  function  hydrateForceLoadedStores ( ) : Promise < void >  { 
271-   const  els  =  document . querySelectorAll ( `[${ REACT_ON_RAILS_STORE_ATTRIBUTE }  ) ; 
272-   await  Promise . all ( Array . from ( els ) . map ( ( el )  =>  hydrateStore ( el ) ) ) ; 
273- } 
281+ export  const  hydrateForceLoadedStores  =  ( )  => 
282+   forAllElementsAsync ( `[${ REACT_ON_RAILS_STORE_ATTRIBUTE }  ,  hydrateStore ) ; 
274283
275- export  async  function  hydrateAllStores ( ) : Promise < void >  { 
276-   const  els  =  document . querySelectorAll ( `[${ REACT_ON_RAILS_STORE_ATTRIBUTE }  ) ; 
277-   await  Promise . all ( Array . from ( els ) . map ( ( el )  =>  hydrateStore ( el ) ) ) ; 
278- } 
284+ export  const  hydrateAllStores  =  ( )  => 
285+   forAllElementsAsync ( `[${ REACT_ON_RAILS_STORE_ATTRIBUTE }  ,  hydrateStore ) ; 
279286
280287function  unmountAllStores ( ) : void { 
281288  storeRenderers . forEach ( ( storeRenderer )  =>  storeRenderer . unmount ( ) ) ; 
0 commit comments