mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Implement useOpaqueIdentifier (#21260)
The format of this ID is specific to the format.
This commit is contained in:
+20
-2
@@ -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(
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user