perf(v2): lazy collapsibles, reduce html output and build times (#5136)

* Allow collapsible to be lazy

* cleanup comments

* CollapsibleLazy little bug
This commit is contained in:
Sébastien Lorber 2021-07-14 16:18:52 +02:00 committed by GitHub
parent 04bcdbd3e1
commit 677e53d4db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 10 deletions

View file

@ -141,7 +141,7 @@ function DocSidebarItemCategory({
{label} {label}
</a> </a>
<Collapsible as="ul" className="menu__list" collapsed={collapsed}> <Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
<DocSidebarItems <DocSidebarItems
items={items} items={items}
tabIndex={collapsed ? -1 : 0} tabIndex={collapsed ? -1 : 0}

View file

@ -209,7 +209,8 @@ function NavItemMobile({
}}> }}>
{props.children ?? props.label} {props.children ?? props.label}
</NavLink> </NavLink>
<Collapsible as="ul" className="menu__list" collapsed={collapsed}>
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
{items.map(({className: childItemClassName, ...childItemProps}, i) => ( {items.map(({className: childItemClassName, ...childItemProps}, i) => (
<li className="menu__list-item" key={i}> <li className="menu__list-item" key={i}>
<NavLink <NavLink

View file

@ -40,6 +40,7 @@ export default function TOCCollapsible({toc, className}: TOCCollapsibleProps) {
</button> </button>
<Collapsible <Collapsible
lazy
className={styles.tocCollapsibleContent} className={styles.tocCollapsibleContent}
collapsed={collapsed}> collapsed={collapsed}>
<TOCHeadings toc={toc} /> <TOCHeadings toc={toc} />

View file

@ -15,6 +15,7 @@ import React, {
Dispatch, Dispatch,
SetStateAction, SetStateAction,
ReactNode, ReactNode,
useLayoutEffect,
} from 'react'; } from 'react';
const DefaultAnimationEasing = 'ease-in-out'; const DefaultAnimationEasing = 'ease-in-out';
@ -158,19 +159,21 @@ function getSSRStyle(collapsed: boolean) {
return collapsed ? CollapsedStyles : ExpandedStyles; return collapsed ? CollapsedStyles : ExpandedStyles;
} }
export function Collapsible({ type CollapsibleBaseProps = {
as?: CollapsibleElementType;
collapsed: boolean;
children: ReactNode;
animation?: CollapsibleAnimationConfig;
className?: string;
};
function CollapsibleBase({
as: As = 'div', as: As = 'div',
collapsed, collapsed,
children, children,
animation, animation,
className, className,
}: { }: CollapsibleBaseProps) {
as?: CollapsibleElementType; // TODO better typing, allow any html element (keyof JSX.IntrinsicElement => not working)
collapsed: boolean;
children: ReactNode;
animation?: CollapsibleAnimationConfig;
className?: string;
}) {
// any because TS is a pain for HTML element refs, see https://twitter.com/sebastienlorber/status/1412784677795110914 // any because TS is a pain for HTML element refs, see https://twitter.com/sebastienlorber/status/1412784677795110914
const collapsibleRef = useRef<any>(null); const collapsibleRef = useRef<any>(null);
@ -205,3 +208,38 @@ export function Collapsible({
</As> </As>
); );
} }
function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
const [mounted, setMounted] = useState(!collapsed);
useLayoutEffect(() => {
if (!collapsed) {
setMounted(true);
}
}, [collapsed]);
// lazyCollapsed updated in effect so that the first expansion transition can work
const [lazyCollapsed, setLazyCollapsed] = useState(collapsed);
useLayoutEffect(() => {
if (mounted) {
setLazyCollapsed(collapsed);
}
}, [mounted, collapsed]);
return mounted ? (
<CollapsibleBase {...props} collapsed={lazyCollapsed} />
) : null;
}
type CollapsibleProps = CollapsibleBaseProps & {
// Lazy allows to delay the rendering when collapsed => it will render children only after hydration, on first expansion
// Required prop: it forces to think if content should be server-rendered or not!
// This has perf impact on the SSR output and html file sizes
// See https://github.com/facebook/docusaurus/issues/4753
lazy: boolean;
};
export function Collapsible({lazy, ...props}: CollapsibleProps) {
const Comp = lazy ? CollapsibleLazy : CollapsibleBase;
return <Comp {...props} />;
}