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