Fix transition fix
parent
d939b341a3
commit
ea611b1287
@ -1,47 +1,69 @@
|
||||
import Router from 'next/router'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
type Cleanup = () => void
|
||||
|
||||
export default function useTransitionFix(): Cleanup{
|
||||
const cleanupRef = useRef<Cleanup>(() => {})
|
||||
|
||||
useEffect(() => {
|
||||
const changeListener = () => {
|
||||
// Create a clone of every <style> and <link> that currently affects the page. It doesn't
|
||||
// matter if Next.js is going to remove them or not since we are going to remove the copies
|
||||
// ourselves later on when the transition finishes.
|
||||
const nodes = document.querySelectorAll('link[rel=stylesheet], style:not([media=x])')
|
||||
const copies = [...nodes].map((el) => el.cloneNode(true) as HTMLElement)
|
||||
|
||||
for (let copy of copies) {
|
||||
// Remove Next.js' data attributes so the copies are not removed from the DOM in the route
|
||||
// change process.
|
||||
copy.removeAttribute('data-n-p')
|
||||
copy.removeAttribute('data-n-href')
|
||||
|
||||
// Add duplicated nodes to the DOM.
|
||||
document.head.appendChild(copy)
|
||||
}
|
||||
|
||||
cleanupRef.current = () => {
|
||||
for (let copy of copies) {
|
||||
// Remove previous page's styles after the transition has finalized.
|
||||
document.head.removeChild(copy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Router.events.on('beforeHistoryChange', changeListener)
|
||||
|
||||
return () => {
|
||||
Router.events.off('beforeHistoryChange', changeListener)
|
||||
cleanupRef.current()
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Return an fixed reference function that calls the internal cleanup reference.
|
||||
return useCallback(() => {
|
||||
cleanupRef.current()
|
||||
}, [])
|
||||
}
|
||||
import { useEffect } from 'react';
|
||||
|
||||
// Temporary fix to avoid flash of unstyled content (FOUC) during route transitions.
|
||||
// Keep an eye on this issue and remove this code when resolved: https://github.com/vercel/next.js/issues/17464
|
||||
const useFoucFix = () => useEffect(() => {
|
||||
// Gather all server-side rendered stylesheet entries.
|
||||
let ssrPageStyleSheetsEntries = Array
|
||||
.from(document.querySelectorAll('link[rel="stylesheet"][data-n-p]'))
|
||||
.map((element) => ({
|
||||
element,
|
||||
href: element.getAttribute('href'),
|
||||
}));
|
||||
|
||||
// Remove the `data-n-p` attribute to prevent Next.js from removing it early.
|
||||
ssrPageStyleSheetsEntries.forEach(({ element }) => element.removeAttribute('data-n-p'));
|
||||
|
||||
const fixedStyleHrefs = [];
|
||||
|
||||
const mutationHandler = (mutations) => {
|
||||
// Gather all <style data-n-href="/..."> elements.
|
||||
const newStyleEntries = mutations
|
||||
.filter(({ target }) => target.nodeName === 'STYLE' && target.hasAttribute('data-n-href'))
|
||||
.map(({ target }) => ({
|
||||
element: target,
|
||||
href: target.getAttribute('data-n-href'),
|
||||
}));
|
||||
|
||||
// Cycle through them and either:
|
||||
// - Remove the `data-n-href` attribute to prevent Next.js from removing it early.
|
||||
// - Remove the element if it's already present.
|
||||
newStyleEntries.forEach(({ element, href }) => {
|
||||
const styleExists = fixedStyleHrefs.includes(href);
|
||||
|
||||
if (styleExists) {
|
||||
element.remove();
|
||||
} else {
|
||||
element.setAttribute('data-fouc-fix-n-href', href);
|
||||
element.removeAttribute('data-n-href');
|
||||
fixedStyleHrefs.push(href);
|
||||
}
|
||||
});
|
||||
|
||||
// Cycle through the server-side rendered stylesheets and remove the ones that
|
||||
// are already present as inline <style> tags added by Next.js, so that we don't have duplicate styles.
|
||||
ssrPageStyleSheetsEntries = ssrPageStyleSheetsEntries.reduce((entries, entry) => {
|
||||
const { element, href } = entry;
|
||||
const styleExists = fixedStyleHrefs.includes(href);
|
||||
|
||||
if (styleExists) {
|
||||
element.remove();
|
||||
} else {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
return entries;
|
||||
}, []);
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(mutationHandler);
|
||||
|
||||
observer.observe(document.head, {
|
||||
subtree: true,
|
||||
attributeFilter: ['media'],
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
export default useFoucFix;
|
Loading…
Reference in New Issue