77 * @flow
88 */
99import type { ReactContext } from 'shared/ReactTypes' ;
10- import type { SuspenseNode } from '../../../frontend/types' ;
1110import type Store from '../../store' ;
1211
1312import * as React from 'react' ;
@@ -21,14 +20,10 @@ import {
2120} from 'react' ;
2221import { StoreContext } from '../context' ;
2322
24- export type SuspenseTreeState = {
25- shells : $ReadOnlyArray < SuspenseNode [ 'id' ] > ,
26- } ;
23+ export type SuspenseTreeState = { } ;
2724
28- type ACTION_HANDLE_SUSPENSE_TREE_MUTATION = {
29- type : 'HANDLE_SUSPENSE_TREE_MUTATION' ,
30- } ;
31- export type SuspenseTreeAction = ACTION_HANDLE_SUSPENSE_TREE_MUTATION ;
25+ // unused for now
26+ export type SuspenseTreeAction = { type : 'unused' } ;
3227export type SuspenseTreeDispatch = ( action : SuspenseTreeAction ) => void ;
3328
3429const SuspenseTreeStateContext : ReactContext < SuspenseTreeState > =
@@ -43,11 +38,39 @@ type Props = {
4338 children : React$Node ,
4439} ;
4540
46- function SuspenseTreeContextController({ children } : Props): React.Node {
41+ /**
42+ * The Store is mutable. This Hook ensures renders read the latest Suspense related
43+ * data.
44+ */
45+ function useSuspenseStore(): Store {
4746 const store = useContext ( StoreContext ) ;
48-
47+ const [ , storeUpdated ] = useReducer < number , number , void > (
48+ ( x : number ) => ( x + 1 ) % Number . MAX_SAFE_INTEGER ,
49+ 0 ,
50+ ) ;
4951 const initialRevision = useMemo ( ( ) => store . revisionSuspense , [ store ] ) ;
52+ // We're currently storing everything Suspense related in the same Store as
53+ // Components. However, most reads are currently stateless. This ensures
54+ // the latest state is always read from the Store.
55+ useEffect ( ( ) => {
56+ const handleSuspenseTreeMutated = ( ) => {
57+ storeUpdated ( ) ;
58+ } ;
59+
60+ // Since this is a passive effect, the tree may have been mutated before our initial subscription.
61+ if ( store . revisionSuspense !== initialRevision ) {
62+ // At the moment, we can treat this as a mutation.
63+ handleSuspenseTreeMutated ( ) ;
64+ }
5065
66+ store . addListener ( 'suspenseTreeMutated' , handleSuspenseTreeMutated ) ;
67+ return ( ) =>
68+ store . removeListener ( 'suspenseTreeMutated' , handleSuspenseTreeMutated ) ;
69+ } , [ initialRevision , store ] ) ;
70+ return store ;
71+ }
72+
73+ function SuspenseTreeContextController ( { children} : Props ) : React . Node {
5174 // This reducer is created inline because it needs access to the Store.
5275 // The store is mutable, but the Store itself is global and lives for the lifetime of the DevTools,
5376 // so it's okay for the reducer to have an empty dependencies array.
@@ -59,18 +82,14 @@ function SuspenseTreeContextController({children}: Props): React.Node {
5982 ) : SuspenseTreeState => {
6083 const { type} = action ;
6184 switch ( type ) {
62- case 'HANDLE_SUSPENSE_TREE_MUTATION' :
63- return { ...state , shells : store . roots } ;
6485 default :
6586 throw new Error ( `Unrecognized action "${ type } "` ) ;
6687 }
6788 } ,
6889 [ ] ,
6990 ) ;
7091
71- const initialState : SuspenseTreeState = {
72- shells : store . roots ,
73- } ;
92+ const initialState : SuspenseTreeState = { } ;
7493 const [ state , dispatch ] = useReducer ( reducer , initialState ) ;
7594 const transitionDispatch = useMemo (
7695 ( ) => ( action : SuspenseTreeAction ) =>
@@ -80,28 +99,6 @@ function SuspenseTreeContextController({children}: Props): React.Node {
8099 [ dispatch ] ,
81100 ) ;
82101
83- useEffect ( ( ) => {
84- const handleSuspenseTreeMutated = ( ) => {
85- dispatch ( {
86- type : 'HANDLE_SUSPENSE_TREE_MUTATION' ,
87- } ) ;
88- } ;
89-
90- // Since this is a passive effect, the tree may have been mutated before our initial subscription.
91- if ( store . revisionSuspense !== initialRevision ) {
92- // At the moment, we can treat this as a mutation.
93- // We don't know which Elements were newly added/removed, but that should be okay in this case.
94- // It would only impact the search state, which is unlikely to exist yet at this point.
95- dispatch ( {
96- type : 'HANDLE_SUSPENSE_TREE_MUTATION' ,
97- } ) ;
98- }
99-
100- store . addListener ( 'suspenseTreeMutated' , handleSuspenseTreeMutated ) ;
101- return ( ) =>
102- store . removeListener ( 'suspenseTreeMutated' , handleSuspenseTreeMutated ) ;
103- } , [ dispatch , initialRevision , store ] ) ;
104-
105102 return (
106103 < SuspenseTreeStateContext . Provider value = { state } >
107104 < SuspenseTreeDispatcherContext . Provider value = { transitionDispatch } >
@@ -111,15 +108,6 @@ function SuspenseTreeContextController({children}: Props): React.Node {
111108 ) ;
112109}
113110
114- function useSuspenseStore(): Store {
115- const store = useContext ( StoreContext ) ;
116- // We're currently storing everything Suspense related in the same Store as
117- // Components. However, most reads are currently stateless. This ensures
118- // the latest state is always read from the Store.
119- useContext ( SuspenseTreeStateContext ) ;
120- return store ;
121- }
122-
123111export {
124112 SuspenseTreeDispatcherContext ,
125113 SuspenseTreeStateContext ,
0 commit comments