1
1
/* @flow */
2
2
3
3
// eslint-disable-next-line import/no-extraneous-dependencies
4
- import React , { Children , Element } from 'react' ;
4
+ import React , { Element } from 'react' ;
5
+ // eslint-disable-next-line import/no-extraneous-dependencies
6
+ import reactTreeWalker from 'react-tree-walker' ;
5
7
import { isPromise } from '../utils' ;
6
8
import ServerProvider from './ServerProvider' ;
7
9
import createRunJobsExecContext from './createRunJobsExecContext' ;
@@ -10,8 +12,6 @@ import type { RehydrateState } from './types';
10
12
11
13
type React$Element = Element < * > ;
12
14
type Context = { [ key : string ] : any ; } ;
13
- type ElementVisitor =
14
- ( element : React$Element , instance : ?Function , context : Context ) => boolean | void ;
15
15
type ElementJob = {
16
16
job : Promise < any > ,
17
17
element : React$Element ,
@@ -23,102 +23,32 @@ type RunJobsResult = {
23
23
STATE_IDENTIFIER : string ,
24
24
} ;
25
25
26
- // Recurse an React Element tree, running visitor on each element.
27
- // If visitor returns `false`, don't call the element's render function
28
- // or recurse into its child elements
29
- export function walkTree (
30
- element : React$Element ,
31
- context : Context ,
32
- visitor : ElementVisitor ,
33
- ) {
34
- const Component = element . type ;
35
-
36
- // a stateless functional component or a class
37
- if ( typeof Component === 'function' ) {
38
- const props = Object . assign ( { } , Component . defaultProps , element . props ) ;
39
- let childContext = context ;
40
- let child ;
41
-
42
- // Are we are a react class?
43
- // http://bit.ly/2j9Ifk3
44
- const isReactClassComponent = Component . prototype &&
45
- ( Component . prototype . isReactComponent || Component . prototype . isPureReactComponent ) ;
46
-
47
- if ( isReactClassComponent ) {
48
- const instance = new Component ( props , context ) ;
49
- // In case the user doesn't pass these to super in the constructor
50
- instance . props = instance . props || props ;
51
- instance . context = instance . context || context ;
52
-
53
- // Override setState to just change the state, not queue up an update.
54
- // (we can't do the default React thing as we aren't mounted "properly"
55
- // however, we don't need to re-render as well only support setState in
56
- // componentWillMount, which happens *before* render).
57
- instance . setState = ( newState ) => {
58
- instance . state = Object . assign ( { } , instance . state , newState ) ;
59
- } ;
60
-
61
- if ( instance . componentWillMount ) {
62
- instance . componentWillMount ( ) ;
63
- }
64
-
65
- if ( instance . getChildContext ) {
66
- childContext = Object . assign ( { } , context , instance . getChildContext ( ) ) ;
67
- }
68
-
69
- if ( visitor ( element , instance , context ) === false ) {
70
- return ;
71
- }
72
-
73
- child = instance . render ( ) ;
74
- } else { // just a stateless functional
75
- if ( visitor ( element , null , context ) === false ) {
76
- return ;
77
- }
78
-
79
- child = Component ( props , context ) ;
80
- }
81
-
82
- if ( child ) {
83
- walkTree ( child , childContext , visitor ) ;
84
- }
85
- } else { // a basic string or dom element, just get children
86
- if ( visitor ( element , null , context ) === false ) {
87
- return ;
88
- }
89
-
90
- if ( element . props && element . props . children ) {
91
- Children . forEach ( element . props . children , ( child : any ) => {
92
- if ( child ) {
93
- walkTree ( child , context , visitor ) ;
94
- }
95
- } ) ;
96
- }
97
- }
98
- }
99
-
100
26
function getJobs (
101
27
rootElement : React$Element ,
102
28
rootContext : Object ,
103
29
fetchRoot : boolean = true ,
104
30
) : ElementJob [ ] {
105
31
const jobs = [ ] ;
106
32
107
- walkTree ( rootElement , rootContext , ( element , instance , context ) => {
108
- const skipRoot = ! fetchRoot && ( element === rootElement ) ;
109
- if ( instance && typeof instance . getExecutingJob === 'function' && ! skipRoot ) {
110
- const job = instance . getExecutingJob ( ) ;
111
- if ( isPromise ( job ) ) {
112
- jobs . push ( { job, element, context } ) ;
113
-
114
- // Tell walkTree to not recurse inside this component; we will
115
- // wait for the query to execute before attempting it.
116
- return false ;
33
+ reactTreeWalker (
34
+ rootElement ,
35
+ ( element , instance , context ) => {
36
+ const skipRoot = ! fetchRoot && ( element === rootElement ) ;
37
+ if ( instance && typeof instance . getExecutingJob === 'function' && ! skipRoot ) {
38
+ const job = instance . getExecutingJob ( ) ;
39
+ if ( isPromise ( job ) ) {
40
+ jobs . push ( { job, element, context } ) ;
41
+
42
+ // Tell walkTree to not recurse inside this component; we will
43
+ // wait for the query to execute before attempting it.
44
+ return false ;
45
+ }
117
46
}
118
- }
119
47
120
- return undefined ;
121
- } ) ;
48
+ return undefined ;
49
+ } ,
50
+ rootContext ,
51
+ ) ;
122
52
123
53
return jobs ;
124
54
}
@@ -147,25 +77,23 @@ export default function runJobs(
147
77
STATE_IDENTIFIER ,
148
78
} ) ;
149
79
150
- const jobs = getJobs ( processingElement , rootContext , isRoot ) ;
151
-
152
- // No jobs found, nothing to do.
153
- if ( ! jobs . length ) {
154
- return Promise . resolve ( resolveResult ( ) ) ;
155
- }
156
-
157
- // Wait on each job that we found, re-rendering the subtree when they are done.
158
- const mappedJobs = jobs . map ( ( { job, element, context } ) =>
159
- job . then ( ( ) => runJobs (
160
- element ,
161
- context ,
162
- // We've just grabbed the job for element so don't try and get it again
163
- false ,
164
- ) ) ,
165
- ) ;
80
+ const runningJobs = getJobs ( processingElement , rootContext , isRoot )
81
+ // Map over the jobs and then...
82
+ . map ( ( { job, element, context } ) =>
83
+ // ... make sure they will continue executing jobs for their Children
84
+ // when they are complete.
85
+ job . then ( ( ) => runJobs (
86
+ element ,
87
+ context ,
88
+ // We've just grabbed the job for element so don't try and get it again
89
+ false ,
90
+ ) ) ,
91
+ ) ;
166
92
167
- return Promise . all ( mappedJobs )
168
- // Swallow errors.
169
- . catch ( ( ) => undefined )
170
- . then ( ( ) => resolveResult ( ) ) ;
93
+ return runningJobs . length > 0
94
+ ? Promise . all ( runningJobs )
95
+ // Swallow errors.
96
+ . catch ( ( ) => undefined )
97
+ . then ( ( ) => resolveResult ( ) )
98
+ : Promise . resolve ( resolveResult ( ) ) ;
171
99
}
0 commit comments