Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
[ghstack-poisoned]
  • Loading branch information
mofeiZ committed Oct 2, 2024
2 parents d9c0dc6 + ee44b6a commit 0735db9
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 28 deletions.
17 changes: 16 additions & 1 deletion packages/react-client/src/ReactFlightReplyClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export function processReply(
temporaryReferences: void | TemporaryReferenceSet,
resolve: (string | FormData) => void,
reject: (error: mixed) => void,
): void {
): (reason: mixed) => void {
let nextPartId = 1;
let pendingParts = 0;
let formData: null | FormData = null;
Expand Down Expand Up @@ -841,6 +841,19 @@ export function processReply(
return JSON.stringify(model, resolveToJSON);
}

function abort(reason: mixed): void {
if (pendingParts > 0) {
pendingParts = 0; // Don't resolve again later.
// Resolve with what we have so far, which may have holes at this point.
// They'll error when the stream completes on the server.
if (formData === null) {
resolve(json);
} else {
resolve(formData);
}
}
}

const json = serializeModel(root, 0);

if (formData === null) {
Expand All @@ -854,6 +867,8 @@ export function processReply(
resolve(formData);
}
}

return abort;
}

const boundCache: WeakMap<
Expand Down
29 changes: 12 additions & 17 deletions packages/react-native-renderer/src/ReactNativeTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,31 +112,32 @@ export interface INativeMethods {
measure(callback: MeasureOnSuccessCallback): void;
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void;
measureLayout(
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
relativeToNativeNode: number | HostInstance,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void,
): void;
setNativeProps(nativeProps: {...}): void;
}

export type NativeMethods = $ReadOnly<{|
export type NativeMethods = $ReadOnly<{
blur(): void,
focus(): void,
measure(callback: MeasureOnSuccessCallback): void,
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
measureLayout(
relativeToNativeNode: number | ElementRef<HostComponent<mixed>>,
relativeToNativeNode: number | HostInstance,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void,
): void,
setNativeProps(nativeProps: {...}): void,
|}>;
}>;

// This validates that INativeMethods and NativeMethods stay in sync using Flow!
declare const ensureNativeMethodsAreSynced: NativeMethods;
(ensureNativeMethodsAreSynced: INativeMethods);

export type HostComponent<T> = AbstractComponent<T, $ReadOnly<NativeMethods>>;
export type HostInstance = NativeMethods;
export type HostComponent<Config> = AbstractComponent<Config, HostInstance>;

