Commit graph

10 commits

Author SHA1 Message Date
Zack Tanner
86cbc1f526
add experimental client router cache config (#62856)
This introduces an experimental router flag (`experimental.staleTimes`)
to change the router cache behavior. Specifically:

```ts
// next.config.js
module.exports = {
  experimental: {
    staleTimes: {
      dynamic: <seconds>,
      static: <seconds>,
    },
  },
};
```

- `dynamic` is the value that is used when the `prefetch` `Link` prop is
left unspecified. (Default 30 seconds)
- `static` is the value that is used when the `prefetch` `Link` prop is
`true`. (Default 5 minutes)

Additional details:
- Loading boundaries are considered reusable for the time period
indicated by the `static` property (default 5 minutes)
- This doesn't disable partial rendering support, **meaning shared
layouts won't automatically be refetched every navigation, only the new
segment data**.
- This also doesn't change back/forward caching behavior to ensure
things like scroll restoration etc still work nicely.

Please see the original proposal
[here](https://github.com/vercel/next.js/discussions/54075#discussioncomment-6754339)
for more information. The primary difference is that this is a global
configuration, rather than a per-segment configuration, and it isn't
applied to layouts. (We expect this to be a "stop-gap" and not the final
router caching solution)

Closes NEXT-2703
2024-04-02 05:42:18 -07:00
Zack Tanner
7d20ba17b8
Fix instant loading states after invalidating prefetch cache (#63256)
In #61573, I updated the navigation reducer to request a new prefetch
entry if it's stale. But this has the unintended consequence of making
instant loading states effectively useless after 30s (when the prefetch
would have expired). Blocking navigation and then rendering the loading
state isn't ideal - if we have some loading data in a cache node, we
should re-use it.

Now that #62346 stores loading data in the `CacheNode`, we can copy over
`loading` during a navigation.

This PR repurposes `fillCacheWithDataProperty` which wasn't being used
anywhere, to instead be a utility we can use to programmatically trigger
a lazy fetch on a particular segment path by nulling out it's data while
copying over other properties. We could have used the existing util
as-is, but ideally we only have a single spot where lazy fetching can
happen, which currently is in `LayoutRouter`.

When a stale prefetch entry is detected, rather than applying the data
to the tree, this PR will copy over the `loading` nodes and will
"delete" the data so it can be refetched.

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Adding or Updating Examples

- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->


Closes NEXT-2806
2024-03-19 12:02:12 -07:00
Zack Tanner
5e3bac5c1e
Store loading data in CacheNode (#62346)
This changes how loading components are provided to `LayoutRouter` to instead be part of the `CacheNode`. This makes it so that we can copy it over/expire it in a similar way to the `data` property on a `CacheNode`. Consequently, this fixes a bug in PPR navigations, where 2 different loading states (the prefetched one, followed by the updated one) might be displayed upon navigating. 

Note: This disables a PPR test that asserts the loading data gets expired after 5 minutes. As implemented, the loading data will remain cached for a particular segment. 

<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the PR.
- Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide

### Adding or Updating Examples

- The "examples guidelines" are followed from our contributing doc https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to understand the PR)
- When linking to a Slack thread, you might want to share details of the conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic behind a change

### What?

### Why?

### How?

Closes NEXT-
Fixes #

-->


Closes NEXT-2557
2024-03-14 16:32:51 -07:00
Zack Tanner
d371f648d2
Renew prefetch cache entry after update from server (#61573)
### What
When a prefetch cache entry becomes "stale", it'll remain stale until
eventually it gets evicted. However during that stale window, the cache
is never revalidated, and so instant navigations stop working and data
is fetched from origin every navigation.

### Why
The `lastUsedTime` entry on the prefetch cache is currently only updated
after the first read. Once it becomes stale, `applyFlightData`'s
recursive functions will see that there is no longer a reusable prefetch
cache entry, and will trigger a lazy fetch of the new data on every
subsequent navigation.

### How
This updates prefetch cache handling to always ensure we’re using a
fresh or reusable prefetch cache entry. This means that stale prefetch
entries will be refreshed on a navigation event. As a result of this,
I’ve had to disable one of our tests that relies on this stale cache
behavior. It’s not ideal that we’re blocked on the loading boundary when
fetching child segment data—ideally we can refactor this to cache the
loading component in the CacheNode and copy it over on navigations,
similar to how ‘head’ is handled. I’ll work on this in a separate PR.

Note: The new client cache test for this is disabled in PPR for the same
reason as the other tests: auto prefetching with PPR navigations is
currently loading fresh data rather than reusing the prefetch cache.


Fixes #58969
Fixes #58723
Closes NEXT-1904
2024-02-20 09:07:18 -08:00
Wyatt Johnson
252d9cf453
Navigation Signals in PPR (#60450)
### What

This adds support for navigation signals like `notFound()` and
`redirect(url)` when Partial Prerendering has been enabled.

### Why

Navigation API's like `notFound()` and `redirect(url)` throw errors in
order to interrupt the rendering of components. When a page both invokes
API's that cause the render to be marked as dynamic (like
`unstable_noStore()`) and also a navigation API, these errors may race
to the end. In the case where the navigation error does not beat out the
error emitted by dynamic API's will still trigger the detection warning
that's present to warn you about situations where you may have
accidentally caught the error.

### How

This resolves this issue by explicitly checking for navigation signals
(errors) thrown during the render, and not displaying the "caught
dynamic API" error and console warning.

Closes NEXT-2037
2024-02-06 12:25:50 -07:00
Zack Tanner
db468e399b
ppr: ensure the router state tree is provided for interception routes (#61059)
### What?
Interception routes on dynamic paths don't currently work when PPR is
turned on, and instead trigger an MPA navigation to the full route.

### Why?
Route interception relies on the `Next-Flight-Router-State` header to
extract the dynamic params necessary to convert something like
`[locale]`, which would correspond with a URL of `/en`, to something
like `["locale", "en", "d"]`. (See [this
function](fbfd7b5f81/packages/next/src/server/app-render/app-render.tsx (L129-L137))
for more info). When PPR is turned on, we currently ignore this request
header and don't provide it to the functions that render the component
tree.

Without the flight router state, this function bails out and instead
will leave the segment key as `[locale]`. When the client router goes to
diff the current tree on the client with the response from the server,
it will detect a mismatch between the current segment and the incoming
segment, and trigger an MPA navigation in [this
block](fbfd7b5f81/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts (L414-L416)).

### How?
This special-cases interception routes to not strip this header.

Closes NEXT-2215
2024-01-24 10:20:47 -08:00
Zack Tanner
1481b2649f
Fix TypeError when using params in RootLayout with parallel routes (#60401)
### What?
When accessing `params` on a `RootLayout`, while also using parallel
routes, two potential errors would occur:
- A `Warning: React.createElement: type is invalid` error when
attempting to render a `NotFound` component that doesn't exist
- A `TypeError: Cannot read properties of undefined` error when
attempting to access params in the root layout.

### Why?
`createComponentTree` will render a duplicate `RootLayout` (to ensure
the `notFound()` fallback in unmatched parallel slots have a
`NotFoundBoundary` to catch them) but it currently doesn't ensure a
`NotFound` component exists nor does it forward `params` to the layout.

### How?
This forwards the params to the `RootLayout` and doesn't render a
`NotFoundComponent` if one doesn't exist. This replaces a few `any`
types with more sound types that would have helped catch these mistakes.
There's still a lot more typing that needs to be done (left a comment
below with some additional details) but I opted to make the minimal
changes related to this issue.

Longer term we should remove this duplicate `RootLayout` (see #60220)
which will require special UI to show unmatched slots (similar to the
error overlay, but less harsh)

Closes NEXT-1909
Fixes #59711
2024-01-09 07:06:24 -08:00
Wyatt Johnson
2da04af2e8
Partial Pre Rendering Headers (#59447)
This fixes some of headers (and adds associated tests) for pages when
PPR is enabled. Namely, the `Cache-Control` headers are now returning
correctly, reflecting the non-cachability of some requests:

- Requests that postpone (dynamic data is streamed after the initial
static shell is streamed)
- Requests for the Dynamic RSC payload

Additionally, the `X-NextJS-Cache` header has been updated for better
support for PPR:

- Requests that postpone no longer return this header as it doesn't
reflect the cache state of the request (because it streams)
- Requests for the Prefetch RSC now returns the correct cache headers
depending on the segment and pre-postpone state

This also enables the other pathnames in the test suites 🙌🏻 

Closes NEXT-1840
2023-12-14 13:14:06 -07:00
Wyatt Johnson
eab1fe8397
Enable PPR for dynamic = "force-dynamic" (#58779)
This makes some critical modifications to the app render pipeline when
PPR has been enabled for pages with segments defining:

```js
export const dynamic = "force-dynamic"
```

Importantly, it no longer modifies the revalidation time to zero for
those pages, and now falls back to the provided default revalidation
time. When static render occurs, if the page being rendered has a
segment config defining `dynamic === "force-dynamic"`, then it will
postpone at the root of the component tree. This ensures that no render
code is executed for the page, as the entirety of the tree will have
postponed. This fixes the bug where the flight prefetch wasn't generated
correctly as well.
2023-12-06 01:10:00 +00:00
Zack Tanner
809164d776
Enable PPR tests for test suites (#59030)
Cherry-picks #58708 without the dependency on https://github.com/vercel/next.js/pull/58779

Co-authored-by: Wyatt Johnson <633002+wyattjoh@users.noreply.github.com>
2023-11-29 03:22:45 +00:00