Implement useOpaqueIdentifier (#21260)

The format of this ID is specific to the format.
This commit is contained in:
Sebastian Markbåge
2021-04-14 14:25:42 -07:00
committed by GitHub
parent dde875dfb1
commit f4d7a0f1ea
7 changed files with 62 additions and 7 deletions
+20 -2
View File
@@ -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,
@@ -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,
@@ -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);
}
@@ -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,
};
}
@@ -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(
+6
View File
@@ -53,6 +53,8 @@ type Destination = {
const POP = Buffer.from('/', 'utf8');
let opaqueID = 0;
const ReactNoopServer = ReactFizzServer({
scheduleWork(callback: () => void) {
callback();
@@ -84,6 +86,10 @@ const ReactNoopServer = ReactFizzServer({
return {state: 'pending', children: []};
},
makeServerID(): number {
return opaqueID++;
},
getChildFormatContext(): null {
return null;
},
+12 -3
View File
@@ -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';
@@ -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;
@@ -474,7 +476,7 @@ function useTransition(): [(callback: () => void) => void, boolean] {
}
function useOpaqueIdentifier(): OpaqueIDType {
throw new Error('Not yet implemented.');
return makeServerID(currentResponseState);
}
function unsupportedRefresh() {
@@ -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;
}
+5 -1
View File
@@ -74,6 +74,8 @@ import {
finishHooks,
resetHooksState,
Dispatcher,
currentResponseState,
setCurrentResponseState,
} from './ReactFizzHooks';
import {
@@ -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;
@@ -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
@@ -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;
+2 -1
View File
@@ -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."
}