UNPKG

react-concurrent-router

Version:

Performant routing embracing React concurrent UI patterns

87 lines (84 loc) 3.13 kB
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 };