Remove additional <div>
at each segment level in app
(#43717)
This commit is contained in:
parent
cbf87e1ebf
commit
5cff316aee
1 changed files with 62 additions and 29 deletions
|
@ -1,10 +1,4 @@
|
|||
'use client'
|
||||
|
||||
import React, { useContext, useEffect, useRef, use } from 'react'
|
||||
import type {
|
||||
ChildProp,
|
||||
//Segment
|
||||
} from '../../server/app-render'
|
||||
import type {
|
||||
AppRouterInstance,
|
||||
ChildSegmentMap,
|
||||
|
@ -12,9 +6,13 @@ import type {
|
|||
import type {
|
||||
FlightRouterState,
|
||||
FlightSegmentPath,
|
||||
// FlightDataPath,
|
||||
} from '../../server/app-render'
|
||||
import type { ErrorComponent } from './error-boundary'
|
||||
import type { FocusAndScrollRef } from './reducer'
|
||||
|
||||
import React, { useContext, useEffect, use } from 'react'
|
||||
import { findDOMNode as ReactDOMfindDOMNode } from 'react-dom'
|
||||
import type { ChildProp } from '../../server/app-render'
|
||||
import {
|
||||
CacheStates,
|
||||
LayoutRouterContext,
|
||||
|
@ -77,6 +75,31 @@ function walkAddRefetch(
|
|||
return treeToRecreate
|
||||
}
|
||||
|
||||
// TODO-APP: Replace with new React API for finding dom nodes without a `ref` when available
|
||||
/**
|
||||
* Wraps ReactDOM.findDOMNode with additional logic to hide React Strict Mode warning
|
||||
*/
|
||||
function findDOMNode(
|
||||
instance: Parameters<typeof ReactDOMfindDOMNode>[0]
|
||||
): ReturnType<typeof ReactDOMfindDOMNode> {
|
||||
// Only apply strict mode warning when not in production
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const originalConsoleError = console.error
|
||||
try {
|
||||
console.error = (...messages) => {
|
||||
// Ignore strict mode warning for the findDomNode call below
|
||||
if (!messages[0].includes('Warning: %s is deprecated in StrictMode.')) {
|
||||
originalConsoleError(...messages)
|
||||
}
|
||||
}
|
||||
return ReactDOMfindDOMNode(instance)
|
||||
} finally {
|
||||
console.error = originalConsoleError!
|
||||
}
|
||||
}
|
||||
return ReactDOMfindDOMNode(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the top of the HTMLElement is in the viewport.
|
||||
*/
|
||||
|
@ -85,6 +108,36 @@ function topOfElementInViewport(element: HTMLElement) {
|
|||
return rect.top >= 0
|
||||
}
|
||||
|
||||
class ScrollAndFocusHandler extends React.Component<{
|
||||
focusAndScrollRef: FocusAndScrollRef
|
||||
children: React.ReactNode
|
||||
}> {
|
||||
componentDidMount() {
|
||||
// Handle scroll and focus, it's only applied once in the first useEffect that triggers that changed.
|
||||
const { focusAndScrollRef } = this.props
|
||||
const domNode = findDOMNode(this)
|
||||
|
||||
if (focusAndScrollRef.apply && domNode instanceof HTMLElement) {
|
||||
// State is mutated to ensure that the focus and scroll is applied only once.
|
||||
focusAndScrollRef.apply = false
|
||||
// Set focus on the element
|
||||
domNode.focus()
|
||||
// Only scroll into viewport when the layout is not visible currently.
|
||||
if (!topOfElementInViewport(domNode)) {
|
||||
const htmlElement = document.documentElement
|
||||
const existing = htmlElement.style.scrollBehavior
|
||||
htmlElement.style.scrollBehavior = 'auto'
|
||||
domNode.scrollIntoView()
|
||||
htmlElement.style.scrollBehavior = existing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InnerLayoutRouter handles rendering the provided segment based on the cache.
|
||||
*/
|
||||
|
@ -117,26 +170,6 @@ export function InnerLayoutRouter({
|
|||
|
||||
const { changeByServerResponse, tree: fullTree, focusAndScrollRef } = context
|
||||
|
||||
const focusAndScrollElementRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
// Handle scroll and focus, it's only applied once in the first useEffect that triggers that changed.
|
||||
if (focusAndScrollRef.apply && focusAndScrollElementRef.current) {
|
||||
// State is mutated to ensure that the focus and scroll is applied only once.
|
||||
focusAndScrollRef.apply = false
|
||||
// Set focus on the element
|
||||
focusAndScrollElementRef.current.focus()
|
||||
// Only scroll into viewport when the layout is not visible currently.
|
||||
if (!topOfElementInViewport(focusAndScrollElementRef.current)) {
|
||||
const htmlElement = document.documentElement
|
||||
const existing = htmlElement.style.scrollBehavior
|
||||
htmlElement.style.scrollBehavior = 'auto'
|
||||
focusAndScrollElementRef.current.scrollIntoView()
|
||||
htmlElement.style.scrollBehavior = existing
|
||||
}
|
||||
}
|
||||
}, [focusAndScrollRef])
|
||||
|
||||
// Read segment path from the parallel router cache node.
|
||||
let childNode = childNodes.get(path)
|
||||
|
||||
|
@ -257,9 +290,9 @@ export function InnerLayoutRouter({
|
|||
|
||||
// Ensure root layout is not wrapped in a div as the root layout renders `<html>`
|
||||
return rootLayoutIncluded ? (
|
||||
<div ref={focusAndScrollElementRef} data-nextjs-scroll-focus-boundary={''}>
|
||||
<ScrollAndFocusHandler focusAndScrollRef={focusAndScrollRef}>
|
||||
{subtree}
|
||||
</div>
|
||||
</ScrollAndFocusHandler>
|
||||
) : (
|
||||
subtree
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue