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] Implement useOpaqueIdentifier #21260

Merged
merged 1 commit into from
Apr 14, 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
22 changes: 20 additions & 2 deletions packages/react-dom/src/server/ReactDOMServerFormatConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ export type ResponseState = {
placeholderPrefix: PrecomputedChunk,
segmentPrefix: PrecomputedChunk,
boundaryPrefix: string,
opaqueIdentifierPrefix: PrecomputedChunk,
opaqueIdentifierPrefix: string,
nextSuspenseID: number,
nextOpaqueID: number,
sentCompleteSegmentFunction: boolean,
sentCompleteBoundaryFunction: boolean,
sentClientRenderFunction: boolean,
Expand All @@ -72,8 +73,9 @@ export function createResponseState(
placeholderPrefix: stringToPrecomputedChunk(identifierPrefix + 'P:'),
segmentPrefix: stringToPrecomputedChunk(identifierPrefix + 'S:'),
boundaryPrefix: identifierPrefix + 'B:',
opaqueIdentifierPrefix: stringToPrecomputedChunk(identifierPrefix + 'R:'),
opaqueIdentifierPrefix: identifierPrefix + 'R:',
nextSuspenseID: 0,
nextOpaqueID: 0,
sentCompleteSegmentFunction: false,
sentCompleteBoundaryFunction: false,
sentClientRenderFunction: false,
Expand Down Expand Up @@ -172,6 +174,22 @@ export function createSuspenseBoundaryID(
return {formattedID: null};
}

export type OpaqueIDType = string;

export function makeServerID(
responseState: null | ResponseState,
): OpaqueIDType {
invariant(
responseState !== null,
'Invalid hook call. Hooks can only be called inside of the body of a function component.',
);
// TODO: This is not deterministic since it's created during render.
return (
responseState.opaqueIdentifierPrefix +
(responseState.nextOpaqueID++).toString(36)
);
}

function encodeHTMLTextNode(text: string): string {
return escapeTextForBrowser(text);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ SUSPENSE_UPDATE_TO_CLIENT_RENDER[0] = SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG;
// Per response,
export type ResponseState = {
nextSuspenseID: number,
nextOpaqueID: number,
};

// Allows us to keep track of what we've already written so we can refer back to it.
export function createResponseState(): ResponseState {
return {
nextSuspenseID: 0,
nextOpaqueID: 0,
};
}

Expand Down Expand Up @@ -108,6 +110,19 @@ export function createSuspenseBoundaryID(
return responseState.nextSuspenseID++;
}

export type OpaqueIDType = number;

export function makeServerID(
responseState: null | ResponseState,
): OpaqueIDType {
invariant(
responseState !== null,
'Invalid hook call. Hooks can only be called inside of the body of a function component.',
);
// TODO: This is not deterministic since it's created during render.
return responseState.nextOpaqueID++;
}

const RAW_TEXT = stringToPrecomputedChunk('RCTRawText');

export function pushEmpty(
Expand Down
6 changes: 6 additions & 0 deletions packages/react-noop-renderer/src/ReactNoopServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type Destination = {

const POP = Buffer.from('/', 'utf8');

let opaqueID = 0;

const ReactNoopServer = ReactFizzServer({
scheduleWork(callback: () => void) {
callback();
Expand Down Expand Up @@ -84,6 +86,10 @@ const ReactNoopServer = ReactFizzServer({
return {state: 'pending', children: []};
},

makeServerID(): number {
return opaqueID++;
},

getChildFormatContext(): null {
return null;
},
Expand Down
15 changes: 12 additions & 3 deletions packages/react-server/src/ReactFizzHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ import type {
ReactContext,
} from 'shared/ReactTypes';

import type {ResponseState, OpaqueIDType} from './ReactServerFormatConfig';

import {readContext as readContextImpl} from './ReactFizzNewContext';

import {makeServerID} from './ReactServerFormatConfig';

import invariant from 'shared/invariant';
import {enableCache} from 'shared/ReactFeatureFlags';
import is from 'shared/objectIs';
Expand All @@ -41,8 +45,6 @@ type Hook = {|
next: Hook | null,
|};

type OpaqueIDType = string;

let currentlyRenderingComponent: Object | null = null;
let firstWorkInProgressHook: Hook | null = null;
let workInProgressHook: Hook | null = null;
Expand Down Expand Up @@ -474,7 +476,7 @@ function useTransition(): [(callback: () => void) => void, boolean] {
}

function useOpaqueIdentifier(): OpaqueIDType {
throw new Error('Not yet implemented.');
return makeServerID(currentResponseState);
}

function unsupportedRefresh() {
Expand Down Expand Up @@ -513,3 +515,10 @@ if (enableCache) {
Dispatcher.getCacheForType = getCacheForType;
Dispatcher.useCacheRefresh = useCacheRefresh;
}

export let currentResponseState: null | ResponseState = (null: any);
export function setCurrentResponseState(
responseState: null | ResponseState,
): void {
currentResponseState = responseState;
}
6 changes: 5 additions & 1 deletion packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ import {
finishHooks,
resetHooksState,
Dispatcher,
currentResponseState,
setCurrentResponseState,
} from './ReactFizzHooks';

import {
Expand Down Expand Up @@ -1341,7 +1343,8 @@ function performWork(request: Request): void {
const prevContext = getActiveContext();
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = Dispatcher;

const prevResponseState = currentResponseState;
setCurrentResponseState(request.responseState);
try {
const pingedTasks = request.pingedTasks;
let i;
Expand All @@ -1357,6 +1360,7 @@ function performWork(request: Request): void {
reportError(request, error);
fatalError(request, error);
} finally {
setCurrentResponseState(prevResponseState);
ReactCurrentDispatcher.current = prevDispatcher;
if (prevDispatcher === Dispatcher) {
// This means that we were in a reentrant work loop. This could happen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ export opaque type Destination = mixed; // eslint-disable-line no-undef
export opaque type ResponseState = mixed;
export opaque type FormatContext = mixed;
export opaque type SuspenseBoundaryID = mixed;
export opaque type OpaqueIDType = mixed;

export const isPrimaryRenderer = false;

export const getChildFormatContext = $$$hostConfig.getChildFormatContext;
export const createSuspenseBoundaryID = $$$hostConfig.createSuspenseBoundaryID;
export const makeServerID = $$$hostConfig.makeServerID;
export const pushEmpty = $$$hostConfig.pushEmpty;
export const pushTextInstance = $$$hostConfig.pushTextInstance;
export const pushStartInstance = $$$hostConfig.pushStartInstance;
Expand Down
3 changes: 2 additions & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,6 @@
"400": "menuitems cannot have `children` nor `dangerouslySetInnerHTML`.",
"401": "The stacks must reach the root at the same time. This is a bug in React.",
"402": "The depth must equal at least at zero before reaching the root. This is a bug in React.",
"403": "Tried to pop a Context at the root of the app. This is a bug in React."
"403": "Tried to pop a Context at the root of the app. This is a bug in React.",
"404": "Invalid hook call. Hooks can only be called inside of the body of a function component."
}