Files
Andrew ClarkandGitHub 61803f818a [PPR Nav] Fix flash of loading state during back/forward (#60578)
### Depends on

- #60577 

---

A popstate navigation reads data from the local cache. It does not issue
new network requests (unless the cache entries have been evicted). So,
when navigating with back/forward, we should not switch back to the PPR
loading state. We should render the full, cached dynamic data
immediately.

To implement this, on a popstate navigation, we update the cache to drop
the prefetch data for any segment whose dynamic data was already
received. We clone the entire cache node tree and set the `prefetchRsc`
field to `null` to prevent it from being rendered. (We can't mutate the
node in place because Cache Node is a concurrent data structure.)

Technically, what we're actually checking is whether the dynamic network
response was received. But since it's a streaming response, this does
not mean that all the dynamic data has fully streamed in. It just means
that _some_ of the dynamic data was received. But as a heuristic, we
assume that the rest dynamic data will stream in quickly, so it's still
better to skip the prefetch state.

Closes NEXT-2084
2024-01-12 14:18:54 -05:00

46 lines
1.2 KiB
TypeScript

'use client'
import { Component, createContext, use, useState } from 'react'
const ShouldFallbackThrowContext = createContext(false)
export function ShouldFallbackThrowContainer({ children }) {
const [shouldFallbackThrow, setShouldFallbackThrow] = useState(false)
return (
<>
<label>
Throw if fallback appears
<input
id="should-fallback-throw"
type="checkbox"
checked={shouldFallbackThrow}
onChange={(e) => setShouldFallbackThrow(e.target.checked)}
/>
</label>
<ShouldFallbackThrowContext.Provider value={shouldFallbackThrow}>
<ErrorBoundary>{children}</ErrorBoundary>
</ShouldFallbackThrowContext.Provider>
</>
)
}
export function Fallback({ children }) {
if (use(ShouldFallbackThrowContext)) {
throw new Error('Unexpected fallback')
}
return children
}
class ErrorBoundary extends Component<{ children: React.ReactNode }> {
state = { error: null }
static getDerivedStateFromError(error) {
return { error }
}
render() {
if (this.state.error) {
return <div id="error">{this.state.error.message}</div>
}
return this.props.children
}
}