refactor(theme): move LayoutProviders to Layout/Provider; composeProviders util (#7676)

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
This commit is contained in:
Sébastien Lorber 2022-06-24 18:36:27 +02:00 committed by GitHub
parent 90a8ca387e
commit afc08eef4f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 44 deletions

View file

@ -596,14 +596,14 @@ declare module '@theme/Layout' {
export default function Layout(props: Props): JSX.Element; export default function Layout(props: Props): JSX.Element;
} }
declare module '@theme/LayoutProviders' { declare module '@theme/Layout/Provider' {
import type {ReactNode} from 'react'; import type {ReactNode} from 'react';
export interface Props { export interface Props {
readonly children: ReactNode; readonly children: ReactNode;
} }
export default function LayoutProviders(props: Props): JSX.Element; export default function LayoutProvider(props: Props): JSX.Element;
} }
declare module '@theme/SearchMetadata' { declare module '@theme/SearchMetadata' {

View file

@ -0,0 +1,33 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import {composeProviders} from '@docusaurus/theme-common';
import {
ColorModeProvider,
TabGroupChoiceProvider,
AnnouncementBarProvider,
DocsPreferredVersionContextProvider,
ScrollControllerProvider,
NavbarProvider,
PluginHtmlClassNameProvider,
} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/Layout/Provider';
const Provider = composeProviders([
ColorModeProvider,
AnnouncementBarProvider,
TabGroupChoiceProvider,
ScrollControllerProvider,
DocsPreferredVersionContextProvider,
PluginHtmlClassNameProvider,
NavbarProvider,
]);
export default function LayoutProvider({children}: Props): JSX.Element {
return <Provider>{children}</Provider>;
}

View file

@ -14,7 +14,7 @@ import SkipToContent from '@theme/SkipToContent';
import AnnouncementBar from '@theme/AnnouncementBar'; import AnnouncementBar from '@theme/AnnouncementBar';
import Navbar from '@theme/Navbar'; import Navbar from '@theme/Navbar';
import Footer from '@theme/Footer'; import Footer from '@theme/Footer';
import LayoutProviders from '@theme/LayoutProviders'; import LayoutProvider from '@theme/Layout/Provider';
import ErrorPageContent from '@theme/ErrorPageContent'; import ErrorPageContent from '@theme/ErrorPageContent';
import type {Props} from '@theme/Layout'; import type {Props} from '@theme/Layout';
import styles from './styles.module.css'; import styles from './styles.module.css';
@ -32,7 +32,7 @@ export default function Layout(props: Props): JSX.Element {
useKeyboardNavigation(); useKeyboardNavigation();
return ( return (
<LayoutProviders> <LayoutProvider>
<PageMetadata title={title} description={description} /> <PageMetadata title={title} description={description} />
<SkipToContent /> <SkipToContent />
@ -53,6 +53,6 @@ export default function Layout(props: Props): JSX.Element {
</div> </div>
{!noFooter && <Footer />} {!noFooter && <Footer />}
</LayoutProviders> </LayoutProvider>
); );
} }

View file

@ -1,36 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import {
ColorModeProvider,
TabGroupChoiceProvider,
AnnouncementBarProvider,
DocsPreferredVersionContextProvider,
ScrollControllerProvider,
NavbarProvider,
PluginHtmlClassNameProvider,
} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/LayoutProviders';
export default function LayoutProviders({children}: Props): JSX.Element {
return (
<ColorModeProvider>
<AnnouncementBarProvider>
<TabGroupChoiceProvider>
<ScrollControllerProvider>
<DocsPreferredVersionContextProvider>
<PluginHtmlClassNameProvider>
<NavbarProvider>{children}</NavbarProvider>
</PluginHtmlClassNameProvider>
</DocsPreferredVersionContextProvider>
</ScrollControllerProvider>
</TabGroupChoiceProvider>
</AnnouncementBarProvider>
</ColorModeProvider>
);
}

View file

@ -175,7 +175,7 @@ function DocsPreferredVersionContextProviderUnsafe({
export function DocsPreferredVersionContextProvider({ export function DocsPreferredVersionContextProvider({
children, children,
}: { }: {
children: JSX.Element; children: ReactNode;
}): JSX.Element { }): JSX.Element {
if (isDocsPluginEnabled) { if (isDocsPluginEnabled) {
return ( return (
@ -184,7 +184,7 @@ export function DocsPreferredVersionContextProvider({
</DocsPreferredVersionContextProviderUnsafe> </DocsPreferredVersionContextProviderUnsafe>
); );
} }
return children; return <>{children}</>;
} }
function useDocsPreferredVersionContext(): ContextValue { function useDocsPreferredVersionContext(): ContextValue {

View file

@ -40,6 +40,7 @@ export {
useIsomorphicLayoutEffect, useIsomorphicLayoutEffect,
useEvent, useEvent,
usePrevious, usePrevious,
composeProviders,
ReactContextError, ReactContextError,
} from './utils/reactUtils'; } from './utils/reactUtils';

View file

@ -5,7 +5,15 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {useCallback, useEffect, useLayoutEffect, useMemo, useRef} from 'react'; import React, {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
type ComponentType,
type ReactNode,
} from 'react';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
/** /**
@ -90,3 +98,32 @@ export function useShallowMemoObject<O extends object>(obj: O): O {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(() => obj, deps.flat()); return useMemo(() => obj, deps.flat());
} }
type SimpleProvider = ComponentType<{children: ReactNode}>;
/**
* Creates a single React provider from an array of existing providers
* assuming providers only take "children" as props.
*
* Prevents the annoying React element nesting
* Example here: https://getfrontend.tips/compose-multiple-react-providers/
*
* The order matters:
* - The first provider is at the top of the tree.
* - The last provider is the most nested one
*
* @param providers array of providers to compose
*/
export function composeProviders(providers: SimpleProvider[]): SimpleProvider {
// Creates a single React component: it's cheaper to compose JSX elements
return ({children}) => (
<>
{providers.reduceRight(
(element, CurrentProvider) => (
<CurrentProvider>{element}</CurrentProvider>
),
children,
)}
</>
);
}