Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove optimistic navigation behavior when prefetch is false #58413

Merged
merged 14 commits into from
Nov 16, 2023

Conversation

ztanner
Copy link
Member

@ztanner ztanner commented Nov 14, 2023

What?

When navigating between pages (via prefetch: false) within a dynamic segment, the shared layout is re-rendered. This can cause unexpected behavior like layout data changing when navigating between child segments.

Why?

When prefetch is false, we're currently opting into an "optimistic navigation" codepath, which will optimistically render layout-routers up to the point where data is missing, while kicking off data fetches. It attempts to determine where refetching needs to happen by traversing the router cache nodes and checking where data is missing. However, it locates these cache nodes by using "segments" obtained by deconstructing the URL, which won't accurately contain dynamic segment data. For ex, /en which corresponds with /app/[lang]/page.tsx will have a cache node key of lang|en|d, not en. Similarly, the optimistic tree that gets constructed will also be incorrect, since it uses the URL segment.

How?

My initial fix was to match the dynamic segment against the segment constructed by the URL. But after discussion with @sebmarkbage and the team, it seems more correct to remove the optimistic case all together as there's no guarantee that the url will actually match to that segment.

Fixes #50670

@ztanner ztanner changed the title fix optimistic bailout for dynamic segments fix optimistic navigation behavior between a shared dynamic segment Nov 14, 2023
@ztanner ztanner marked this pull request as ready for review November 14, 2023 03:56
@ztanner ztanner requested a review from a team as a code owner November 15, 2023 01:28
@ijjk
Copy link
Member

ijjk commented Nov 15, 2023

Stats from current PR

Default Build
General
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
buildDuration 10.9s 11.1s ⚠️ +203ms
buildDurationCached 6.3s 6.3s N/A
nodeModulesSize 199 MB 199 MB N/A
nextStartRea..uration (ms) 403ms 406ms N/A
Client Bundles (main, webpack)
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
199-HASH.js gzip 29.2 kB 28.8 kB N/A
3f784ff6-HASH.js gzip 53.3 kB 53.3 kB N/A
494.HASH.js gzip 180 B 181 B N/A
framework-HASH.js gzip 45.2 kB 45.2 kB
main-app-HASH.js gzip 241 B 240 B N/A
main-HASH.js gzip 31.7 kB 31.8 kB N/A
webpack-HASH.js gzip 1.7 kB 1.7 kB
Overall change 46.9 kB 46.9 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
_app-HASH.js gzip 194 B 195 B N/A
_error-HASH.js gzip 182 B 181 B N/A
amp-HASH.js gzip 504 B 506 B N/A
css-HASH.js gzip 322 B 323 B N/A
dynamic-HASH.js gzip 2.5 kB 2.5 kB
edge-ssr-HASH.js gzip 253 B 255 B N/A
head-HASH.js gzip 348 B 347 B N/A
hooks-HASH.js gzip 369 B 368 B N/A
image-HASH.js gzip 4.3 kB 4.3 kB N/A
index-HASH.js gzip 256 B 256 B
link-HASH.js gzip 2.65 kB 2.63 kB N/A
routerDirect..HASH.js gzip 311 B 311 B
script-HASH.js gzip 384 B 383 B N/A
withRouter-HASH.js gzip 307 B 308 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 3.17 kB 3.17 kB
Client Build Manifests
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
_buildManifest.js gzip 486 B 483 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
index.html gzip 528 B 526 B N/A
link.html gzip 541 B 541 B
withRouter.html gzip 524 B 522 B N/A
Overall change 541 B 541 B
Edge SSR bundle Size
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
edge-ssr.js gzip 92.6 kB 92.6 kB N/A
page.js gzip 145 kB 145 kB N/A
Overall change 0 B 0 B
Middleware size
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
middleware-b..fest.js gzip 625 B 625 B
middleware-r..fest.js gzip 150 B 151 B N/A
middleware.js gzip 24.8 kB 24.8 kB N/A
edge-runtime..pack.js gzip 1.92 kB 1.92 kB
Overall change 2.55 kB 2.55 kB
Next Runtimes
vercel/next.js canary vercel/next.js fix/optimistic-bailout Change
app-page-exp...dev.js gzip 167 kB 167 kB
app-page-exp..prod.js gzip 93.2 kB 93.2 kB
app-page-tur..prod.js gzip 93.9 kB 93.9 kB
app-page-tur..prod.js gzip 88.5 kB 88.5 kB
app-page.run...dev.js gzip 137 kB 137 kB
app-page.run..prod.js gzip 87.9 kB 87.9 kB
app-route-ex...dev.js gzip 23.8 kB 23.8 kB
app-route-ex..prod.js gzip 16.4 kB 16.4 kB
app-route-tu..prod.js gzip 16.4 kB 16.4 kB
app-route-tu..prod.js gzip 16 kB 16 kB
app-route.ru...dev.js gzip 23.2 kB 23.2 kB
app-route.ru..prod.js gzip 16 kB 16 kB
pages-api-tu..prod.js gzip 9.37 kB 9.37 kB
pages-api.ru...dev.js gzip 9.64 kB 9.64 kB
pages-api.ru..prod.js gzip 9.37 kB 9.37 kB
pages-turbo...prod.js gzip 21.8 kB 21.8 kB
pages.runtim...dev.js gzip 22.5 kB 22.5 kB
pages.runtim..prod.js gzip 21.8 kB 21.8 kB
server.runti..prod.js gzip 49 kB 49 kB
Overall change 923 kB 923 kB
Diff details
Diff for page.js

