@samitouri / QOS-React-2 / commits / 80849fd1c6

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;