Skip to content

Commit d1d30ea

Browse files
committed
[Flight] Allow lazily resolving outlined models (#28780)
We used to assume that outlined models are emitted before the reference (which was true before Blobs). However, it still wasn't safe to assume that all the data will be available because an "import" (client reference) can be async and therefore if it's directly a child of an outlined model, it won't be able to update in place. This is a similar problem as the one hit by @unstubbable in #28669 with elements, but a little different since these don't follow the same way of wrapping. I don't love the structuring of this code which now needs to pass a first class mapper instead of just being known code. It also shares the host path which is just an identity function. It wouldn't necessarily pass my own review but I don't have a better one for now. I'd really prefer if this was done at a "row" level but that ends up creating even more code. Add test for Blob in FormData and async modules in Maps. DiffTrain build for [14f50ad](14f50ad)
1 parent b1a0af9 commit d1d30ea

File tree

3 files changed

+184
-145
lines changed

3 files changed

+184
-145
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4c12339ce3fa398050d1026c616ea43d43dcaf4a
1+
14f50ad1554f0adf20fa1b5bc62859ed32be0bc6

compiled/facebook-www/ReactFlightDOMClient-dev.modern.js

Lines changed: 119 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,14 @@ if (__DEV__) {
657657
return chunk;
658658
}
659659

660-
function createModelResolver(chunk, parentObject, key, cyclic) {
660+
function createModelResolver(
661+
chunk,
662+
parentObject,
663+
key,
664+
cyclic,
665+
response,
666+
map
667+
) {
661668
var blocked;
662669

663670
if (initializingChunkBlockedModel) {
@@ -674,11 +681,11 @@ if (__DEV__) {
674681
}
675682

676683
return function (value) {
677-
parentObject[key] = value; // If this is the root object for a model reference, where `blocked.value`
684+
parentObject[key] = map(response, value); // If this is the root object for a model reference, where `blocked.value`
678685
// is a stale `null`, the resolved value can be used directly.
679686

680687
if (key === "" && blocked.value === null) {
681-
blocked.value = value;
688+
blocked.value = parentObject[key];
682689
}
683690

684691
blocked.deps--;
@@ -733,26 +740,95 @@ if (__DEV__) {
733740
return proxy;
734741
}
735742

736-
function getOutlinedModel(response, id) {
743+
function getOutlinedModel(response, id, parentObject, key, map) {
737744
var chunk = getChunk(response, id);
738745

739746
switch (chunk.status) {
740747
case RESOLVED_MODEL:
741748
initializeModelChunk(chunk);
742749
break;
750+
751+
case RESOLVED_MODULE:
752+
initializeModuleChunk(chunk);
753+
break;
743754
} // The status might have changed after initialization.
744755

745756
switch (chunk.status) {
746-
case INITIALIZED: {
747-
return chunk.value;
748-
}
749-
// We always encode it first in the stream so it won't be pending.
757+
case INITIALIZED:
758+
var chunkValue = map(response, chunk.value);
759+
760+
if (chunk._debugInfo) {
761+
// If we have a direct reference to an object that was rendered by a synchronous
762+
// server component, it might have some debug info about how it was rendered.
763+
// We forward this to the underlying object. This might be a React Element or
764+
// an Array fragment.
765+
// If this was a string / number return value we lose the debug info. We choose
766+
// that tradeoff to allow sync server components to return plain values and not
767+
// use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
768+
if (
769+
typeof chunkValue === "object" &&
770+
chunkValue !== null &&
771+
(Array.isArray(chunkValue) ||
772+
chunkValue.$$typeof === REACT_ELEMENT_TYPE) &&
773+
!chunkValue._debugInfo
774+
) {
775+
// We should maybe use a unique symbol for arrays but this is a React owned array.
776+
// $FlowFixMe[prop-missing]: This should be added to elements.
777+
Object.defineProperty(chunkValue, "_debugInfo", {
778+
configurable: false,
779+
enumerable: false,
780+
writable: true,
781+
value: chunk._debugInfo
782+
});
783+
}
784+
}
785+
786+
return chunkValue;
787+
788+
case PENDING:
789+
case BLOCKED:
790+
case CYCLIC:
791+
var parentChunk = initializingChunk;
792+
chunk.then(
793+
createModelResolver(
794+
parentChunk,
795+
parentObject,
796+
key,
797+
chunk.status === CYCLIC,
798+
response,
799+
map
800+
),
801+
createModelReject(parentChunk)
802+
);
803+
return null;
750804

751805
default:
752806
throw chunk.reason;
753807
}
754808
}
755809

810+
function createMap(response, model) {
811+
return new Map(model);
812+
}
813+
814+
function createSet(response, model) {
815+
return new Set(model);
816+
}
817+
818+
function createFormData(response, model) {
819+
var formData = new FormData();
820+
821+
for (var i = 0; i < model.length; i++) {
822+
formData.append(model[i][0], model[i][1]);
823+
}
824+
825+
return formData;
826+
}
827+
828+
function createModel(response, model) {
829+
return model;
830+
}
831+
756832
function parseModelString(response, parentObject, key, value) {
757833
if (value[0] === "$") {
758834
if (value === "$") {
@@ -798,8 +874,13 @@ if (__DEV__) {
798874
// Server Reference
799875
var _id2 = parseInt(value.slice(2), 16);
800876

801-
var metadata = getOutlinedModel(response, _id2);
802-
return createServerReferenceProxy(response, metadata);
877+
return getOutlinedModel(
878+
response,
879+
_id2,
880+
parentObject,
881+
key,
882+
createServerReferenceProxy
883+
);
803884
}
804885

805886
case "T": {
@@ -822,17 +903,26 @@ if (__DEV__) {
822903
// Map
823904
var _id4 = parseInt(value.slice(2), 16);
824905

825-
var data = getOutlinedModel(response, _id4);
826-
return new Map(data);
906+
return getOutlinedModel(
907+
response,
908+
_id4,
909+
parentObject,
910+
key,
911+
createMap
912+
);
827913
}
828914

829915
case "W": {
830916
// Set
831917
var _id5 = parseInt(value.slice(2), 16);
832918

833-
var _data = getOutlinedModel(response, _id5);
834-
835-
return new Set(_data);
919+
return getOutlinedModel(
920+
response,
921+
_id5,
922+
parentObject,
923+
key,
924+
createSet
925+
);
836926
}
837927

838928
case "B": {
@@ -843,15 +933,13 @@ if (__DEV__) {
843933
// FormData
844934
var _id7 = parseInt(value.slice(2), 16);
845935

846-
var _data3 = getOutlinedModel(response, _id7);
847-
848-
var formData = new FormData();
849-
850-
for (var i = 0; i < _data3.length; i++) {
851-
formData.append(_data3[i][0], _data3[i][1]);
852-
}
853-
854-
return formData;
936+
return getOutlinedModel(
937+
response,
938+
_id7,
939+
parentObject,
940+
key,
941+
createFormData
942+
);
855943
}
856944

857945
case "I": {
@@ -908,70 +996,13 @@ if (__DEV__) {
908996
// We assume that anything else is a reference ID.
909997
var _id8 = parseInt(value.slice(1), 16);
910998

911-
var _chunk2 = getChunk(response, _id8);
912-
913-
switch (_chunk2.status) {
914-
case RESOLVED_MODEL:
915-
initializeModelChunk(_chunk2);
916-
break;
917-
918-
case RESOLVED_MODULE:
919-
initializeModuleChunk(_chunk2);
920-
break;
921-
} // The status might have changed after initialization.
922-
923-
switch (_chunk2.status) {
924-
case INITIALIZED:
925-
var chunkValue = _chunk2.value;
926-
927-
if (_chunk2._debugInfo) {
928-
// If we have a direct reference to an object that was rendered by a synchronous
929-
// server component, it might have some debug info about how it was rendered.
930-
// We forward this to the underlying object. This might be a React Element or
931-
// an Array fragment.
932-
// If this was a string / number return value we lose the debug info. We choose
933-
// that tradeoff to allow sync server components to return plain values and not
934-
// use them as React Nodes necessarily. We could otherwise wrap them in a Lazy.
935-
if (
936-
typeof chunkValue === "object" &&
937-
chunkValue !== null &&
938-
(Array.isArray(chunkValue) ||
939-
chunkValue.$$typeof === REACT_ELEMENT_TYPE) &&
940-
!chunkValue._debugInfo
941-
) {
942-
// We should maybe use a unique symbol for arrays but this is a React owned array.
943-
// $FlowFixMe[prop-missing]: This should be added to elements.
944-
Object.defineProperty(chunkValue, "_debugInfo", {
945-
configurable: false,
946-
enumerable: false,
947-
writable: true,
948-
value: _chunk2._debugInfo
949-
});
950-
}
951-
}
952-
953-
return chunkValue;
954-
955-
case PENDING:
956-
case BLOCKED:
957-
case CYCLIC:
958-
var parentChunk = initializingChunk;
959-
960-
_chunk2.then(
961-
createModelResolver(
962-
parentChunk,
963-
parentObject,
964-
key,
965-
_chunk2.status === CYCLIC
966-
),
967-
createModelReject(parentChunk)
968-
);
969-
970-
return null;
971-
972-
default:
973-
throw _chunk2.reason;
974-
}
999+
return getOutlinedModel(
1000+
response,
1001+
_id8,
1002+
parentObject,
1003+
key,
1004+
createModel
1005+
);
9751006
}
9761007
}
9771008
}

0 commit comments

Comments
 (0)