1010import  *  as  React  from  'react' ; 
1111import  { 
1212  createContext , 
13+   unstable_getCacheForType  as  getCacheForType , 
14+   unstable_startTransition  as  startTransition , 
15+   unstable_useCacheRefresh  as  useCacheRefresh , 
1316  useCallback , 
1417  useContext , 
1518  useEffect , 
@@ -18,7 +21,6 @@ import {
1821  useState , 
1922}  from  'react' ; 
2023import  { unstable_batchedUpdates  as  batchedUpdates }  from  'react-dom' ; 
21- import  { createResource }  from  '../../cache' ; 
2224import  { BridgeContext ,  StoreContext }  from  '../context' ; 
2325import  { hydrate ,  fillInPath }  from  'react-devtools-shared/src/hydration' ; 
2426import  { TreeStateContext }  from  './TreeContext' ; 
@@ -33,7 +35,6 @@ import type {
3335  Element , 
3436  InspectedElement  as  InspectedElementFrontend , 
3537}  from  'react-devtools-shared/src/devtools/views/Components/types' ; 
36- import  type  { Resource ,  Thenable }  from  '../../cache' ; 
3738
3839export  type  StoreAsGlobal  =  ( id : number ,  path : Array < string  |  number > )  =>  void ; 
3940
@@ -51,13 +52,15 @@ export type GetInspectedElement = (
5152  id : number , 
5253)  =>  InspectedElementFrontend  |  null ; 
5354
54- type   RefreshInspectedElement  =  ( )  =>  void ; 
55+ type   ClearErrorsForInspectedElement  =  ( )  =>  void ; 
56+ type  ClearWarningsForInspectedElement  =  ( )  =>  void ; 
5557
5658export  type  InspectedElementContextType  =  { | 
59+   clearErrorsForInspectedElement : ClearErrorsForInspectedElement , 
60+   clearWarningsForInspectedElement : ClearWarningsForInspectedElement , 
5761  copyInspectedElementPath : CopyInspectedElementPath , 
5862  getInspectedElementPath : GetInspectedElementPath , 
5963  getInspectedElement : GetInspectedElement , 
60-   refreshInspectedElement : RefreshInspectedElement , 
6164  storeAsGlobal : StoreAsGlobal , 
6265| } ; 
6366
@@ -67,35 +70,21 @@ const InspectedElementContext = createContext<InspectedElementContextType>(
6770InspectedElementContext . displayName  =  'InspectedElementContext' ; 
6871
6972type  ResolveFn  =  ( inspectedElement : InspectedElementFrontend )  =>  void ; 
70- type  InProgressRequest  =  { | 
71-   promise : Thenable < InspectedElementFrontend > , 
72-   resolveFn : ResolveFn , 
73+ type  Callback  =  ( inspectedElement : InspectedElementFrontend )  =>  void ; 
74+ type  Thenable  =  { | 
75+   callbacks : Set < Callback > , 
76+   then : ( callback : Callback )  =>  void , 
77+   resolve : ResolveFn , 
7378| } ; 
7479
75- const  inProgressRequests : WeakMap < Element ,  InProgressRequest >  =  new  WeakMap ( ) ; 
76- const   resource :  Resource < 
77-    Element , 
80+ const  inspectedElementThenables : WeakMap < Element ,  Thenable >  =  new  WeakMap ( ) ; 
81+ 
82+ function   createInspectedElementCache ( ) :  WeakMap < 
7883  Element , 
7984  InspectedElementFrontend , 
80- >  =  createResource ( 
81-   ( element : Element )  =>  { 
82-     const  request  =  inProgressRequests . get ( element ) ; 
83-     if  ( request  !=  null )  { 
84-       return  request . promise ; 
85-     } 
86- 
87-     let  resolveFn  =  ( ( null : any ) : ResolveFn ) ; 
88-     const  promise  =  new  Promise ( resolve  =>  { 
89-       resolveFn  =  resolve ; 
90-     } ) ; 
91- 
92-     inProgressRequests . set ( element ,  { promise,  resolveFn} ) ; 
93- 
94-     return  promise ; 
95-   } , 
96-   ( element : Element )  =>  element , 
97-   { useWeakMap : true } , 
98- ) ; 
85+ >  { 
86+   return  new  WeakMap ( ) ; 
87+ } 
9988
10089type  Props  =  { | 
10190  children : React$Node , 
@@ -145,28 +134,75 @@ function InspectedElementContextController({children}: Props) {
145134    [ bridge ,  store ] , 
146135  ) ; 
147136
137+   // TODO (cache) Better encapsulate the cache and read/write methods. 
138+   const  inspectedElementCache  =  getCacheForType ( createInspectedElementCache ) ; 
139+ 
148140  const  getInspectedElement  =  useCallback < GetInspectedElement > ( 
149141    ( id : number )  =>  { 
150142      const  element  =  store . getElementByID ( id ) ; 
151143      if  ( element  !==  null )  { 
152-         return  resource . read ( element ) ; 
153-       }  else  { 
154-         return  null ; 
144+         const  maybeInspectedElement  =  inspectedElementCache . get ( element ) ; 
145+         if  ( maybeInspectedElement  !==  undefined )  { 
146+           return  maybeInspectedElement ; 
147+         }  else  { 
148+           const  maybeThenable  =  inspectedElementThenables . get ( element ) ; 
149+           if  ( maybeThenable  !=  null )  { 
150+             throw  maybeThenable ; 
151+           } 
152+ 
153+           const  thenable : Thenable  =  { 
154+             callbacks : new  Set ( ) , 
155+             then : callback  =>  { 
156+               thenable . callbacks . add ( callback ) ; 
157+             } , 
158+             resolve : inspectedElement  =>  { 
159+               thenable . callbacks . forEach ( callback  => 
160+                 callback ( inspectedElement ) , 
161+               ) ; 
162+             } , 
163+           } ; 
164+ 
165+           inspectedElementThenables . set ( element ,  thenable ) ; 
166+ 
167+           throw  thenable ; 
168+         } 
155169      } 
170+       return  null ; 
156171    } , 
157-     [ store ] , 
172+     [ inspectedElementCache ,   store ] , 
158173  ) ; 
159174
160175  // It's very important that this context consumes selectedElementID and not inspectedElementID. 
161176  // Otherwise the effect that sends the "inspect" message across the bridge- 
162177  // would itself be blocked by the same render that suspends (waiting for the data). 
163178  const  { selectedElementID}  =  useContext ( TreeStateContext ) ; 
164179
165-   const  refreshInspectedElement  =  useCallback < RefreshInspectedElement > ( ( )  =>  { 
180+   const  refresh  =  useCacheRefresh ( ) ; 
181+ 
182+   const  clearErrorsForInspectedElement  =  useCallback < ClearErrorsForInspectedElement > ( ( )  =>  { 
166183    if  ( selectedElementID  !==  null )  { 
167184      const  rendererID  =  store . getRendererIDForElement ( selectedElementID ) ; 
168185      if  ( rendererID  !==  null )  { 
169186        bridge . send ( 'inspectElement' ,  { id : selectedElementID ,  rendererID} ) ; 
187+ 
188+         startTransition ( ( )  =>  { 
189+           store . clearErrorsForElement ( selectedElementID ) ; 
190+           refresh ( ) ; 
191+         } ) ; 
192+       } 
193+     } 
194+   } ,  [ bridge ,  selectedElementID ] ) ; 
195+ 
196+   const  clearWarningsForInspectedElement  =  useCallback < ClearWarningsForInspectedElement > ( ( )  =>  { 
197+     if  ( selectedElementID  !==  null )  { 
198+       const  rendererID  =  store . getRendererIDForElement ( selectedElementID ) ; 
199+       if  ( rendererID  !==  null )  { 
200+         bridge . send ( 'inspectElement' ,  { id : selectedElementID ,  rendererID} ) ; 
201+ 
202+         startTransition ( ( )  =>  { 
203+           store . clearWarningsForElement ( selectedElementID ) ; 
204+           refresh ( ) ; 
205+         } ) ; 
170206      } 
171207    } 
172208  } ,  [ bridge ,  selectedElementID ] ) ; 
@@ -198,7 +234,9 @@ function InspectedElementContextController({children}: Props) {
198234
199235              fillInPath ( inspectedElement ,  data . value ,  data . path ,  value ) ; 
200236
201-               resource . write ( element ,  inspectedElement ) ; 
237+               // TODO (cache) This mutation seems sketchy. 
238+               // Probably need to refresh the cache with a new seed. 
239+               inspectedElementCache . set ( element ,  inspectedElement ) ; 
202240
203241              // Schedule update with React if the currently-selected element has been invalidated. 
204242              if  ( id  ===  selectedElementID )  { 
@@ -277,16 +315,17 @@ function InspectedElementContextController({children}: Props) {
277315
278316          element  =  store . getElementByID ( id ) ; 
279317          if  ( element  !==  null )  { 
280-             const  request  =  inProgressRequests . get ( element ) ; 
281-             if  ( request  !=  null )  { 
282-               inProgressRequests . delete ( element ) ; 
318+             inspectedElementCache . set ( element ,  inspectedElement ) ; 
319+ 
320+             const  thenable  =  inspectedElementThenables . get ( element ) ; 
321+             if  ( thenable  !=  null )  { 
322+               inspectedElementThenables . delete ( element ) ; 
323+ 
283324              batchedUpdates ( ( )  =>  { 
284-                 request . resolveFn ( inspectedElement ) ; 
325+                 thenable . resolve ( inspectedElement ) ; 
285326                setCurrentlyInspectedElement ( inspectedElement ) ; 
286327              } ) ; 
287328            }  else  { 
288-               resource . write ( element ,  inspectedElement ) ; 
289- 
290329              // Schedule update with React if the currently-selected element has been invalidated. 
291330              if  ( id  ===  selectedElementID )  { 
292331                setCurrentlyInspectedElement ( inspectedElement ) ; 
@@ -356,19 +395,21 @@ function InspectedElementContextController({children}: Props) {
356395
357396  const  value  =  useMemo ( 
358397    ( )  =>  ( { 
398+       clearErrorsForInspectedElement, 
399+       clearWarningsForInspectedElement, 
359400      copyInspectedElementPath, 
360401      getInspectedElement, 
361402      getInspectedElementPath, 
362-       refreshInspectedElement, 
363403      storeAsGlobal, 
364404    } ) , 
365405    // InspectedElement is used to invalidate the cache and schedule an update with React. 
366406    [ 
407+       clearErrorsForInspectedElement , 
408+       clearWarningsForInspectedElement , 
367409      copyInspectedElementPath , 
368410      currentlyInspectedElement , 
369411      getInspectedElement , 
370412      getInspectedElementPath , 
371-       refreshInspectedElement , 
372413      storeAsGlobal , 
373414    ] , 
374415  ) ; 
0 commit comments