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

[Fizz] Add FormatContext and Refactor Work #21103

Merged
merged 5 commits into from
Mar 26, 2021
Merged
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
6 changes: 5 additions & 1 deletion packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {
abort,
} from 'react-server/src/ReactFizzServer';

import {createResponseState} from './ReactDOMServerFormatConfig';
import {
createResponseState,
createRootFormatContext,
} from './ReactDOMServerFormatConfig';

type Options = {
identifierPrefix?: string,
Expand Down Expand Up @@ -46,6 +49,7 @@ function renderToReadableStream(
children,
controller,
createResponseState(options ? options.identifierPrefix : undefined),
createRootFormatContext(), // We call this here in case we need options to initialize it.
options ? options.progressiveChunkSize : undefined,
options ? options.onError : undefined,
options ? options.onCompleteAll : undefined,
Expand Down
6 changes: 5 additions & 1 deletion packages/react-dom/src/server/ReactDOMFizzServerNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import {
abort,
} from 'react-server/src/ReactFizzServer';

import {createResponseState} from './ReactDOMServerFormatConfig';
import {
createResponseState,
createRootFormatContext,
} from './ReactDOMServerFormatConfig';

function createDrainHandler(destination, request) {
return () => startFlowing(request);
Expand Down Expand Up @@ -46,6 +49,7 @@ function pipeToNodeWritable(
children,
destination,
createResponseState(options ? options.identifierPrefix : undefined),
createRootFormatContext(), // We call this here in case we need options to initialize it.
options ? options.progressiveChunkSize : undefined,
options ? options.onError : undefined,
options ? options.onCompleteAll : undefined,
Expand Down
52 changes: 51 additions & 1 deletion packages/react-dom/src/server/ReactDOMServerFormatConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import escapeTextForBrowser from './escapeTextForBrowser';
import invariant from 'shared/invariant';

// Per response,
// Per response, global state that is not contextual to the rendering subtree.
export type ResponseState = {
placeholderPrefix: PrecomputedChunk,
segmentPrefix: PrecomputedChunk,
Expand Down Expand Up @@ -50,6 +50,56 @@ export function createResponseState(
};
}

// Constants for the namespace we use. We don't actually provide the namespace but conditionally
// use different segment parents based on namespace. Therefore we use constants instead of the string.
const ROOT_NAMESPACE = 0; // At the root we don't need to know which namespace it is. We just need to know that it's already the right one.
const HTML_NAMESPACE = 1;
const SVG_NAMESPACE = 2;
const MATHML_NAMESPACE = 3;

type NamespaceFlag = 0 | 1 | 2 | 3;

// Lets us keep track of contextual state and pick it back up after suspending.
export type FormatContext = {
namespace: NamespaceFlag, // root/svg/html/mathml
selectedValue: null | string, // the selected value(s) inside a <select>, or null outside <select>
};

function createFormatContext(
namespace: NamespaceFlag,
selectedValue: null | string,
): FormatContext {
return {
namespace,
selectedValue,
};
}

export function createRootFormatContext(): FormatContext {
return createFormatContext(ROOT_NAMESPACE, null);
}

export function getChildFormatContext(
parentContext: FormatContext,
type: string,
props: Object,
): FormatContext {
switch (type) {
case 'select':
return createFormatContext(
parentContext.namespace,
props.value != null ? props.value : props.defaultValue,
);
case 'svg':
return createFormatContext(SVG_NAMESPACE, null);
case 'math':
return createFormatContext(MATHML_NAMESPACE, null);
case 'foreignObject':
return createFormatContext(HTML_NAMESPACE, null);
}
return parentContext;
}

// This object is used to lazily reuse the ID of the first generated node, or assign one.
// We can't assign an ID up front because the node we're attaching it to might already
// have one. So we need to lazily use that if it's available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,33 @@ export function createResponseState(): ResponseState {
};
}

// isInAParentText
export type FormatContext = boolean;

export function createRootFormatContext(): FormatContext {
return false;
}

export function getChildFormatContext(
parentContext: FormatContext,
type: string,
props: Object,
): FormatContext {
const prevIsInAParentText = parentContext;
const isInAParentText =
type === 'AndroidTextInput' || // Android
type === 'RCTMultilineTextInputView' || // iOS
type === 'RCTSinglelineTextInputView' || // iOS
type === 'RCTText' ||
type === 'RCTVirtualText';

if (prevIsInAParentText !== isInAParentText) {
return isInAParentText;
} else {
return parentContext;
}
}

// This object is used to lazily reuse the ID of the first generated node, or assign one.
// This is very specific to DOM where we can't assign an ID to.
export type SuspenseBoundaryID = number;
Expand Down
5 changes: 5 additions & 0 deletions packages/react-noop-renderer/src/ReactNoopServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ const ReactNoopServer = ReactFizzServer({
return {state: 'pending', children: []};
},

getChildFormatContext(): null {
return null;
},

pushTextInstance(target: Array<Uint8Array>, text: string): void {
const textInstance: TextInstance = {
text,
Expand Down Expand Up @@ -236,6 +240,7 @@ function render(children: React$Element<any>, options?: Options): Destination {
children,
destination,
null,
null,
options ? options.progressiveChunkSize : undefined,
options ? options.onError : undefined,
options ? options.onCompleteAll : undefined,
Expand Down
Loading