Skip to content

Commit

Permalink
Use throw to trigger Suspense to avoid use() in reducer warning (verc…
Browse files Browse the repository at this point in the history
…el#40950)

This is a temporarily fix as discussed with @sebmarkbage. It will be superseded by async reducer functions once that's added.




## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
  • Loading branch information
timneutkens authored and BowlingX committed Oct 5, 2022
1 parent 8796688 commit 2969c59
Showing 1 changed file with 47 additions and 10 deletions.
57 changes: 47 additions & 10 deletions packages/next/client/components/reducer.ts
Expand Up @@ -7,10 +7,43 @@ import type {
Segment,
} from '../../server/app-render'
// TODO-APP: change to React.use once it becomes stable
import { experimental_use as use } from 'react'
import { matchSegment } from './match-segments'
import { fetchServerResponse } from './app-router.client'

/**
* Create data fetching record for Promise.
*/
// TODO-APP: change `any` to type inference.
function createRecordFromThenable(thenable: any) {
thenable.status = 'pending'
thenable.then(
(value: any) => {
if (thenable.status === 'pending') {
thenable.status = 'fulfilled'
thenable.value = value
}
},
(err: any) => {
if (thenable.status === 'pending') {
thenable.status = 'rejected'
thenable.value = err
}
}
)
return thenable
}

/**
* Read record value or throw Promise if it's not resolved yet.
*/
function readRecordValue(thenable: any) {
if (thenable.status === 'fulfilled') {
return thenable.value
} else {
throw thenable
}
}

function createHrefFromUrl(url: URL): string {
return url.pathname + url.search + url.hash
}
Expand Down Expand Up @@ -781,11 +814,13 @@ function clientReducer(

// If no in-flight fetch at the top, start it.
if (!cache.data) {
cache.data = fetchServerResponse(url, state.tree)
cache.data = createRecordFromThenable(
fetchServerResponse(url, state.tree)
)
}

// Unwrap cache data with `use` to suspend here (in the reducer) until the fetch resolves.
const [flightData, canonicalUrlOverride] = use(cache.data)
const [flightData, canonicalUrlOverride] = readRecordValue(cache.data)

// Handle case when navigating to page in `pages` from `app`
if (typeof flightData === 'string') {
Expand Down Expand Up @@ -993,14 +1028,16 @@ function clientReducer(

if (!cache.data) {
// Fetch data from the root of the tree.
cache.data = fetchServerResponse(new URL(href, location.origin), [
state.tree[0],
state.tree[1],
state.tree[2],
'refetch',
])
cache.data = createRecordFromThenable(
fetchServerResponse(new URL(href, location.origin), [
state.tree[0],
state.tree[1],
state.tree[2],
'refetch',
])
)
}
const [flightData, canonicalUrlOverride] = use(cache.data)
const [flightData, canonicalUrlOverride] = readRecordValue(cache.data)

// Handle case when navigating to page in `pages` from `app`
if (typeof flightData === 'string') {
Expand Down

0 comments on commit 2969c59

Please sign in to comment.