Skip to content

Commit

Permalink
Add iterable support to Fizz
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed Apr 10, 2021
1 parent 933880b commit 37c336c
Showing 1 changed file with 61 additions and 1 deletion.
62 changes: 61 additions & 1 deletion packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ const didWarnAboutContextTypeOnFunctionComponent = {};
const didWarnAboutGetDerivedStateOnFunctionComponent = {};
let didWarnAboutReassigningProps = false;
const didWarnAboutDefaultPropsOnFunctionComponent = {};
let didWarnAboutGenerators = false;
let didWarnAboutMaps = false;

// This would typically be a function component but we still support module pattern
// components for some reason.
Expand Down Expand Up @@ -708,6 +710,40 @@ function renderElement(
}
}

function validateIterable(iterable, iteratorFn: Function): void {
if (__DEV__) {
// We don't support rendering Generators because it's a mutation.
// See https://github.com/facebook/react/issues/12995
if (
typeof Symbol === 'function' &&
// $FlowFixMe Flow doesn't know about toStringTag
iterable[Symbol.toStringTag] === 'Generator'
) {
if (!didWarnAboutGenerators) {
console.error(
'Using Generators as children is unsupported and will likely yield ' +
'unexpected results because enumerating a generator mutates it. ' +
'You may convert it to an array with `Array.from()` or the ' +
'`[...spread]` operator before rendering. Keep in mind ' +
'you might need to polyfill these features for older browsers.',
);
}
didWarnAboutGenerators = true;
}

// Warn about using Maps as children
if ((iterable: any).entries === iteratorFn) {
if (!didWarnAboutMaps) {
console.error(
'Using Maps as children is not supported. ' +
'Use an array of keyed ReactElements instead.',
);
}
didWarnAboutMaps = true;
}
}
}

// This function by it self renders a node and consumes the task by mutating it
// to update the current execution state.
function renderNodeDestructive(
Expand Down Expand Up @@ -756,7 +792,30 @@ function renderNodeDestructive(

const iteratorFn = getIteratorFn(node);
if (iteratorFn) {
throw new Error('Not yet implemented node type.');
if (__DEV__) {
validateIterable(node, iteratorFn());
}
const iterator = iteratorFn.call(node);
if (iterator) {
let step = iterator.next();
// If there are not entries, we need to push an empty so we start by checking that.
if (!step.done) {
do {
// Recursively render the rest. We need to use the non-destructive form
// so that we can safely pop back up and render the sibling if something
// suspends.
renderNode(request, task, step.value);
step = iterator.next();
} while (!step.done);
return;
}
}
pushEmpty(
task.blockedSegment.chunks,
request.responseState,
task.assignID,
);
task.assignID = null;
}

const childString = Object.prototype.toString.call(node);
Expand Down Expand Up @@ -805,6 +864,7 @@ function renderNodeDestructive(

// Any other type is assumed to be empty.
pushEmpty(task.blockedSegment.chunks, request.responseState, task.assignID);
task.assignID = null;
}

function spawnNewSuspendedTask(
Expand Down

0 comments on commit 37c336c

Please sign in to comment.