Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Not for merge] WWW sync #18381

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/react-dom/src/client/ReactDOMComponentTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import {getParentSuspenseInstance} from './ReactDOMHostConfig';
const randomKey = Math.random()
.toString(36)
.slice(2);
const internalInstanceKey = '__reactFiber$' + randomKey;
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalContainerInstanceKey = '__reactContainer$' + randomKey;
const internalEventListenersKey = '__reactListeners$' + randomKey;
const internalInstanceKey = '__reactInternalInstance$' + randomKey;
const internalEventHandlersKey = '__reactEventHandlers$' + randomKey;
const internalContainerInstanceKey = '__reactContainere$' + randomKey;
const internalEventListenersKey = '__reactEventListeners$' + randomKey;

export function precacheFiberNode(
hostInst: Fiber,
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function flattenChildren(children) {
if (child == null) {
return;
}
content += child;
content += (child: any);
// Note: we don't warn about invalid children here.
// Instead, this is done separately below so that
// it happens during the hydration codepath too.
Expand All @@ -52,7 +52,7 @@ export function validateProps(element: Element, props: Object) {
if (typeof child === 'string' || typeof child === 'number') {
return;
}
if (typeof child.type !== 'string') {
if (typeof (child: any).type !== 'string') {
return;
}
if (!didWarnInvalidChild) {
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,11 @@ function flattenOptionChildren(children: mixed): ?string {
let content = '';
// Flatten children and warn if they aren't strings or numbers;
// invalid types are ignored.
React.Children.forEach(children, function(child) {
React.Children.forEach((children: any), function(child) {
if (child == null) {
return;
}
content += child;
content += (child: any);
if (__DEV__) {
if (
!didWarnInvalidOptionChildren &&
Expand Down
81 changes: 52 additions & 29 deletions packages/react/src/ReactChildren.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {ReactNodeList} from 'shared/ReactTypes';

import invariant from 'shared/invariant';
import {
getIteratorFn,
Expand All @@ -25,7 +29,7 @@ const SUBSEPARATOR = ':';
* @param {string} key to be escaped.
* @return {string} the escaped key.
*/
function escape(key) {
function escape(key: any): string {
const escapeRegex = /[=:]/g;
const escaperLookup = {
'=': '=0',
Expand All @@ -46,7 +50,7 @@ function escape(key) {
let didWarnAboutMaps = false;

const userProvidedKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text) {
function escapeUserProvidedKey(text: string): string {
return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
}

Expand All @@ -57,7 +61,7 @@ function escapeUserProvidedKey(text) {
* @param {number} index Index that is used if a manual key is not provided.
* @return {string}
*/
function getComponentKey(component, index) {
function getComponentKey(component: mixed, index: number): string {
// Do some typechecking here since we call this blindly. We want to ensure
// that we don't block potential future ES APIs.
if (
Expand All @@ -72,7 +76,13 @@ function getComponentKey(component, index) {
return index.toString(36);
}

function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
function mapIntoArray(
children: ?ReactNodeList,
array: Array<React$Node>,
escapedPrefix: string,
nameSoFar: string,
callback: (?React$Node) => ?ReactNodeList,
): number {
const type = typeof children;

if (type === 'undefined' || type === 'boolean') {
Expand All @@ -91,7 +101,7 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
invokeCallback = true;
break;
case 'object':
switch (children.$$typeof) {
switch ((children: any).$$typeof) {
case REACT_ELEMENT_TYPE:
case REACT_PORTAL_TYPE:
invokeCallback = true;
Expand All @@ -111,16 +121,18 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
if (childKey != null) {
escapedChildKey = escapeUserProvidedKey(childKey) + '/';
}
mapIntoArray(mappedChild, array, escapedChildKey, c => c);
mapIntoArray(mappedChild, array, escapedChildKey, '', c => c);
} else if (mappedChild != null) {
if (isValidElement(mappedChild)) {
mappedChild = cloneAndReplaceKey(
mappedChild,
// Keep both the (mapped) and old keys if they differ, just as
// traverseAllChildren used to do for objects as children
escapedPrefix +
// $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
? // $FlowFixMe Flow incorrectly thinks existing element's key can be a number
escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
Expand Down Expand Up @@ -151,18 +163,21 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
} else {
const iteratorFn = getIteratorFn(children);
if (typeof iteratorFn === 'function') {
const iterableChildren: Iterable<React$Node> & {
entries: any,
} = (children: any);
if (disableMapsAsChildren) {
invariant(
iteratorFn !== children.entries,
iteratorFn !== iterableChildren.entries,
'Maps are not valid as a React child (found: %s). Consider converting ' +
'children to an array of keyed ReactElements instead.',
children,
iterableChildren,
);
}

if (__DEV__) {
// Warn about using Maps as children
if (iteratorFn === children.entries) {
if (iteratorFn === iterableChildren.entries) {
if (!didWarnAboutMaps) {
console.warn(
'Using Maps as children is deprecated and will be removed in ' +
Expand All @@ -174,7 +189,7 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
}
}

const iterator = iteratorFn.call(children);
const iterator = iteratorFn.call(iterableChildren);
let step;
let ii = 0;
while (!(step = iterator.next()).done) {
Expand All @@ -196,12 +211,12 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
'instead.' +
ReactDebugCurrentFrame.getStackAddendum();
}
const childrenString = '' + children;
const childrenString = '' + (children: any);
invariant(
false,
'Objects are not valid as a React child (found: %s).%s',
childrenString === '[object Object]'
? 'object with keys {' + Object.keys(children).join(', ') + '}'
? 'object with keys {' + Object.keys((children: any)).join(', ') + '}'
: childrenString,
addendum,
);
Expand All @@ -211,6 +226,8 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
return subtreeCount;
}

type MapFunc = (child: ?React$Node) => ?ReactNodeList;

/**
* Maps children that are typically specified as `props.children`.
*
Expand All @@ -224,22 +241,19 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
* @param {*} context Context for mapFunction.
* @return {object} Object containing the ordered map of results.
*/
function mapChildren(children, func, context) {
function mapChildren(
children: ?ReactNodeList,
func: MapFunc,
context: mixed,
): ?Array<React$Node> {
if (children == null) {
return children;
}
const result = [];
let count = 0;
mapIntoArray(
children,
result,
'',
'',
function(child) {
return func.call(context, child, count++);
},
context,
);
mapIntoArray(children, result, '', '', function(child) {
return func.call(context, child, count++);
});
return result;
}

Expand All @@ -252,12 +266,17 @@ function mapChildren(children, func, context) {
* @param {?*} children Children tree container.
* @return {number} The number of children.
*/
function countChildren(children) {
function countChildren(children: ?ReactNodeList): number {
let n = 0;
mapChildren(children, () => n++);
mapChildren(children, () => {
n++;
// Don't return anything
});
return n;
}

type ForEachFunc = (child: ?React$Node) => void;

/**
* Iterates through children that are typically specified as `props.children`.
*
Expand All @@ -270,7 +289,11 @@ function countChildren(children) {
* @param {function(*, int)} forEachFunc
* @param {*} forEachContext Context for forEachContext.
*/
function forEachChildren(children, forEachFunc, forEachContext) {
function forEachChildren(
children: ?ReactNodeList,
forEachFunc: ForEachFunc,
forEachContext: mixed,
): void {
mapChildren(
children,
function() {
Expand All @@ -287,7 +310,7 @@ function forEachChildren(children, forEachFunc, forEachContext) {
*
* See https://reactjs.org/docs/react-api.html#reactchildrentoarray
*/
function toArray(children) {
function toArray(children: ?ReactNodeList): Array<React$Node> {
return mapChildren(children, child => child) || [];
}

Expand All @@ -305,7 +328,7 @@ function toArray(children) {
* @return {ReactElement} The first and only `ReactElement` contained in the
* structure.
*/
function onlyChild(children) {
function onlyChild<T>(children: T): T {
invariant(
isValidElement(children),
'React.Children.only expected to receive a single React element child.',
Expand Down
68 changes: 68 additions & 0 deletions packages/react/src/__tests__/ReactChildren-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,74 @@ describe('ReactChildren', () => {
]);
});

it('should combine keys when map returns an array', () => {
const instance = (
<div>
<div key="a" />
{false}
<div key="b" />
<p />
</div>
);
const mappedChildren = React.Children.map(
instance.props.children,
// Try a few things: keyed, unkeyed, hole, and a cloned element.
kid => [
<span key="x" />,
null,
<span key="y" />,
kid,
kid && React.cloneElement(kid, {key: 'z'}),
<hr />,
],
);
expect(mappedChildren.length).toBe(18);

// <div key="a">
expect(mappedChildren[0].type).toBe('span');
expect(mappedChildren[0].key).toBe('.$a/.$x');
expect(mappedChildren[1].type).toBe('span');
expect(mappedChildren[1].key).toBe('.$a/.$y');
expect(mappedChildren[2].type).toBe('div');
expect(mappedChildren[2].key).toBe('.$a/.$a');
expect(mappedChildren[3].type).toBe('div');
expect(mappedChildren[3].key).toBe('.$a/.$z');
expect(mappedChildren[4].type).toBe('hr');
expect(mappedChildren[4].key).toBe('.$a/.5');

// false
expect(mappedChildren[5].type).toBe('span');
expect(mappedChildren[5].key).toBe('.1/.$x');
expect(mappedChildren[6].type).toBe('span');
expect(mappedChildren[6].key).toBe('.1/.$y');
expect(mappedChildren[7].type).toBe('hr');
expect(mappedChildren[7].key).toBe('.1/.5');

// <div key="b">
expect(mappedChildren[8].type).toBe('span');
expect(mappedChildren[8].key).toBe('.$b/.$x');
expect(mappedChildren[9].type).toBe('span');
expect(mappedChildren[9].key).toBe('.$b/.$y');
expect(mappedChildren[10].type).toBe('div');
expect(mappedChildren[10].key).toBe('.$b/.$b');
expect(mappedChildren[11].type).toBe('div');
expect(mappedChildren[11].key).toBe('.$b/.$z');
expect(mappedChildren[12].type).toBe('hr');
expect(mappedChildren[12].key).toBe('.$b/.5');

// <p>
expect(mappedChildren[13].type).toBe('span');
expect(mappedChildren[13].key).toBe('.3/.$x');
expect(mappedChildren[14].type).toBe('span');
expect(mappedChildren[14].key).toBe('.3/.$y');
expect(mappedChildren[15].type).toBe('p');
expect(mappedChildren[15].key).toBe('.3/.3');
expect(mappedChildren[16].type).toBe('p');
expect(mappedChildren[16].key).toBe('.3/.$z');
expect(mappedChildren[17].type).toBe('hr');
expect(mappedChildren[17].key).toBe('.3/.5');
});

it('should throw on object', () => {
expect(function() {
React.Children.forEach({a: 1, b: 2}, function() {}, null);
Expand Down
8 changes: 4 additions & 4 deletions packages/shared/ReactInstanceMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@
* supported we can rename it.
*/
export function remove(key) {
key._reactInternals = undefined;
key._reactInternalFiber = undefined;
}

export function get(key) {
return key._reactInternals;
return key._reactInternalFiber;
}

export function has(key) {
return key._reactInternals !== undefined;
return key._reactInternalFiber !== undefined;
}

export function set(key, value) {
key._reactInternals = value;
key._reactInternalFiber = value;
}