88 lines
4.9 KiB
JavaScript
88 lines
4.9 KiB
JavaScript
import { applyFlightData } from "./apply-flight-data";
|
|
import { fetchServerResponse } from "./fetch-server-response";
|
|
import { PAGE_SEGMENT_KEY } from "../../../shared/lib/segment";
|
|
/**
|
|
* Refreshes inactive segments that are still in the current FlightRouterState.
|
|
* A segment is considered "inactive" when the server response indicates it didn't match to a page component.
|
|
* This happens during a soft-navigation, where the server will want to patch in the segment
|
|
* with the "default" component, but we explicitly ignore the server in this case
|
|
* and keep the existing state for that segment. New data for inactive segments are inherently
|
|
* not part of the server response when we patch the tree, because they were associated with a response
|
|
* from an earlier navigation/request. For each segment, once it becomes "active", we encode the URL that provided
|
|
* the data for it. This function traverses parallel routes looking for these markers so that it can re-fetch
|
|
* and patch the new data into the tree.
|
|
*/ export async function refreshInactiveParallelSegments(options) {
|
|
const fetchedSegments = new Set();
|
|
await refreshInactiveParallelSegmentsImpl({
|
|
...options,
|
|
rootTree: options.updatedTree,
|
|
fetchedSegments
|
|
});
|
|
}
|
|
async function refreshInactiveParallelSegmentsImpl(param) {
|
|
let { state, updatedTree, updatedCache, includeNextUrl, fetchedSegments, rootTree = updatedTree, canonicalUrl } = param;
|
|
const [, parallelRoutes, refetchPath, refetchMarker] = updatedTree;
|
|
const fetchPromises = [];
|
|
if (refetchPath && refetchPath !== canonicalUrl && refetchMarker === "refresh" && // it's possible for the tree to contain multiple segments that contain data at the same URL
|
|
// we keep track of them so we can dedupe the requests
|
|
!fetchedSegments.has(refetchPath)) {
|
|
fetchedSegments.add(refetchPath) // Mark this URL as fetched
|
|
;
|
|
// Eagerly kick off the fetch for the refetch path & the parallel routes. This should be fine to do as they each operate
|
|
// independently on their own cache nodes, and `applyFlightData` will copy anything it doesn't care about from the existing cache.
|
|
const fetchPromise = fetchServerResponse(new URL(refetchPath, location.origin), // refetch from the root of the updated tree, otherwise it will be scoped to the current segment
|
|
// and might not contain the data we need to patch in interception route data (such as dynamic params from a previous segment)
|
|
[
|
|
rootTree[0],
|
|
rootTree[1],
|
|
rootTree[2],
|
|
"refetch"
|
|
], includeNextUrl ? state.nextUrl : null, state.buildId).then((fetchResponse)=>{
|
|
const flightData = fetchResponse[0];
|
|
if (typeof flightData !== "string") {
|
|
for (const flightDataPath of flightData){
|
|
// we only pass the new cache as this function is called after clearing the router cache
|
|
// and filling in the new page data from the server. Meaning the existing cache is actually the cache that's
|
|
// just been created & has been written to, but hasn't been "committed" yet.
|
|
applyFlightData(updatedCache, updatedCache, flightDataPath);
|
|
}
|
|
} else {
|
|
// When flightData is a string, it suggests that the server response should have triggered an MPA navigation
|
|
// I'm not 100% sure of this decision, but it seems unlikely that we'd want to introduce a redirect side effect
|
|
// when refreshing on-screen data, so handling this has been ommitted.
|
|
}
|
|
});
|
|
fetchPromises.push(fetchPromise);
|
|
}
|
|
for(const key in parallelRoutes){
|
|
const parallelFetchPromise = refreshInactiveParallelSegmentsImpl({
|
|
state,
|
|
updatedTree: parallelRoutes[key],
|
|
updatedCache,
|
|
includeNextUrl,
|
|
fetchedSegments,
|
|
rootTree,
|
|
canonicalUrl
|
|
});
|
|
fetchPromises.push(parallelFetchPromise);
|
|
}
|
|
await Promise.all(fetchPromises);
|
|
}
|
|
/**
|
|
* Walks the current parallel segments to determine if they are "active".
|
|
* An active parallel route will have a `__PAGE__` segment in the FlightRouterState.
|
|
* As opposed to a `__DEFAULT__` segment, which means there was no match for that parallel route.
|
|
* We add a special marker here so that we know how to refresh its data when the router is revalidated.
|
|
*/ export function addRefreshMarkerToActiveParallelSegments(tree, path) {
|
|
const [segment, parallelRoutes, , refetchMarker] = tree;
|
|
// a page segment might also contain concatenated search params, so we do a partial match on the key
|
|
if (segment.includes(PAGE_SEGMENT_KEY) && refetchMarker !== "refresh") {
|
|
tree[2] = path;
|
|
tree[3] = "refresh";
|
|
}
|
|
for(const key in parallelRoutes){
|
|
addRefreshMarkerToActiveParallelSegments(parallelRoutes[key], path);
|
|
}
|
|
}
|
|
|
|
//# sourceMappingURL=refetch-inactive-parallel-segments.js.map
|