Просмотр исходного кода

add ResizableAreaFallback for SSR

Yuki Takei 1 год назад
Родитель
Сommit
f6b32e2ce5

+ 3 - 13
apps/app/src/components/Sidebar/ResizableArea/ResizableArea.tsx

@@ -1,21 +1,11 @@
-import React, { memo, useCallback, useRef } from 'react';
+import { memo, useCallback, useRef } from 'react';
 
+import type { ResizableAreaProps } from './props';
 
 import styles from './ResizableArea.module.scss';
 
 
-type Props = {
-  className?: string,
-  width?: number,
-  minWidth?: number,
-  disabled?: boolean,
-  children?: React.ReactNode,
-  onResize?: (newWidth: number) => void,
-  onResizeDone?: (newWidth: number) => void,
-  onCollapsed?: () => void,
-}
-
-export const ResizableArea = memo((props: Props): JSX.Element => {
+export const ResizableArea = memo((props: ResizableAreaProps): JSX.Element => {
   const {
     className,
     width, minWidth = 0,

+ 25 - 0
apps/app/src/components/Sidebar/ResizableArea/ResizableAreaFallback.tsx

@@ -0,0 +1,25 @@
+import { memo } from 'react';
+
+
+type Props = {
+  className?: string,
+  width?: number,
+  children?: React.ReactNode,
+}
+
+export const ResizableAreaFallback = memo((props: Props): JSX.Element => {
+  const {
+    className = '',
+    width,
+    children,
+  } = props;
+
+  return (
+    <div
+      className={className}
+      style={{ width }}
+    >
+      {children}
+    </div>
+  );
+});

+ 10 - 0
apps/app/src/components/Sidebar/ResizableArea/props.d.ts

@@ -0,0 +1,10 @@
+export type ResizableAreaProps = {
+  className?: string,
+  width?: number,
+  minWidth?: number,
+  disabled?: boolean,
+  children?: React.ReactNode,
+  onResize?: (newWidth: number) => void,
+  onResizeDone?: (newWidth: number) => void,
+  onCollapsed?: () => void,
+}

+ 56 - 27
apps/app/src/components/Sidebar/Sidebar.tsx

@@ -1,10 +1,12 @@
-import React, {
+import {
   type FC,
   memo, useCallback, useEffect, useState,
   useRef,
 } from 'react';
 
+import withLoadingProps from 'next-dynamic-loading-props';
 import dynamic from 'next/dynamic';
+import { useIsomorphicLayoutEffect } from 'usehooks-ts';
 
 import { SidebarMode } from '~/interfaces/ui';
 import { useIsSearchPage } from '~/stores/context';
@@ -20,7 +22,8 @@ import {
 import { DrawerToggler } from '../Common/DrawerToggler';
 
 import { AppTitleOnSidebarHead, AppTitleOnSubnavigation } from './AppTitle/AppTitle';
-import { ResizableArea } from './ResizableArea/ResizableArea';
+import { ResizableAreaFallback } from './ResizableArea/ResizableAreaFallback';
+import type { ResizableAreaProps } from './ResizableArea/props';
 import { SidebarHead } from './SidebarHead';
 import { SidebarNav, type SidebarNavProps } from './SidebarNav';
 
@@ -28,11 +31,32 @@ import styles from './Sidebar.module.scss';
 
 
 const SidebarContents = dynamic(() => import('./SidebarContents').then(mod => mod.SidebarContents), { ssr: false });
+const ResizableArea = withLoadingProps<ResizableAreaProps>(useLoadingProps => dynamic(
+  () => import('./ResizableArea').then(mod => mod.ResizableArea),
+  {
+    ssr: false,
+    loading: () => {
+      // eslint-disable-next-line react-hooks/rules-of-hooks
+      const { children, ...rest } = useLoadingProps();
+      return <ResizableAreaFallback {...rest}>{children}</ResizableAreaFallback>;
+    },
+  },
+));
 
 
 const resizableAreaMinWidth = 348;
 const sidebarNavCollapsedWidth = 48;
 
+const getWidthByMode = (isDrawerMode: boolean, isCollapsedMode: boolean, currentProductNavWidth: number | undefined): number | undefined => {
+  if (isDrawerMode) {
+    return undefined;
+  }
+  if (isCollapsedMode) {
+    return sidebarNavCollapsedWidth;
+  }
+  return currentProductNavWidth;
+};
+
 
 type ResizableContainerProps = {
   children?: React.ReactNode,
@@ -48,7 +72,10 @@ const ResizableContainer = memo((props: ResizableContainerProps): JSX.Element =>
   const { mutateAndSave: mutatePreferCollapsedMode } = usePreferCollapsedMode();
   const { mutate: mutateCollapsedContentsOpened } = useCollapsedContentsOpened();
 
-  const [resizableAreaWidth, setResizableAreaWidth] = useState<number|undefined>(undefined);
+  const [isClient, setClient] = useState(false);
+  const [resizableAreaWidth, setResizableAreaWidth] = useState<number|undefined>(
+    getWidthByMode(isDrawerMode(), isCollapsedMode(), currentProductNavWidth),
+  );
 
   const resizeHandler = useCallback((newWidth: number) => {
     setResizableAreaWidth(newWidth);
@@ -63,36 +90,38 @@ const ResizableContainer = memo((props: ResizableContainerProps): JSX.Element =>
     mutateCollapsedContentsOpened(false);
   }, [mutateCollapsedContentsOpened, mutatePreferCollapsedMode]);
 
+  useIsomorphicLayoutEffect(() => {
+    setClient(true);
+  }, []);
 
   // open/close resizable container when drawer mode
   useEffect(() => {
-    if (isDrawerMode()) {
-      setResizableAreaWidth(undefined);
-    }
-    else if (isCollapsedMode()) {
-      setResizableAreaWidth(sidebarNavCollapsedWidth);
-    }
-    else {
-      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      setResizableAreaWidth(currentProductNavWidth!);
-    }
-
+    setResizableAreaWidth(getWidthByMode(isDrawerMode(), isCollapsedMode(), currentProductNavWidth));
     mutateDrawerOpened(false);
   }, [currentProductNavWidth, isCollapsedMode, isDrawerMode, mutateDrawerOpened]);
 
-  return (
-    <ResizableArea
-      className="flex-expand-vert"
-      width={resizableAreaWidth}
-      minWidth={resizableAreaMinWidth}
-      disabled={!isDockMode()}
-      onResize={resizeHandler}
-      onResizeDone={resizeDoneHandler}
-      onCollapsed={collapsedByResizableAreaHandler}
-    >
-      {children}
-    </ResizableArea>
-  );
+  return !isClient
+    ? (
+      <ResizableAreaFallback
+        className="flex-expand-vert"
+        width={resizableAreaWidth}
+      >
+        {children}
+      </ResizableAreaFallback>
+    )
+    : (
+      <ResizableArea
+        className="flex-expand-vert"
+        width={resizableAreaWidth}
+        minWidth={resizableAreaMinWidth}
+        disabled={!isDockMode()}
+        onResize={resizeHandler}
+        onResizeDone={resizeDoneHandler}
+        onCollapsed={collapsedByResizableAreaHandler}
+      >
+        {children}
+      </ResizableArea>
+    );
 
 });