in packages/relay-runtime/store/experimental-live-resolvers/LiveResolverStore.js [683:776]
function updateTargetFromSource(
target: MutableRecordSource,
source: RecordSource,
currentWriteEpoch: number,
idsMarkedForInvalidation: ?DataIDSet,
updatedRecordIDs: DataIDSet,
invalidatedRecordIDs: DataIDSet,
): void {
// First, update any records that were marked for invalidation.
// For each provided dataID that was invalidated, we write the
// INVALIDATED_AT_KEY on the record, indicating
// the epoch at which the record was invalidated.
if (idsMarkedForInvalidation) {
idsMarkedForInvalidation.forEach(dataID => {
const targetRecord = target.get(dataID);
const sourceRecord = source.get(dataID);
// If record was deleted during the update (and also invalidated),
// we don't need to count it as an invalidated id
if (sourceRecord === null) {
return;
}
let nextRecord;
if (targetRecord != null) {
// If the target record exists, use it to set the epoch
// at which it was invalidated. This record will be updated with
// any changes from source in the section below
// where we update the target records based on the source.
nextRecord = RelayModernRecord.clone(targetRecord);
} else {
// If the target record doesn't exist, it means that a new record
// in the source was created (and also invalidated), so we use that
// record to set the epoch at which it was invalidated. This record
// will be updated with any changes from source in the section below
// where we update the target records based on the source.
nextRecord =
sourceRecord != null ? RelayModernRecord.clone(sourceRecord) : null;
}
if (!nextRecord) {
return;
}
RelayModernRecord.setValue(
nextRecord,
RelayStoreUtils.INVALIDATED_AT_KEY,
currentWriteEpoch,
);
invalidatedRecordIDs.add(dataID);
target.set(dataID, nextRecord);
});
}
// Update the target based on the changes present in source
const dataIDs = source.getRecordIDs();
for (let ii = 0; ii < dataIDs.length; ii++) {
const dataID = dataIDs[ii];
const sourceRecord = source.get(dataID);
const targetRecord = target.get(dataID);
// Prevent mutation of a record from outside the store.
if (__DEV__) {
if (sourceRecord) {
RelayModernRecord.freeze(sourceRecord);
}
}
if (sourceRecord && targetRecord) {
// ReactFlightClientResponses are lazy and only materialize when readRoot
// is called when we read the field, so if the record is a Flight field
// we always use the new record's data regardless of whether
// it actually changed. Let React take care of reconciliation instead.
const nextRecord =
RelayModernRecord.getType(targetRecord) ===
RelayStoreReactFlightUtils.REACT_FLIGHT_TYPE_NAME
? sourceRecord
: RelayModernRecord.update(targetRecord, sourceRecord);
if (nextRecord !== targetRecord) {
// Prevent mutation of a record from outside the store.
if (__DEV__) {
RelayModernRecord.freeze(nextRecord);
}
updatedRecordIDs.add(dataID);
target.set(dataID, nextRecord);
}
} else if (sourceRecord === null) {
target.delete(dataID);
if (targetRecord !== null) {
updatedRecordIDs.add(dataID);
}
} else if (sourceRecord) {
target.set(dataID, sourceRecord);
updatedRecordIDs.add(dataID);
} // don't add explicit undefined
}
}