Support Iterables in Flight (#26313)
We support any super type of anything that we can serialize. Meaning that as long as the Type that's passed through is less precise, it means that we can encoded it as any subtype and therefore the incoming type doesn't have to be the subtype in that case. Basically, as long as you're only passing through an `Iterable<T>` in TypeScript, then you can pass any `Iterable<T>` and we'll treat it as an array. For example we support Promises *and* Thenables but both are encoded as Promises. We support Arrays and since Arrays are also Iterables, we can support Iterables. For @wongmjane
Sebastian Markbåge committed
Mar 5, 2023 at 13:18 UTC
106ea1c584d811eda5d328a5f7f856a738c2ca24
2 files changed
+35
packages/react-client/src/__tests__/ReactFlight-test.js
+28
@@ -169,6 +169,34 @@ describe('ReactFlight', () => {
169
expect(ReactNoop).toMatchRenderedOutput(<span>Hello, Seb Smith</span>);
170
});
171
172
+ it('can render an iterable as an array', async () => {
173
+ function ItemListClient(props) {
174
+ return <span>{props.items}</span>;
175
+ }
176
+ const ItemList = clientReference(ItemListClient);
177
+
178
+ function Items() {
179
+ const iterable = {
180
+ [Symbol.iterator]: function* () {
181
+ yield 'A';
182
+ yield 'B';
183
+ yield 'C';
184
+ },
185
+ };
186
+ return <ItemList items={iterable} />;
187
+ }
188
+
189
+ const model = <Items />;
190
+
191
+ const transport = ReactNoopFlightServer.render(model);
192
+
193
+ await act(async () => {
194
+ ReactNoop.render(await ReactNoopFlightClient.read(transport));
195
+ });
196
+
197
+ expect(ReactNoop).toMatchRenderedOutput(<span>ABC</span>);
198
+ });
199
+
200
it('can render a lazy component as a shared component on the server', async () => {
201
function SharedComponent({text}) {
202
return (
packages/react-server/src/ReactFlightServer.js
+7
@@ -73,6 +73,7 @@ import {
73
} from './ReactFlightNewContext';
74
75
import {
76
+ getIteratorFn,
77
REACT_ELEMENT_TYPE,
78
REACT_FORWARD_REF_TYPE,
79
REACT_FRAGMENT_TYPE,
@@ -1059,6 +1060,12 @@ export function resolveModelToJSON(
1060
}
1061
return (undefined: any);
1062
}
1063
+ if (!isArray(value)) {
1064
+ const iteratorFn = getIteratorFn(value);
1065
+ if (iteratorFn) {
1066
+ return Array.from((value: any));
1067
+ }
1068
+ }
1069
1070
if (__DEV__) {
1071
if (value !== null && !isArray(value)) {