react-concurrent-router
Version:
Performant routing embracing React concurrent UI patterns
87 lines (84 loc) • 3.13 kB
JavaScript
import _extends from '@babel/runtime/helpers/esm/extends';
import React, { useContext, useCallback, useState, useMemo, useEffect, Fragment } from 'react';
import { R as RouterContext } from './RouterContext-Bf2jDrm9.js';
const RouteRenderer = ({
pendingIndicator
}) => {
const {
assistPrefetch,
awaitComponent,
get,
subscribe
} = useContext(RouterContext);
const computeInitialEntry = useCallback(entry => {
if (!assistPrefetch || !entry.prefetched) return entry;
const prefetched = {};
for (const [property, value] of entry.prefetched.entries()) {
prefetched[property] = value.data;
}
return _extends({}, entry, {
prefetched
});
}, []);
const [isPendingEntry, setIsPendingEntry] = useState(false);
const [routeEntry, setRouteEntry] = useState(computeInitialEntry(get()));
const Component = useMemo(() => routeEntry.component.read(), [routeEntry]);
const processFetchEntities = useCallback(pendingEntry => {
if (!pendingEntry.assistedPrefetch) {
return {
prefetched: pendingEntry.prefetched,
toBePrefetched: []
};
}
const prefetched = {};
const toBePrefetched = [];
for (const [property, value] of pendingEntry.prefetched.entries()) {
if (value.defer === false && value.data.isLoaded() === false) {
toBePrefetched.push({
key: property,
data: value.data
});
} else prefetched[property] = value.data;
}
return {
prefetched,
toBePrefetched
};
}, []);
useEffect(() => {
const dispose = subscribe(async nextEntry => {
if (nextEntry.skipRender) return;
const {
prefetched,
toBePrefetched
} = processFetchEntities(nextEntry);
const shouldUpdatePendingIndicator = Boolean(pendingIndicator && (awaitComponent && !nextEntry.component.isLoaded() || nextEntry.assistedPrefetch && toBePrefetched.length));
if (shouldUpdatePendingIndicator) setIsPendingEntry(true);
if (awaitComponent) await nextEntry.component.load();
const newlyPrefetched = toBePrefetched.length ? await toBePrefetched.reduce(async (newlyPrefetched, {
key,
data
}) => {
await data.load();
return _extends({}, newlyPrefetched, {
[key]: data
});
}, {}) : {};
const routeEntry = _extends({}, nextEntry, {
prefetched: _extends({}, prefetched, newlyPrefetched)
});
setRouteEntry(routeEntry);
if (shouldUpdatePendingIndicator) setIsPendingEntry(false);
});
return () => dispose();
}, [assistPrefetch, awaitComponent, processFetchEntities, pendingIndicator, subscribe]);
const locationKey = routeEntry.location ? routeEntry.location.pathname + routeEntry.location.search + routeEntry.location.hash : 'default';
return React.createElement(Fragment, {
key: locationKey
}, isPendingEntry && pendingIndicator ? pendingIndicator : null, React.createElement(Component, {
key: window.location.href,
params: routeEntry.params,
prefetched: routeEntry.prefetched
}));
};
export { RouteRenderer as default };