@tanstack/react-router 0.0.1-beta.273 → 0.0.1-beta.274
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/Matches.js.map +1 -1
- package/build/cjs/RouterProvider.js +9 -6
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/index.js +4 -1
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js +150 -100
- package/build/cjs/router.js.map +1 -1
- package/build/esm/index.js +157 -103
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +353 -353
- package/build/types/Matches.d.ts +2 -2
- package/build/types/RouterProvider.d.ts +1 -1
- package/build/types/route.d.ts +2 -2
- package/build/types/router.d.ts +3 -1
- package/build/umd/index.development.js +159 -106
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/Matches.tsx +2 -2
- package/src/RouterProvider.tsx +11 -5
- package/src/route.ts +2 -2
- package/src/router.ts +197 -128
package/build/esm/index.js
CHANGED
|
@@ -472,9 +472,13 @@ function useLoaderData(opts) {
|
|
|
472
472
|
});
|
|
473
473
|
}
|
|
474
474
|
|
|
475
|
-
|
|
475
|
+
let routerContext = /*#__PURE__*/React.createContext(null);
|
|
476
476
|
if (typeof document !== 'undefined') {
|
|
477
|
-
window.__TSR_ROUTER_CONTEXT__
|
|
477
|
+
if (window.__TSR_ROUTER_CONTEXT__) {
|
|
478
|
+
routerContext = window.__TSR_ROUTER_CONTEXT__;
|
|
479
|
+
} else {
|
|
480
|
+
window.__TSR_ROUTER_CONTEXT__ = routerContext;
|
|
481
|
+
}
|
|
478
482
|
}
|
|
479
483
|
function RouterProvider({
|
|
480
484
|
router,
|
|
@@ -584,7 +588,7 @@ function Transitioner() {
|
|
|
584
588
|
return null;
|
|
585
589
|
}
|
|
586
590
|
function getRouteMatch(state, id) {
|
|
587
|
-
return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
|
|
591
|
+
return [...state.preloadMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
|
|
588
592
|
}
|
|
589
593
|
function useRouterState(opts) {
|
|
590
594
|
const router = useRouter();
|
|
@@ -1688,7 +1692,7 @@ class Router {
|
|
|
1688
1692
|
context: undefined,
|
|
1689
1693
|
abortController: new AbortController(),
|
|
1690
1694
|
shouldReloadDeps: undefined,
|
|
1691
|
-
|
|
1695
|
+
fetchCount: 0,
|
|
1692
1696
|
cause
|
|
1693
1697
|
};
|
|
1694
1698
|
|
|
@@ -1897,10 +1901,13 @@ class Router {
|
|
|
1897
1901
|
}) => {
|
|
1898
1902
|
let latestPromise;
|
|
1899
1903
|
let firstBadMatchIndex;
|
|
1900
|
-
const
|
|
1904
|
+
const updateMatch = match => {
|
|
1905
|
+
const isPreload = this.state.preloadMatches.find(d => d.id === match.id);
|
|
1906
|
+
const isPending = this.state.pendingMatches?.find(d => d.id === match.id);
|
|
1907
|
+
const matchesKey = isPreload ? 'preloadMatches' : isPending ? 'pendingMatches' : 'matches';
|
|
1901
1908
|
this.__store.setState(s => ({
|
|
1902
1909
|
...s,
|
|
1903
|
-
|
|
1910
|
+
[matchesKey]: s[matchesKey]?.map(d => d.id === match.id ? match : d)
|
|
1904
1911
|
}));
|
|
1905
1912
|
};
|
|
1906
1913
|
|
|
@@ -1953,7 +1960,7 @@ class Router {
|
|
|
1953
1960
|
from: match.pathname
|
|
1954
1961
|
}),
|
|
1955
1962
|
buildLocation: this.buildLocation,
|
|
1956
|
-
cause: match.cause
|
|
1963
|
+
cause: preload ? 'preload' : match.cause
|
|
1957
1964
|
})) ?? {};
|
|
1958
1965
|
if (isRedirect(beforeLoadContext)) {
|
|
1959
1966
|
throw beforeLoadContext;
|
|
@@ -1983,7 +1990,7 @@ class Router {
|
|
|
1983
1990
|
const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
|
|
1984
1991
|
const matchPromises = [];
|
|
1985
1992
|
validResolvedMatches.forEach((match, index) => {
|
|
1986
|
-
matchPromises.push((async
|
|
1993
|
+
matchPromises.push(new Promise(async resolve => {
|
|
1987
1994
|
const parentMatchPromise = matchPromises[index - 1];
|
|
1988
1995
|
const route = this.looseRoutesById[match.routeId];
|
|
1989
1996
|
const handleErrorAndRedirect = err => {
|
|
@@ -1998,92 +2005,95 @@ class Router {
|
|
|
1998
2005
|
let loadPromise;
|
|
1999
2006
|
matches[index] = match = {
|
|
2000
2007
|
...match,
|
|
2001
|
-
fetchedAt: Date.now(),
|
|
2002
2008
|
showPending: false
|
|
2003
2009
|
};
|
|
2010
|
+
let didShowPending = false;
|
|
2004
2011
|
const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
};
|
|
2012
|
+
const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
|
|
2013
|
+
const shouldPending = !preload && pendingMs && (route.options.pendingComponent ?? this.options.defaultPendingComponent);
|
|
2014
|
+
const fetch = async () => {
|
|
2015
|
+
if (match.isFetching) {
|
|
2016
|
+
loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
|
|
2017
|
+
} else {
|
|
2018
|
+
const loaderContext = {
|
|
2019
|
+
params: match.params,
|
|
2020
|
+
search: match.search,
|
|
2021
|
+
preload: !!preload,
|
|
2022
|
+
parentMatchPromise,
|
|
2023
|
+
abortController: match.abortController,
|
|
2024
|
+
context: match.context,
|
|
2025
|
+
location: this.state.location,
|
|
2026
|
+
navigate: opts => this.navigate({
|
|
2027
|
+
...opts,
|
|
2028
|
+
from: match.pathname
|
|
2029
|
+
}),
|
|
2030
|
+
cause: preload ? 'preload' : match.cause
|
|
2031
|
+
};
|
|
2026
2032
|
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2033
|
+
// Default to reloading the route all the time
|
|
2034
|
+
let shouldLoad = true;
|
|
2035
|
+
const shouldReloadFn = route.options.shouldReload;
|
|
2036
|
+
let shouldReloadDeps = typeof shouldReloadFn === 'function' ? shouldReloadFn(loaderContext) : !!(shouldReloadFn ?? true);
|
|
2037
|
+
const compareDeps = () => {
|
|
2038
|
+
if (typeof shouldReloadDeps === 'object') {
|
|
2039
|
+
// compare the deps to see if they've changed
|
|
2040
|
+
shouldLoad = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
|
|
2041
|
+
} else {
|
|
2042
|
+
shouldLoad = !!shouldReloadDeps;
|
|
2043
|
+
}
|
|
2044
|
+
};
|
|
2045
|
+
|
|
2046
|
+
// If it's the first preload, or the route is entering, or we're
|
|
2047
|
+
// invalidating, we definitely need to load the route
|
|
2048
|
+
if (invalidate) ; else if (preload) {
|
|
2049
|
+
if (!match.fetchCount) ; else {
|
|
2050
|
+
compareDeps();
|
|
2051
|
+
}
|
|
2052
|
+
} else if (match.cause === 'enter') {
|
|
2053
|
+
if (!match.fetchCount) ; else {
|
|
2054
|
+
compareDeps();
|
|
2055
|
+
}
|
|
2037
2056
|
} else {
|
|
2038
|
-
|
|
2057
|
+
compareDeps();
|
|
2058
|
+
}
|
|
2059
|
+
if (typeof shouldReloadDeps === 'object') {
|
|
2060
|
+
matches[index] = match = {
|
|
2061
|
+
...match,
|
|
2062
|
+
shouldReloadDeps
|
|
2063
|
+
};
|
|
2039
2064
|
}
|
|
2040
|
-
}
|
|
2041
2065
|
|
|
2042
|
-
|
|
2043
|
-
|
|
2066
|
+
// If the user doesn't want the route to reload, just
|
|
2067
|
+
// resolve with the existing loader data
|
|
2044
2068
|
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
...match,
|
|
2051
|
-
isFetching: true
|
|
2052
|
-
};
|
|
2053
|
-
const componentsPromise = Promise.all(componentTypes.map(async type => {
|
|
2054
|
-
const component = route.options[type];
|
|
2055
|
-
if (component?.preload) {
|
|
2056
|
-
await component.preload();
|
|
2069
|
+
if (!shouldLoad) {
|
|
2070
|
+
loadPromise = Promise.resolve(match.loaderData);
|
|
2071
|
+
} else {
|
|
2072
|
+
if (match.fetchCount && match.status === 'success') {
|
|
2073
|
+
resolve();
|
|
2057
2074
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
matches[index] = match = {
|
|
2064
|
-
...match,
|
|
2065
|
-
loadPromise
|
|
2066
|
-
};
|
|
2067
|
-
if (!preload) {
|
|
2068
|
-
updatePendingMatch(match);
|
|
2069
|
-
}
|
|
2070
|
-
let didShowPending = false;
|
|
2071
|
-
const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
|
|
2072
|
-
await new Promise(async resolve => {
|
|
2073
|
-
// If the route has a pending component and a pendingMs option,
|
|
2074
|
-
// forcefully show the pending component
|
|
2075
|
-
if (pendingPromise) {
|
|
2076
|
-
pendingPromise.then(() => {
|
|
2077
|
-
if (latestPromise = checkLatest()) return;
|
|
2078
|
-
didShowPending = true;
|
|
2075
|
+
|
|
2076
|
+
// Otherwise, load the route
|
|
2079
2077
|
matches[index] = match = {
|
|
2080
2078
|
...match,
|
|
2081
|
-
|
|
2079
|
+
isFetching: true,
|
|
2080
|
+
fetchCount: match.fetchCount + 1
|
|
2082
2081
|
};
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2082
|
+
const componentsPromise = Promise.all(componentTypes.map(async type => {
|
|
2083
|
+
const component = route.options[type];
|
|
2084
|
+
if (component?.preload) {
|
|
2085
|
+
await component.preload();
|
|
2086
|
+
}
|
|
2087
|
+
}));
|
|
2088
|
+
const loaderPromise = route.options.loader?.(loaderContext);
|
|
2089
|
+
loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
|
|
2090
|
+
}
|
|
2086
2091
|
}
|
|
2092
|
+
matches[index] = match = {
|
|
2093
|
+
...match,
|
|
2094
|
+
loadPromise
|
|
2095
|
+
};
|
|
2096
|
+
updateMatch(match);
|
|
2087
2097
|
try {
|
|
2088
2098
|
const loaderData = await loadPromise;
|
|
2089
2099
|
if (latestPromise = checkLatest()) return await latestPromise;
|
|
@@ -2124,18 +2134,38 @@ class Router {
|
|
|
2124
2134
|
// we already moved the pendingMatches to the matches
|
|
2125
2135
|
// state, so we need to update that specific match
|
|
2126
2136
|
if (didShowPending && pendingMinMs && match.showPending) {
|
|
2127
|
-
|
|
2128
|
-
...s,
|
|
2129
|
-
matches: s.matches?.map(d => d.id === match.id ? match : d)
|
|
2130
|
-
}));
|
|
2137
|
+
updateMatch(match);
|
|
2131
2138
|
}
|
|
2132
2139
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2140
|
+
updateMatch(match);
|
|
2141
|
+
};
|
|
2142
|
+
if (match.fetchCount && match.status === 'success') {
|
|
2143
|
+
// Background Fetching
|
|
2144
|
+
fetch();
|
|
2145
|
+
} else {
|
|
2146
|
+
// Critical Fetching
|
|
2147
|
+
|
|
2148
|
+
// If we need to potentially show the pending component,
|
|
2149
|
+
// start a timer to show it after the pendingMs
|
|
2150
|
+
if (shouldPending) {
|
|
2151
|
+
new Promise(r => setTimeout(r, pendingMs)).then(async () => {
|
|
2152
|
+
if (latestPromise = checkLatest()) return latestPromise;
|
|
2153
|
+
didShowPending = true;
|
|
2154
|
+
matches[index] = match = {
|
|
2155
|
+
...match,
|
|
2156
|
+
showPending: true
|
|
2157
|
+
};
|
|
2158
|
+
updateMatch(match);
|
|
2159
|
+
resolve();
|
|
2160
|
+
});
|
|
2135
2161
|
}
|
|
2136
|
-
|
|
2137
|
-
}
|
|
2138
|
-
|
|
2162
|
+
await fetch();
|
|
2163
|
+
}
|
|
2164
|
+
resolve();
|
|
2165
|
+
// No Fetching
|
|
2166
|
+
|
|
2167
|
+
resolve();
|
|
2168
|
+
}));
|
|
2139
2169
|
});
|
|
2140
2170
|
await Promise.all(matchPromises);
|
|
2141
2171
|
return matches;
|
|
@@ -2158,20 +2188,32 @@ class Router {
|
|
|
2158
2188
|
toLocation: next,
|
|
2159
2189
|
pathChanged: pathDidChange
|
|
2160
2190
|
});
|
|
2161
|
-
|
|
2162
|
-
// Match the routes
|
|
2163
|
-
let pendingMatches = this.matchRoutes(next.pathname, next.search, {
|
|
2164
|
-
debug: true
|
|
2165
|
-
});
|
|
2191
|
+
let pendingMatches;
|
|
2166
2192
|
const previousMatches = this.state.matches;
|
|
2193
|
+
this.__store.batch(() => {
|
|
2194
|
+
this.__store.setState(s => ({
|
|
2195
|
+
...s,
|
|
2196
|
+
preloadMatches: s.preloadMatches.filter(d => {
|
|
2197
|
+
return Date.now() - d.updatedAt < (this.options.defaultPreloadMaxAge ?? 3000);
|
|
2198
|
+
})
|
|
2199
|
+
}));
|
|
2167
2200
|
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2201
|
+
// Match the routes
|
|
2202
|
+
pendingMatches = this.matchRoutes(next.pathname, next.search, {
|
|
2203
|
+
debug: true
|
|
2204
|
+
});
|
|
2205
|
+
|
|
2206
|
+
// Ingest the new matches
|
|
2207
|
+
this.__store.setState(s => ({
|
|
2208
|
+
...s,
|
|
2209
|
+
isLoading: true,
|
|
2210
|
+
location: next,
|
|
2211
|
+
pendingMatches,
|
|
2212
|
+
preloadMatches: s.preloadMatches.filter(d => {
|
|
2213
|
+
return !pendingMatches.find(e => e.id === d.id);
|
|
2214
|
+
})
|
|
2215
|
+
}));
|
|
2216
|
+
});
|
|
2175
2217
|
try {
|
|
2176
2218
|
try {
|
|
2177
2219
|
// Load the matches
|
|
@@ -2229,6 +2271,17 @@ class Router {
|
|
|
2229
2271
|
let matches = this.matchRoutes(next.pathname, next.search, {
|
|
2230
2272
|
throwOnError: true
|
|
2231
2273
|
});
|
|
2274
|
+
const loadedMatchIds = Object.fromEntries([...this.state.matches, ...(this.state.pendingMatches ?? []), ...this.state.preloadMatches]?.map(d => [d.id, true]));
|
|
2275
|
+
this.__store.batch(() => {
|
|
2276
|
+
matches.forEach(match => {
|
|
2277
|
+
if (!loadedMatchIds[match.id]) {
|
|
2278
|
+
this.__store.setState(s => ({
|
|
2279
|
+
...s,
|
|
2280
|
+
preloadMatches: [...s.preloadMatches, match]
|
|
2281
|
+
}));
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
});
|
|
2232
2285
|
matches = await this.loadMatches({
|
|
2233
2286
|
matches,
|
|
2234
2287
|
preload: true,
|
|
@@ -2291,7 +2344,7 @@ class Router {
|
|
|
2291
2344
|
dehydrate = () => {
|
|
2292
2345
|
return {
|
|
2293
2346
|
state: {
|
|
2294
|
-
dehydratedMatches: this.state.matches.map(d => pick(d, ['
|
|
2347
|
+
dehydratedMatches: this.state.matches.map(d => pick(d, ['id', 'status', 'updatedAt', 'loaderData']))
|
|
2295
2348
|
}
|
|
2296
2349
|
};
|
|
2297
2350
|
};
|
|
@@ -2354,6 +2407,7 @@ function getInitialRouterState(location) {
|
|
|
2354
2407
|
location,
|
|
2355
2408
|
matches: [],
|
|
2356
2409
|
pendingMatches: [],
|
|
2410
|
+
preloadMatches: [],
|
|
2357
2411
|
lastUpdated: Date.now()
|
|
2358
2412
|
};
|
|
2359
2413
|
}
|