type SecretInternalsType = {
computeComponentStackForErrorReporting(tag: number): string,
Expand Down Expand Up @@ -209,7 +210,7 @@ export type RenderRootOptions = {
export type ReactNativeType = {
findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?ElementRef<HostComponent<mixed>>,
): ?HostInstance,
findNodeHandle<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?number,
Expand All @@ -218,14 +219,11 @@ export type ReactNativeType = {
child: PublicInstance | HostComponent<mixed>,
): boolean,
dispatchCommand(
handle: ElementRef<HostComponent<mixed>>,
handle: HostInstance,
command: string,
args: Array<mixed>,
): void,
sendAccessibilityEvent(
handle: ElementRef<HostComponent<mixed>>,
eventType: string,
): void,
sendAccessibilityEvent(handle: HostInstance, eventType: string): void,
render(
element: MixedElement,
containerTag: number,
Expand All @@ -247,20 +245,17 @@ type PublicTextInstance = mixed;
export type ReactFabricType = {
findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?ElementRef<HostComponent<mixed>>,
): ?HostInstance,
findNodeHandle<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?number,
dispatchCommand(
handle: ElementRef<HostComponent<mixed>>,
handle: HostInstance,
command: string,
args: Array<mixed>,
): void,
isChildPublicInstance(parent: PublicInstance, child: PublicInstance): boolean,
sendAccessibilityEvent(
handle: ElementRef<HostComponent<mixed>>,
eventType: string,
): void,
sendAccessibilityEvent(handle: HostInstance, eventType: string): void,
render(
element: MixedElement,
containerTag: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ function createFromFetch<T>(

function encodeReply(
value: ReactServerValue,
options?: {temporaryReferences?: TemporaryReferenceSet},
options?: {temporaryReferences?: TemporaryReferenceSet, signal?: AbortSignal},
): Promise<
string | URLSearchParams | FormData,
> /* We don't use URLSearchParams yet but maybe */ {
return new Promise((resolve, reject) => {
processReply(
const abort = processReply(
value,
'',
options && options.temporaryReferences
Expand All @@ -135,6 +135,18 @@ function encodeReply(
resolve,
reject,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort((signal: any).reason);
} else {
const listener = () => {
abort((signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ function createFromFetch<T>(

function encodeReply(
value: ReactServerValue,
options?: {temporaryReferences?: TemporaryReferenceSet},
options?: {temporaryReferences?: TemporaryReferenceSet, signal?: AbortSignal},
): Promise<
string | URLSearchParams | FormData,
> /* We don't use URLSearchParams yet but maybe */ {
return new Promise((resolve, reject) => {
processReply(
const abort = processReply(
value,
'',
options && options.temporaryReferences
Expand All @@ -134,6 +134,18 @@ function encodeReply(
resolve,
reject,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort((signal: any).reason);
} else {
const listener = () => {
abort((signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ function createFromFetch<T>(

function encodeReply(
value: ReactServerValue,
options?: {temporaryReferences?: TemporaryReferenceSet},
options?: {temporaryReferences?: TemporaryReferenceSet, signal?: AbortSignal},
): Promise<
string | URLSearchParams | FormData,
> /* We don't use URLSearchParams yet but maybe */ {
return new Promise((resolve, reject) => {
processReply(
const abort = processReply(
value,
'',
options && options.temporaryReferences
Expand All @@ -163,6 +163,18 @@ function encodeReply(
resolve,
reject,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort((signal: any).reason);
} else {
const listener = () => {
abort((signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,4 +618,20 @@ describe('ReactFlightDOMReply', () => {
const root = await ReactServerDOMServer.decodeReply(body, webpackServerMap);
expect(root.prop.obj).toBe(root.prop);
});

it('can abort an unresolved model and get the partial result', async () => {
const promise = new Promise(r => {});
const controller = new AbortController();
const bodyPromise = ReactServerDOMClient.encodeReply(
{promise: promise, hello: 'world'},
{signal: controller.signal},
);
controller.abort();

const result = await ReactServerDOMServer.decodeReply(await bodyPromise);
expect(result.hello).toBe('world');
// TODO: await result.promise should reject at this point because the stream
// has closed but that's a bug in both ReactFlightReplyServer and ReactFlightClient.
// It just halts in this case.
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ function createFromFetch<T>(

function encodeReply(
value: ReactServerValue,
options?: {temporaryReferences?: TemporaryReferenceSet},
options?: {temporaryReferences?: TemporaryReferenceSet, signal?: AbortSignal},
): Promise<
string | URLSearchParams | FormData,
> /* We don't use URLSearchParams yet but maybe */ {
return new Promise((resolve, reject) => {
processReply(
const abort = processReply(
value,
'',
options && options.temporaryReferences
Expand All @@ -134,6 +134,18 @@ function encodeReply(
resolve,
reject,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort((signal: any).reason);
} else {
const listener = () => {
abort((signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ function createFromFetch<T>(

function encodeReply(
value: ReactServerValue,
options?: {temporaryReferences?: TemporaryReferenceSet},
options?: {temporaryReferences?: TemporaryReferenceSet, signal?: AbortSignal},
): Promise<
string | URLSearchParams | FormData,
> /* We don't use URLSearchParams yet but maybe */ {
return new Promise((resolve, reject) => {
processReply(
const abort = processReply(
value,
'',
options && options.temporaryReferences
Expand All @@ -163,6 +163,18 @@ function encodeReply(
resolve,
reject,
);
if (options && options.signal) {
const signal = options.signal;
if (signal.aborted) {
abort((signal: any).reason);
} else {
const listener = () => {
abort((signal: any).reason);
signal.removeEventListener('abort', listener);
};
signal.addEventListener('abort', listener);
}
}
});
}

Expand Down

0 comments on commit 0735db9

Please sign in to comment.