Преглед изворни кода

refactor useSWRxCurrentPage

Yuki Takei пре 3 година
родитељ
комит
2dec6ec79d

+ 2 - 2
packages/app/src/client/services/page-operation.ts

@@ -6,7 +6,7 @@ import urljoin from 'url-join';
 import { OptionsToSave } from '~/interfaces/page-operation';
 import { useCurrentPageId } from '~/stores/context';
 import { useEditingMarkdown, useIsEnabledUnsavedWarning, usePageTagsForEditors } from '~/stores/editor';
-import { useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import { useSWRMUTxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
 import { useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
 import loggerFactory from '~/utils/logger';
 
@@ -179,7 +179,7 @@ export const useSaveOrUpdate = (): SaveOrUpdateFunction => {
 
 export const useUpdateStateAfterSave = (pageId: string|undefined|null): (() => Promise<void>) | undefined => {
   const { mutate: mutateCurrentPageId } = useCurrentPageId();
-  const { mutate: mutateCurrentPage } = useSWRxCurrentPage();
+  const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
   const { setRemoteLatestPageData } = useSetRemoteLatestPageData();
   const { mutate: mutateTagsInfo } = useSWRxTagsInfo(pageId);
   const { sync: syncTagsInfoForEditor } = usePageTagsForEditors(pageId);

+ 2 - 2
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -24,7 +24,7 @@ import {
   usePageAccessoriesModal, PageAccessoriesModalContents, IPageForPageDuplicateModal,
   usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, usePagePresentationModal,
 } from '~/stores/modal';
-import { useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import { useSWRMUTxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
 import {
   EditorMode, useDrawerMode, useEditorMode, useIsAbleToShowPageManagement, useIsAbleToShowTagLabel,
   useIsAbleToChangeEditorMode, useIsAbleToShowPageAuthors,
@@ -200,7 +200,7 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
   const router = useRouter();
 
   const { data: shareLinkId } = useShareLinkId();
-  const { mutate: mutateCurrentPage } = useSWRxCurrentPage();
+  const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
 
   const { data: currentPathname } = useCurrentPathname();
   const isSharedPage = pagePathUtils.isSharedPage(currentPathname ?? '');

+ 1 - 8
packages/app/src/components/Page/PageContents.tsx

@@ -1,14 +1,11 @@
 import React, { useEffect } from 'react';
 
-import { pagePathUtils } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 
 import { useUpdateStateAfterSave } from '~/client/services/page-operation';
 import { useDrawioModalLauncherForView } from '~/client/services/side-effects/drawio-modal-launcher-for-view';
 import { useHandsontableModalLauncherForView } from '~/client/services/side-effects/handsontable-modal-launcher-for-view';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-import { useCurrentPathname } from '~/stores/context';
-import { useEditingMarkdown } from '~/stores/editor';
 import { useSWRxCurrentPage } from '~/stores/page';
 import { useViewOptions } from '~/stores/renderer';
 import { registerGrowiFacade } from '~/utils/growi-facade';
@@ -23,11 +20,7 @@ const logger = loggerFactory('growi:Page');
 export const PageContents = (): JSX.Element => {
   const { t } = useTranslation();
 
-  const { data: currentPathname } = useCurrentPathname();
-  const isSharedPage = pagePathUtils.isSharedPage(currentPathname ?? '');
-
-  const { data: currentPage, mutate: mutateCurrentPage } = useSWRxCurrentPage();
-  const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
+  const { data: currentPage } = useSWRxCurrentPage();
   const updateStateAfterSave = useUpdateStateAfterSave(currentPage?._id);
 
   const { data: rendererOptions, mutate: mutateRendererOptions } = useViewOptions();

+ 6 - 4
packages/app/src/components/PageEditor.tsx

@@ -25,15 +25,16 @@ import {
   useIsEditable, useIsUploadableFile, useIsUploadableImage, useIsNotFound, useIsIndentSizeForced,
 } from '~/stores/context';
 import {
-  useCurrentIndentSize, useSWRxSlackChannels, useIsSlackEnabled, useIsTextlintEnabled, usePageTagsForEditors,
+  useCurrentIndentSize, useIsSlackEnabled, useIsTextlintEnabled, usePageTagsForEditors,
   useIsEnabledUnsavedWarning,
   useIsConflict,
   useEditingMarkdown,
 } from '~/stores/editor';
 import { useConflictDiffModal } from '~/stores/modal';
-import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import {
+  useCurrentPagePath, useSWRMUTxCurrentPage, useSWRxCurrentPage, useSWRxTagsInfo,
+} from '~/stores/page';
 import { usePageTreeTermManager } from '~/stores/page-listing';
-import { useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
 import { usePreviewOptions } from '~/stores/renderer';
 import {
   EditorMode,
@@ -74,7 +75,8 @@ const PageEditor = React.memo((): JSX.Element => {
   const { data: pageId } = useCurrentPageId();
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: currentPathname } = useCurrentPathname();
-  const { data: currentPage, mutate: mutateCurrentPage } = useSWRxCurrentPage();
+  const { data: currentPage } = useSWRxCurrentPage();
+  const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
   const { data: grantData, mutate: mutateGrant } = useSelectedGrant();
   const { data: pageTags, sync: syncTagsInfoForEditor } = usePageTagsForEditors(pageId);
   const { mutate: mutateTagsInfo } = useSWRxTagsInfo(pageId);

+ 5 - 2
packages/app/src/components/PageEditorByHackmd.tsx

@@ -24,7 +24,9 @@ import {
 import {
   usePageIdOnHackmd, useHasDraftOnHackmd, useRevisionIdHackmdSynced, useIsHackmdDraftUpdatingInRealtime,
 } from '~/stores/hackmd';
-import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import {
+  useCurrentPagePath, useSWRMUTxCurrentPage, useSWRxCurrentPage, useSWRxTagsInfo,
+} from '~/stores/page';
 import { usePageTreeTermManager } from '~/stores/page-listing';
 import { useRemoteRevisionId } from '~/stores/remote-latest-page';
 import {
@@ -69,7 +71,8 @@ export const PageEditorByHackmd = (): JSX.Element => {
   const { returnPathForURL } = pathUtils;
 
   // pageData
-  const { data: pageData, mutate: mutatePageData } = useSWRxCurrentPage();
+  const { data: pageData } = useSWRxCurrentPage();
+  const { trigger: mutatePageData } = useSWRMUTxCurrentPage();
   const revision = pageData?.revision;
 
   const [isInitialized, setIsInitialized] = useState(false);

+ 3 - 2
packages/app/src/components/PageStatusAlert.tsx

@@ -9,7 +9,7 @@ import {
   useHasDraftOnHackmd, useIsHackmdDraftUpdatingInRealtime, useRevisionIdHackmdSynced,
 } from '~/stores/hackmd';
 import { useConflictDiffModal } from '~/stores/modal';
-import { useSWRxCurrentPage } from '~/stores/page';
+import { useSWRMUTxCurrentPage, useSWRxCurrentPage } from '~/stores/page';
 import { useRemoteRevisionId, useRemoteRevisionLastUpdateUser } from '~/stores/remote-latest-page';
 import { EditorMode, useEditorMode } from '~/stores/ui';
 
@@ -39,7 +39,8 @@ export const PageStatusAlert = (): JSX.Element => {
   const { data: remoteRevisionId } = useRemoteRevisionId();
   const { data: remoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
 
-  const { data: pageData, mutate: mutatePageData } = useSWRxCurrentPage();
+  const { data: pageData } = useSWRxCurrentPage();
+  const { trigger: mutatePageData } = useSWRMUTxCurrentPage();
   const revision = pageData?.revision;
 
   const refreshPage = useCallback(async() => {

+ 2 - 2
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -16,7 +16,7 @@ import { useIsEnabledAttachTitleHeader } from '~/stores/context';
 import {
   IPageForPageDuplicateModal, usePageDuplicateModal, usePageDeleteModal,
 } from '~/stores/modal';
-import { useCurrentPagePath, usePageInfoTermManager, useSWRxCurrentPage } from '~/stores/page';
+import { useCurrentPagePath, usePageInfoTermManager, useSWRMUTxCurrentPage } from '~/stores/page';
 import {
   usePageTreeTermManager, useSWRxPageAncestorsChildren, useSWRxRootPage, useDescendantsPageListForCurrentPathTermManager,
 } from '~/stores/page-listing';
@@ -117,7 +117,7 @@ const ItemsTree = (props: ItemsTreeProps): JSX.Element => {
   const { data: ptDescCountMap, update: updatePtDescCountMap } = usePageTreeDescCountMap();
 
   // for mutation
-  const { mutate: mutateCurrentPage } = useSWRxCurrentPage();
+  const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
   const { advance: advancePt } = usePageTreeTermManager();
   const { advance: advanceFts } = useFullTextSearchTermManager();
   const { advance: advanceDpl } = useDescendantsPageListForCurrentPathTermManager();

+ 41 - 51
packages/app/src/stores/page.tsx

@@ -4,8 +4,9 @@ import type {
   IPageInfoForEntity, IPagePopulatedToShowRevision, Nullable,
 } from '@growi/core';
 import { isClient, pagePathUtils } from '@growi/core';
-import useSWR, { Key, SWRConfiguration, SWRResponse } from 'swr';
+import useSWR, { mutate, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
+import useSWRMutation, { SWRMutationResponse } from 'swr/mutation';
 
 import { apiGet } from '~/client/util/apiv1-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
@@ -24,53 +25,28 @@ import { ITermNumberManagerUtil, useTermNumberManager } from './use-static-swr';
 
 const { isPermalink: _isPermalink } = pagePathUtils;
 
-export const useSWRxPage = (
-    pageId?: string|null,
-    shareLinkId?: string,
-    revisionId?: string,
-    initialData?: IPagePopulatedToShowRevision|null,
-    config?: SWRConfiguration,
-): SWRResponse<IPagePopulatedToShowRevision|null, Error> => {
-
-  const swrResponse = useSWRImmutable(
-    pageId != null ? ['/page', pageId, shareLinkId, revisionId] : null,
-    // TODO: upgrade SWR to v2 and use useSWRMutation
-    //        in order to avoid complicated fetcher settings
-    Object.assign({
-      fetcher: ([endpoint, pageId, shareLinkId, revisionId]: [string, string, string|undefined, string|undefined]) => {
-        return apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { pageId, shareLinkId, revisionId })
-          .then(result => result.data.page)
-          .catch((errs) => {
-            if (!Array.isArray(errs)) { throw Error('error is not array') }
-            const statusCode = errs[0].status;
-            if (statusCode === 403 || statusCode === 404) {
-              // for NotFoundPage
-              return null;
-            }
-            throw Error('failed to get page');
-          });
-      },
-    }, config ?? {}),
-  );
+
+export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|null): SWRResponse<IPagePopulatedToShowRevision|null> => {
+  const key = 'currentPage';
 
   useEffect(() => {
     if (initialData !== undefined) {
-      swrResponse.mutate(initialData);
+      mutate(key, initialData, {
+        optimisticData: initialData,
+        populateCache: true,
+        revalidate: false,
+      });
     }
-  // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [initialData]); // Only depends on `initialData`
+  }, [initialData, key]);
 
-  return swrResponse;
+  return useSWR(key, null, {
+    keepPreviousData: true,
+  });
 };
 
-export const useSWRxPageByPath = (path?: string): SWRResponse<IPagePopulatedToShowRevision, Error> => {
-  return useSWR(
-    path != null ? ['/page', path] : null,
-    ([endpoint, path]) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { path }).then(result => result.data.page),
-  );
-};
+export const useSWRMUTxCurrentPage = (): SWRMutationResponse<IPagePopulatedToShowRevision|null> => {
+  const key = 'currentPage';
 
-export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|null): SWRResponse<IPagePopulatedToShowRevision|null, Error> => {
   const { data: currentPageId } = useCurrentPageId();
   const { data: shareLinkId } = useShareLinkId();
 
@@ -82,20 +58,34 @@ export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|nu
     revisionId = requestRevisionId != null ? requestRevisionId : undefined;
   }
 
-  const swrResult = useSWRxPage(
-    currentPageId, shareLinkId, revisionId,
-    initialData,
-    // overwrite fetcher if the current page is share link
-    shareLinkId == null
-      ? undefined
-      : {
-        fetcher: () => null,
-      },
+  return useSWRMutation(
+    key,
+    async() => {
+      return apiv3Get<{ page: IPagePopulatedToShowRevision }>('/page', { pageId: currentPageId, shareLinkId, revisionId })
+        .then(result => result.data.page)
+        .catch((errs) => {
+          if (!Array.isArray(errs)) { throw Error('error is not array') }
+          const statusCode = errs[0].status;
+          if (statusCode === 403 || statusCode === 404) {
+            // for NotFoundPage
+            return null;
+          }
+          throw Error('failed to get page');
+        });
+    },
+    {
+      populateCache: true,
+      revalidate: false,
+    },
   );
-
-  return swrResult;
 };
 
+export const useSWRxPageByPath = (path?: string): SWRResponse<IPagePopulatedToShowRevision, Error> => {
+  return useSWR(
+    path != null ? ['/page', path] : null,
+    ([endpoint, path]) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { path }).then(result => result.data.page),
+  );
+};
 
 export const useSWRxTagsInfo = (pageId: Nullable<string>): SWRResponse<IPageTagsInfo | undefined, Error> => {
   const { data: shareLinkId } = useShareLinkId();