Skip to content

Commit 1fde85c

Browse files
committed
Migrates implementation to react-tree-walker.
1 parent 95de045 commit 1fde85c

File tree

3 files changed

+45
-111
lines changed

3 files changed

+45
-111
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"react": "15.4.2",
8080
"react-addons-test-utils": "15.4.2",
8181
"react-dom": "15.4.2",
82+
"react-tree-walker": "0.0.1",
8283
"readline-sync": "1.4.5",
8384
"rimraf": "2.5.4",
8485
"sinon": "1.17.7",
@@ -101,5 +102,6 @@
101102
"testPathIgnorePatterns": [
102103
"<rootDir>/(commonjs|coverage|flow-typed|node_modules|tools|umd)/"
103104
]
104-
}
105+
},
106+
"dependencies": {}
105107
}

src/ssr/runJobs.js

+38-110
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
/* @flow */
22

33
// 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';
57
import { isPromise } from '../utils';
68
import ServerProvider from './ServerProvider';
79
import createRunJobsExecContext from './createRunJobsExecContext';
@@ -10,8 +12,6 @@ import type { RehydrateState } from './types';
1012

1113
type React$Element = Element<*>;
1214
type Context = { [key: string]: any; };
13-
type ElementVisitor =
14-
(element: React$Element, instance: ?Function, context: Context) => boolean | void;
1515
type ElementJob = {
1616
job: Promise<any>,
1717
element: React$Element,
@@ -23,102 +23,32 @@ type RunJobsResult = {
2323
STATE_IDENTIFIER: string,
2424
};
2525

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-
10026
function getJobs(
10127
rootElement : React$Element,
10228
rootContext : Object,
10329
fetchRoot : boolean = true,
10430
) : ElementJob[] {
10531
const jobs = [];
10632

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+
}
11746
}
118-
}
11947

120-
return undefined;
121-
});
48+
return undefined;
49+
},
50+
rootContext,
51+
);
12252

12353
return jobs;
12454
}
@@ -147,25 +77,23 @@ export default function runJobs(
14777
STATE_IDENTIFIER,
14878
});
14979

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+
);
16692

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());
17199
}

yarn.lock

+4
Original file line numberDiff line numberDiff line change
@@ -3774,6 +3774,10 @@ react-dom@15.4.2:
37743774
loose-envify "^1.1.0"
37753775
object-assign "^4.1.0"
37763776

3777+
react-tree-walker@0.0.1:
3778+
version "0.0.1"
3779+
resolved "https://registry.yarnpkg.com/react-tree-walker/-/react-tree-walker-0.0.1.tgz#101aead5bd6f23a25df29680046ee73e8d82f9d6"
3780+
37773781
react@15.3.1:
37783782
version "15.3.1"
37793783
resolved "https://registry.yarnpkg.com/react/-/react-15.3.1.tgz#f78501ed8c2ec6e6e31c3223652e97f1369d2bd6"

0 commit comments

Comments
 (0)