Skip to content

Commit

Permalink
refactor(theme): move LayoutProviders to Layout/Provider; composeProv…
Browse files Browse the repository at this point in the history
…iders util (#7676)

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
  • Loading branch information
slorber and Josh-Cena committed Jun 24, 2022
1 parent 90a8ca3 commit afc08ee
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 44 deletions.
4 changes: 2 additions & 2 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Expand Up @@ -596,14 +596,14 @@ declare module '@theme/Layout' {
export default function Layout(props: Props): JSX.Element;
}

declare module '@theme/LayoutProviders' {
declare module '@theme/Layout/Provider' {
import type {ReactNode} from 'react';

export interface Props {
readonly children: ReactNode;
}

export default function LayoutProviders(props: Props): JSX.Element;
export default function LayoutProvider(props: Props): JSX.Element;
}

declare module '@theme/SearchMetadata' {
Expand Down
@@ -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>;
}
6 changes: 3 additions & 3 deletions packages/docusaurus-theme-classic/src/theme/Layout/index.tsx
Expand Up @@ -14,7 +14,7 @@ import SkipToContent from '@theme/SkipToContent';
import AnnouncementBar from '@theme/AnnouncementBar';
import Navbar from '@theme/Navbar';
import Footer from '@theme/Footer';
import LayoutProviders from '@theme/LayoutProviders';
import LayoutProvider from '@theme/Layout/Provider';
import ErrorPageContent from '@theme/ErrorPageContent';
import type {Props} from '@theme/Layout';
import styles from './styles.module.css';
Expand All @@ -32,7 +32,7 @@ export default function Layout(props: Props): JSX.Element {
useKeyboardNavigation();

return (
<LayoutProviders>
<LayoutProvider>
<PageMetadata title={title} description={description} />

<SkipToContent />
Expand All @@ -53,6 +53,6 @@ export default function Layout(props: Props): JSX.Element {
</div>

{!noFooter && <Footer />}
</LayoutProviders>
</LayoutProvider>
);
}

This file was deleted.

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

function useDocsPreferredVersionContext(): ContextValue {
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-theme-common/src/index.ts
Expand Up @@ -40,6 +40,7 @@ export {
useIsomorphicLayoutEffect,
useEvent,
usePrevious,
composeProviders,
ReactContextError,
} from './utils/reactUtils';

Expand Down
39 changes: 38 additions & 1 deletion packages/docusaurus-theme-common/src/utils/reactUtils.tsx
Expand Up @@ -5,7 +5,15 @@
* 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';

/**
Expand Down Expand Up @@ -90,3 +98,32 @@ export function useShallowMemoObject<O extends object>(obj: O): O {
// eslint-disable-next-line react-hooks/exhaustive-deps
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,
)}
</>
);
}

0 comments on commit afc08ee

Please sign in to comment.