Flow-ify ReactPartialRenderer (#11251)
* flow ReactPartialRenderer * prettier * moving flow type around; * Move anys to DEV-only code path and keep it typechecked * Increase Flow coverage
Dustan Kasten committed
Oct 26, 2017 at 12:59 UTC
80849fd1c6c7a75d6aa862c53eb2119df706e82d
1 file changed
+96
-40
packages/react-dom/src/server/ReactPartialRenderer.js
+96
-40
@@ -3,10 +3,14 @@
3
*
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
*/
9
10
'use strict';
11
12
+import type {ReactElement} from 'shared/ReactElementType';
13
+
14
var React = require('react');
15
var emptyFunction = require('fbjs/lib/emptyFunction');
16
var emptyObject = require('fbjs/lib/emptyObject');
@@ -27,7 +31,12 @@ var escapeTextContentForBrowser = require('../shared/escapeTextContentForBrowser
31
var isCustomComponent = require('../shared/isCustomComponent');
32
var omittedCloseTags = require('../shared/omittedCloseTags');
33
30
-var toArray = React.Children.toArray;
34
+// Based on reading the React.Children implementation. TODO: type this somewhere?
35
+type ReactNode = string | number | ReactElement;
36
+type FlatReactChildren = Array<null | ReactNode>;
37
+type toArrayType = (children: mixed) => FlatReactChildren;
38
+var toArray = ((React.Children.toArray: any): toArrayType);
39
+
40
var getStackAddendum = emptyFunction.thatReturns('');
41
42
if (__DEV__) {
@@ -63,14 +72,15 @@ if (__DEV__) {
72
var {ReactDebugCurrentFrame} = require('shared/ReactGlobalSharedState');
73
var currentDebugStack = null;
74
var currentDebugElementStack = null;
66
- var setCurrentDebugStack = function(stack) {
67
- currentDebugElementStack = stack[stack.length - 1].debugElementStack;
75
+ var setCurrentDebugStack = function(stack: Array<Frame>) {
76
+ var frame: Frame = stack[stack.length - 1];
77
+ currentDebugElementStack = ((frame: any): FrameDev).debugElementStack;
78
// We are about to enter a new composite stack, reset the array.
79
currentDebugElementStack.length = 0;
80
currentDebugStack = stack;
81
ReactDebugCurrentFrame.getCurrentStack = getStackAddendum;
82
};
73
- var pushElementToDebugStack = function(element) {
83
+ var pushElementToDebugStack = function(element: ReactElement) {
84
if (currentDebugElementStack !== null) {
85
currentDebugElementStack.push(element);
86
}
@@ -87,7 +97,8 @@ if (__DEV__) {
97
let stack = '';
98
let debugStack = currentDebugStack;
99
for (let i = debugStack.length - 1; i >= 0; i--) {
90
- let debugElementStack = debugStack[i].debugElementStack;
100
+ const frame: Frame = debugStack[i];
101
+ let debugElementStack = ((frame: any): FrameDev).debugElementStack;
102
for (let ii = debugElementStack.length - 1; ii >= 0; ii--) {
103
stack += describeStackFrame(debugElementStack[ii]);
104
}
@@ -130,7 +141,7 @@ var processStyleName = memoizeStringOnly(function(styleName) {
141
return hyphenateStyleName(styleName);
142
});
143
133
-function createMarkupForStyles(styles) {
144
+function createMarkupForStyles(styles): string | null {
145
var serialized = '';
146
var delimiter = '';
147
for (var styleName in styles) {
@@ -159,7 +170,7 @@ function createMarkupForStyles(styles) {
170
}
171
172
function warnNoop(
162
- publicInstance: ReactComponent<any, any, any>,
173
+ publicInstance: React$Component<any, any>,
174
callerName: string,
175
) {
176
if (__DEV__) {
@@ -195,7 +206,7 @@ function getNonChildrenInnerMarkup(props) {
206
return null;
207
}
208
198
-function flattenOptionChildren(children) {
209
+function flattenOptionChildren(children: mixed): string {
210
var content = '';
211
// Flatten children and warn if they aren't strings or numbers;
212
// invalid types are ignored.
@@ -257,13 +268,13 @@ var RESERVED_PROPS = {
268
};
269
270
function createOpenTagMarkup(
260
- tagVerbatim,
261
- tagLowercase,
262
- props,
263
- namespace,
264
- makeStaticMarkup,
265
- isRootElement,
266
-) {
271
+ tagVerbatim: string,
272
+ tagLowercase: string,
273
+ props: Object,
274
+ namespace: string,
275
+ makeStaticMarkup: boolean,
276
+ isRootElement: boolean,
277
+): string {
278
var ret = '<' + tagVerbatim;
279
280
for (var propKey in props) {
@@ -317,12 +328,20 @@ function validateRenderResult(child, type) {
328
}
329
}
330
320
-function resolve(child, context) {
331
+function resolve(
332
+ child: mixed,
333
+ context: Object,
334
+): {|
335
+ child: mixed,
336
+ context: Object,
337
+|} {
338
while (React.isValidElement(child)) {
339
+ // Safe because we just checked it's an element.
340
+ var element: ReactElement = ((child: any): ReactElement);
341
if (__DEV__) {
323
- pushElementToDebugStack(child);
342
+ pushElementToDebugStack(element);
343
}
325
- var Component = child.type;
344
+ var Component = element.type;
345
if (typeof Component !== 'function') {
346
break;
347
}
@@ -354,9 +373,9 @@ function resolve(child, context) {
373
};
374
375
if (shouldConstruct(Component)) {
357
- inst = new Component(child.props, publicContext, updater);
376
+ inst = new Component(element.props, publicContext, updater);
377
} else {
359
- inst = Component(child.props, publicContext, updater);
378
+ inst = Component(element.props, publicContext, updater);
379
if (inst == null || inst.render == null) {
380
child = inst;
381
validateRenderResult(child, Component);
@@ -364,7 +383,7 @@ function resolve(child, context) {
383
}
384
}
385
367
- inst.props = child.props;
386
+ inst.props = element.props;
387
inst.context = publicContext;
388
inst.updater = updater;
389
@@ -388,7 +407,7 @@ function resolve(child, context) {
407
for (var i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
408
var partial = oldQueue[i];
409
var partialState = typeof partial === 'function'
391
- ? partial.call(inst, nextState, child.props, publicContext)
410
+ ? partial.call(inst, nextState, element.props, publicContext)
411
: partial;
412
if (partialState) {
413
if (dontMutate) {
@@ -442,20 +461,46 @@ function resolve(child, context) {
461
return {child, context};
462
}
463
464
+type Frame = {
465
+ domNamespace: string,
466
+ children: FlatReactChildren,
467
+ childIndex: number,
468
+ context: Object,
469
+ footer: string,
470
+};
471
+
472
+type FrameDev = Frame & {
473
+ debugElementStack: Array<ReactElement>,
474
+};
475
+
476
class ReactDOMServerRenderer {
446
- constructor(element, makeStaticMarkup) {
447
- var children = React.isValidElement(element) ? [element] : toArray(element);
448
- var topFrame = {
477
+ stack: Array<Frame>;
478
+ exhausted: boolean;
479
+ // TODO: type this more strictly:
480
+ currentSelectValue: any;
481
+ previousWasTextNode: boolean;
482
+ makeStaticMarkup: boolean;
483
+
484
+ constructor(children: mixed, makeStaticMarkup: boolean) {
485
+ var flatChildren;
486
+ if (React.isValidElement(children)) {
487
+ // Safe because we just checked it's an element.
488
+ var element = ((children: any): ReactElement);
489
+ flatChildren = [element];
490
+ } else {
491
+ flatChildren = toArray(children);
492
+ }
493
+ var topFrame: Frame = {
494
// Assume all trees start in the HTML namespace (not totally true, but
495
// this is what we did historically)
496
domNamespace: Namespaces.html,
452
- children,
497
+ children: flatChildren,
498
childIndex: 0,
499
context: emptyObject,
500
footer: '',
501
};
502
if (__DEV__) {
458
- topFrame.debugElementStack = [];
503
+ ((topFrame: any): FrameDev).debugElementStack = [];
504
}
505
this.stack = [topFrame];
506
this.exhausted = false;
@@ -464,7 +509,7 @@ class ReactDOMServerRenderer {
509
this.makeStaticMarkup = makeStaticMarkup;
510
}
511
467
- read(bytes) {
512
+ read(bytes: number): string | null {
513
if (this.exhausted) {
514
return null;
515
}
@@ -475,7 +520,7 @@ class ReactDOMServerRenderer {
520
this.exhausted = true;
521
break;
522
}
478
- var frame = this.stack[this.stack.length - 1];
523
+ var frame: Frame = this.stack[this.stack.length - 1];
524
if (frame.childIndex >= frame.children.length) {
525
var footer = frame.footer;
526
out += footer;
@@ -501,7 +546,11 @@ class ReactDOMServerRenderer {
546
return out;
547
}
548
504
- render(child, context, parentNamespace) {
549
+ render(
550
+ child: ReactNode | null,
551
+ context: Object,
552
+ parentNamespace: string,
553
+ ): string {
554
if (typeof child === 'string' || typeof child === 'number') {
555
var text = '' + child;
556
if (text === '') {
@@ -516,23 +565,26 @@ class ReactDOMServerRenderer {
565
this.previousWasTextNode = true;
566
return escapeTextContentForBrowser(text);
567
} else {
519
- ({child, context} = resolve(child, context));
520
- if (child === null || child === false) {
568
+ var nextChild;
569
+ ({child: nextChild, context} = resolve(child, context));
570
+ if (nextChild === null || nextChild === false) {
571
return '';
572
} else {
523
- if (React.isValidElement(child)) {
524
- return this.renderDOM(child, context, parentNamespace);
573
+ if (React.isValidElement(nextChild)) {
574
+ // Safe because we just checked it's an element.
575
+ var nextElement = ((nextChild: any): ReactElement);
576
+ return this.renderDOM(nextElement, context, parentNamespace);
577
} else {
526
- var children = toArray(child);
527
- var frame = {
578
+ var nextChildren = toArray(nextChild);
579
+ var frame: Frame = {
580
domNamespace: parentNamespace,
529
- children,
581
+ children: nextChildren,
582
childIndex: 0,
583
context: context,
584
footer: '',
585
};
586
if (__DEV__) {
535
- frame.debugElementStack = [];
587
+ ((frame: any): FrameDev).debugElementStack = [];
588
}
589
this.stack.push(frame);
590
return '';
@@ -541,7 +593,11 @@ class ReactDOMServerRenderer {
593
}
594
}
595
544
- renderDOM(element, context, parentNamespace) {
596
+ renderDOM(
597
+ element: ReactElement,
598
+ context: Object,
599
+ parentNamespace: string,
600
+ ): string {
601
var tag = element.type.toLowerCase();
602
603
let namespace = parentNamespace;
@@ -828,7 +884,7 @@ class ReactDOMServerRenderer {
884
footer: footer,
885
};
886
if (__DEV__) {
831
- frame.debugElementStack = [];
887
+ ((frame: any): FrameDev).debugElementStack = [];
888
}
889
this.stack.push(frame);
890
this.previousWasTextNode = false;