Diff too large to display

Diff for link-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [644],
   {
-    /***/ 1794: /***/ function (
+    /***/ 8959: /***/ function (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/link",
         function () {
-          return __webpack_require__(1921);
+          return __webpack_require__(9877);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 172: /***/ function (module, exports) {
+    /***/ 91: /***/ function (module, exports) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -100,7 +100,7 @@
       /***/
     },
 
-    /***/ 4847: /***/ function (module, exports, __webpack_require__) {
+    /***/ 2976: /***/ function (module, exports, __webpack_require__) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -112,7 +112,7 @@
           return getDomainLocale;
         },
       });
-      const _normalizetrailingslash = __webpack_require__(2003);
+      const _normalizetrailingslash = __webpack_require__(9076);
       const basePath =
         /* unused pure expression or super */ null && (false || "");
       function getDomainLocale(path, locale, locales, domainLocales) {
@@ -136,7 +136,7 @@
       /***/
     },
 
-    /***/ 7414: /***/ function (module, exports, __webpack_require__) {
+    /***/ 106: /***/ function (module, exports, __webpack_require__) {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -152,17 +152,17 @@
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(959)
       );
-      const _resolvehref = __webpack_require__(279);
-      const _islocalurl = __webpack_require__(7377);
-      const _formaturl = __webpack_require__(9616);
-      const _utils = __webpack_require__(3869);
-      const _addlocale = __webpack_require__(5752);
-      const _routercontextsharedruntime = __webpack_require__(1840);
-      const _approutercontextsharedruntime = __webpack_require__(3057);
-      const _useintersection = __webpack_require__(9285);
-      const _getdomainlocale = __webpack_require__(4847);
-      const _addbasepath = __webpack_require__(5853);
-      const _routerreducertypes = __webpack_require__(172);
+      const _resolvehref = __webpack_require__(8873);
+      const _islocalurl = __webpack_require__(1023);
+      const _formaturl = __webpack_require__(3482);
+      const _utils = __webpack_require__(4597);
+      const _addlocale = __webpack_require__(6845);
+      const _routercontextsharedruntime = __webpack_require__(1049);
+      const _approutercontextsharedruntime = __webpack_require__(4095);
+      const _useintersection = __webpack_require__(4991);
+      const _getdomainlocale = __webpack_require__(2976);
+      const _addbasepath = __webpack_require__(7677);
+      const _routerreducertypes = __webpack_require__(91);
       const prefetched = new Set();
       function prefetch(router, href, as, options, appOptions, isAppRouter) {
         if (false) {
@@ -221,8 +221,7 @@
         shallow,
         scroll,
         locale,
-        isAppRouter,
-        prefetchEnabled
+        isAppRouter
       ) {
         const { nodeName } = e.currentTarget;
         // anchors inside an svg have a lowercase nodeName
@@ -247,7 +246,6 @@
             });
           } else {
             router[replace ? "replace" : "push"](as || href, {
-              forceOptimisticNavigation: !prefetchEnabled,
               scroll: routerScroll,
             });
           }
@@ -443,8 +441,7 @@
                 shallow,
                 scroll,
                 locale,
-                isAppRouter,
-                prefetchEnabled
+                isAppRouter
               );
             },
             onMouseEnter(e) {
@@ -581,7 +578,7 @@
       /***/
     },
 
-    /***/ 9285: /***/ function (module, exports, __webpack_require__) {
+    /***/ 4991: /***/ function (module, exports, __webpack_require__) {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -594,7 +591,7 @@
         },
       });
       const _react = __webpack_require__(959);
-      const _requestidlecallback = __webpack_require__(5781);
+      const _requestidlecallback = __webpack_require__(3136);
       const hasIntersectionObserver =
         typeof IntersectionObserver === "function";
       const observers = new Map();
@@ -707,7 +704,7 @@
       /***/
     },
 
-    /***/ 1921: /***/ function (
+    /***/ 9877: /***/ function (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -723,7 +720,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1527);
       /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(3639);
+        __webpack_require__(2075);
       /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_link__WEBPACK_IMPORTED_MODULE_1__
@@ -754,12 +751,12 @@
       /***/
     },
 
-    /***/ 3639: /***/ function (
+    /***/ 2075: /***/ function (
       module,
       __unused_webpack_exports,
       __webpack_require__
     ) {
-      module.exports = __webpack_require__(7414);
+      module.exports = __webpack_require__(106);
 
       /***/
     },
@@ -770,7 +767,7 @@
       return __webpack_require__((__webpack_require__.s = moduleId));
     };
     /******/ __webpack_require__.O(0, [774, 888, 179], function () {
-      return __webpack_exec__(1794);
+      return __webpack_exec__(8959);
     });
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for 199-HASH.js

Diff too large to display

Commit: a8ba27c

@ztanner ztanner changed the title fix optimistic navigation behavior between a shared dynamic segment remove optimistic navigation behavior when prefetch is false Nov 15, 2023
…with-data-property.ts

Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
@timneutkens timneutkens merged commit 24b2ff1 into canary Nov 16, 2023
95 of 103 checks passed
@timneutkens timneutkens deleted the fix/optimistic-bailout branch November 16, 2023 10:58
@@ -69,7 +69,6 @@ export type CacheNode =

export interface NavigateOptions {
/** @internal */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should have been removed too, I've opened a PR here: #59001

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 12, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

next/link prefetch=false broken for dynamic routes
5 participants