Skip to content

Commit

Permalink
Refactor resolution to avoid memo hack
Browse files Browse the repository at this point in the history
Going through createElement isn't quite equivalent for ref and key in
props.
  • Loading branch information
sebmarkbage committed Nov 6, 2020
1 parent 891e526 commit 5d91fed
Showing 1 changed file with 24 additions and 12 deletions.
36 changes: 24 additions & 12 deletions packages/react-server/src/ReactFlightServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,13 @@ export function createRequest(
return request;
}

function attemptResolveElement(element: React$Element<any>): ReactModel {
const type = element.type;
const props = element.props;
if (element.ref !== null && element.ref !== undefined) {
function attemptResolveElement(
type: any,
key: null | React$Key,
ref: mixed,
props: any,
): ReactModel {
if (ref !== null && ref !== undefined) {
// When the ref moves to the regular props object this will implicitly
// throw for functions. We could probably relax it to a DEV warning for other
// cases.
Expand All @@ -128,31 +131,30 @@ function attemptResolveElement(element: React$Element<any>): ReactModel {
return type(props);
} else if (typeof type === 'string') {
// This is a host element. E.g. HTML.
return [REACT_ELEMENT_TYPE, type, element.key, element.props];
return [REACT_ELEMENT_TYPE, type, key, props];
} else if (typeof type === 'symbol') {
if (type === REACT_FRAGMENT_TYPE) {
// For key-less fragments, we add a small optimization to avoid serializing
// it as a wrapper.
// TODO: If a key is specified, we should propagate its key to any children.
// Same as if a server component has a key.
return element.props.children;
return props.children;
}
// This might be a built-in React component. We'll let the client decide.
// Any built-in works as long as its props are serializable.
return [REACT_ELEMENT_TYPE, type, element.key, element.props];
return [REACT_ELEMENT_TYPE, type, key, props];
} else if (type != null && typeof type === 'object') {
if (isModuleReference(type)) {
// This is a reference to a client component.
return [REACT_ELEMENT_TYPE, type, element.key, element.props];
return [REACT_ELEMENT_TYPE, type, key, props];
}
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE: {
const render = type.render;
return render(props, undefined);
}
case REACT_MEMO_TYPE: {
const nextChildren = React.createElement(type.type, element.props);
return attemptResolveElement(nextChildren);
return attemptResolveElement(type.type, key, ref, props);
}
}
}
Expand Down Expand Up @@ -389,7 +391,12 @@ export function resolveModelToJSON(
const element: React$Element<any> = (value: any);
try {
// Attempt to render the server component.
value = attemptResolveElement(element);
value = attemptResolveElement(
element.type,
element.key,
element.ref,
element.props,
);
} catch (x) {
if (typeof x === 'object' && x !== null && typeof x.then === 'function') {
// Something suspended, we'll need to create a new segment and resolve it later.
Expand Down Expand Up @@ -600,7 +607,12 @@ function retrySegment(request: Request, segment: Segment): void {
// Doing this here lets us reuse this same segment if the next component
// also suspends.
segment.query = () => value;
value = attemptResolveElement(element);
value = attemptResolveElement(
element.type,
element.key,
element.ref,
element.props,
);
}
const processedChunk = processModelChunk(request, segment.id, value);
request.completedJSONChunks.push(processedChunk);
Expand Down

0 comments on commit 5d91fed

Please sign in to comment.