Skip to content

Commit 23b2874

Browse files
committed
Implement React Native host config
This is not wired up. I just need something for the flow types since Flight and Fizz are both handled by the isServerSupported flag. Might as well add something though. The principle of this format is the same structure as for HTML but a simpler binary format. Each entry is a tag followed by some data and terminated by null.
1 parent ca596c6 commit 23b2874

File tree

3 files changed

+195
-13
lines changed

3 files changed

+195
-13
lines changed

packages/react-server/src/ReactDOMServerFormatConfig.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ export type SuspenseBoundaryID = {
3838
id: null | string,
3939
};
4040

41-
export function createSuspenseBoundaryID(): SuspenseBoundaryID {
41+
export function createSuspenseBoundaryID(
42+
responseState: ResponseState,
43+
): SuspenseBoundaryID {
4244
return {id: null};
4345
}
4446

packages/react-server/src/ReactFizzServer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ function pingSuspendedWork(request: Request, work: SuspendedWork): void {
145145
}
146146
}
147147

148-
function createSuspenseBoundary(): SuspenseBoundary {
148+
function createSuspenseBoundary(request: Request): SuspenseBoundary {
149149
return {
150-
id: createSuspenseBoundaryID(),
150+
id: createSuspenseBoundaryID(request.responseState),
151151
rootSegmentID: -1,
152152
parentFlushed: false,
153153
pendingWork: 0,
@@ -264,7 +264,7 @@ function renderNode(
264264
const fallback: ReactNodeList = props.fallback;
265265
const content: ReactNodeList = props.children;
266266

267-
const newBoundary = createSuspenseBoundary();
267+
const newBoundary = createSuspenseBoundary(request);
268268

269269
const insertionIndex = segment.chunks.length;
270270
// The children of the boundary segment is actually the fallback.

packages/react-server/src/ReactNativeServerFormatConfig.js

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,197 @@
77
* @flow
88
*/
99

10-
import {convertStringToBuffer} from 'react-server/src/ReactServerStreamConfig';
10+
import type {Destination} from 'react-server/src/ReactServerStreamConfig';
1111

12-
export function formatChunkAsString(type: string, props: Object): string {
13-
let str = '<' + type + '>';
14-
if (typeof props.children === 'string') {
15-
str += props.children;
12+
import {
13+
writeChunk,
14+
convertStringToBuffer,
15+
} from 'react-server/src/ReactServerStreamConfig';
16+
17+
import invariant from 'shared/invariant';
18+
19+
// Every list of children or string is null terminated.
20+
const END_TAG = 0;
21+
// Tree node tags.
22+
const INSTANCE_TAG = 1;
23+
const PLACEHOLDER_TAG = 2;
24+
const SUSPENSE_PENDING_TAG = 3;
25+
const SUSPENSE_COMPLETE_TAG = 4;
26+
const SUSPENSE_CLIENT_RENDER_TAG = 5;
27+
// Command tags.
28+
const SEGMENT_TAG = 1;
29+
const SUSPENSE_UPDATE_TO_COMPLETE_TAG = 2;
30+
const SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG = 3;
31+
32+
const END = new Uint8Array(1);
33+
END[0] = END_TAG;
34+
const PLACEHOLDER = new Uint8Array(1);
35+
PLACEHOLDER[0] = PLACEHOLDER_TAG;
36+
const INSTANCE = new Uint8Array(1);
37+
INSTANCE[0] = INSTANCE_TAG;
38+
const SUSPENSE_PENDING = new Uint8Array(1);
39+
SUSPENSE_PENDING[0] = SUSPENSE_PENDING_TAG;
40+
const SUSPENSE_COMPLETE = new Uint8Array(1);
41+
SUSPENSE_COMPLETE[0] = SUSPENSE_COMPLETE_TAG;
42+
const SUSPENSE_CLIENT_RENDER = new Uint8Array(1);
43+
SUSPENSE_CLIENT_RENDER[0] = SUSPENSE_CLIENT_RENDER_TAG;
44+
45+
const SEGMENT = new Uint8Array(1);
46+
SEGMENT[0] = SEGMENT_TAG;
47+
const SUSPENSE_UPDATE_TO_COMPLETE = new Uint8Array(1);
48+
SUSPENSE_UPDATE_TO_COMPLETE[0] = SUSPENSE_UPDATE_TO_COMPLETE_TAG;
49+
const SUSPENSE_UPDATE_TO_CLIENT_RENDER = new Uint8Array(1);
50+
SUSPENSE_UPDATE_TO_CLIENT_RENDER[0] = SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG;
51+
52+
// Per response,
53+
export type ResponseState = {
54+
nextSuspenseID: number,
55+
};
56+
57+
// Allows us to keep track of what we've already written so we can refer back to it.
58+
export function createResponseState(): ResponseState {
59+
return {
60+
nextSuspenseID: 0,
61+
};
62+
}
63+
64+
// This object is used to lazily reuse the ID of the first generated node, or assign one.
65+
// This is very specific to DOM where we can't assign an ID to.
66+
export type SuspenseBoundaryID = number;
67+
68+
export function createSuspenseBoundaryID(
69+
responseState: ResponseState,
70+
): SuspenseBoundaryID {
71+
return responseState.nextSuspenseID++;
72+
}
73+
74+
const RAW_TEXT = convertStringToBuffer('RCTRawText');
75+
76+
export function pushTextInstance(
77+
target: Array<Uint8Array>,
78+
text: string,
79+
): void {
80+
target.push(
81+
INSTANCE,
82+
RAW_TEXT, // Type
83+
END, // Null terminated type string
84+
// TODO: props { text: text }
85+
END, // End of children
86+
);
87+
}
88+
89+
export function pushStartInstance(
90+
target: Array<Uint8Array>,
91+
type: string,
92+
props: Object,
93+
): void {
94+
target.push(
95+
INSTANCE,
96+
convertStringToBuffer(type),
97+
END, // Null terminated type string
98+
// TODO: props
99+
);
100+
}
101+
102+
export function pushEndInstance(
103+
target: Array<Uint8Array>,
104+
type: string,
105+
props: Object,
106+
): void {
107+
target.push(END);
108+
}
109+
110+
// IDs are formatted as little endian Uint16
111+
function formatID(id: number): Uint8Array {
112+
if (id > 0xffff) {
113+
invariant(
114+
false,
115+
'More boundaries or placeholders than we expected to ever emit.',
116+
);
16117
}
17-
str += '</' + type + '>';
18-
return str;
118+
const buffer = new Uint8Array(2);
119+
buffer[0] = (id >>> 8) & 0xff;
120+
buffer[1] = id & 0xff;
121+
return buffer;
122+
}
123+
124+
// Structural Nodes
125+
126+
// A placeholder is a node inside a hidden partial tree that can be filled in later, but before
127+
// display. It's never visible to users.
128+
export function writePlaceholder(
129+
destination: Destination,
130+
id: number,
131+
): boolean {
132+
writeChunk(destination, PLACEHOLDER);
133+
return writeChunk(destination, formatID(id));
134+
}
135+
136+
// Suspense boundaries are encoded as comments.
137+
export function writeStartCompletedSuspenseBoundary(
138+
destination: Destination,
139+
id: SuspenseBoundaryID,
140+
): boolean {
141+
writeChunk(destination, SUSPENSE_COMPLETE);
142+
return writeChunk(destination, formatID(id));
143+
}
144+
export function writeStartPendingSuspenseBoundary(
145+
destination: Destination,
146+
id: SuspenseBoundaryID,
147+
): boolean {
148+
writeChunk(destination, SUSPENSE_PENDING);
149+
return writeChunk(destination, formatID(id));
150+
}
151+
export function writeStartClientRenderedSuspenseBoundary(
152+
destination: Destination,
153+
id: SuspenseBoundaryID,
154+
): boolean {
155+
writeChunk(destination, SUSPENSE_CLIENT_RENDER);
156+
return writeChunk(destination, formatID(id));
157+
}
158+
export function writeEndSuspenseBoundary(destination: Destination): boolean {
159+
return writeChunk(destination, END);
160+
}
161+
162+
export function writeStartSegment(
163+
destination: Destination,
164+
id: number,
165+
): boolean {
166+
writeChunk(destination, SEGMENT);
167+
return writeChunk(destination, formatID(id));
168+
}
169+
export function writeEndSegment(destination: Destination): boolean {
170+
return writeChunk(destination, END);
171+
}
172+
173+
// Instruction Set
174+
175+
export function writeCompletedSegmentInstruction(
176+
destination: Destination,
177+
responseState: ResponseState,
178+
contentSegmentID: number,
179+
): boolean {
180+
// We don't need to emit this. Instead the client will keep track of pending placeholders.
181+
// TODO: Returning true here is not correct. Avoid having to call this function at all.
182+
return true;
183+
}
184+
185+
export function writeCompletedBoundaryInstruction(
186+
destination: Destination,
187+
responseState: ResponseState,
188+
boundaryID: SuspenseBoundaryID,
189+
contentSegmentID: number,
190+
): boolean {
191+
writeChunk(destination, SUSPENSE_UPDATE_TO_COMPLETE);
192+
writeChunk(destination, formatID(boundaryID));
193+
return writeChunk(destination, formatID(contentSegmentID));
19194
}
20195

21-
export function formatChunk(type: string, props: Object): Uint8Array {
22-
return convertStringToBuffer(formatChunkAsString(type, props));
196+
export function writeClientRenderBoundaryInstruction(
197+
destination: Destination,
198+
responseState: ResponseState,
199+
boundaryID: SuspenseBoundaryID,
200+
): boolean {
201+
writeChunk(destination, SUSPENSE_UPDATE_TO_CLIENT_RENDER);
202+
return writeChunk(destination, formatID(boundaryID));
23203
}

0 commit comments

Comments
 (0)