perf(MenuEditor): optimize router context

This commit is contained in:
Zeke Zhang 2024-11-03 18:16:06 +08:00
parent d786145fc2
commit 023aa0ce4f
2 changed files with 74 additions and 16 deletions

View File

@ -8,7 +8,16 @@
*/
import React, { FC, useEffect } from 'react';
import { Location, NavigateFunction, NavigateOptions, useLocation, useNavigate } from 'react-router-dom';
import {
Location,
NavigateFunction,
NavigateOptions,
PathMatch,
useLocation,
useMatch,
useNavigate,
useParams,
} from 'react-router-dom';
const NavigateNoUpdateContext = React.createContext<NavigateFunction>(null);
NavigateNoUpdateContext.displayName = 'NavigateNoUpdateContext';
@ -22,12 +31,36 @@ LocationSearchContext.displayName = 'LocationSearchContext';
const IsAdminPageContext = React.createContext<boolean>(false);
IsAdminPageContext.displayName = 'IsAdminPageContext';
const CurrentPageUidContext = React.createContext<string>('');
CurrentPageUidContext.displayName = 'CurrentPageUidContext';
const MatchAdminContext = React.createContext<PathMatch<string> | null>(null);
MatchAdminContext.displayName = 'MatchAdminContext';
const MatchAdminNameContext = React.createContext<PathMatch<string> | null>(null);
MatchAdminNameContext.displayName = 'MatchAdminNameContext';
const MatchAdminProvider: FC = ({ children }) => {
const matchAdmin = useMatch('/admin');
return <MatchAdminContext.Provider value={matchAdmin}>{children}</MatchAdminContext.Provider>;
};
const MatchAdminNameProvider: FC = ({ children }) => {
const matchAdminName = useMatch('/admin/:name');
return <MatchAdminNameContext.Provider value={matchAdminName}>{children}</MatchAdminNameContext.Provider>;
};
const IsAdminPageProvider: FC = ({ children }) => {
const location = useLocation();
const isAdminPage = location.pathname.startsWith('/admin');
return <IsAdminPageContext.Provider value={isAdminPage}>{children}</IsAdminPageContext.Provider>;
};
const CurrentPageUidProvider: FC = ({ children }) => {
const params = useParams<any>();
return <CurrentPageUidContext.Provider value={params.name}>{children}</CurrentPageUidContext.Provider>;
};
/**
* When the URL changes, components that use `useNavigate` will re-render.
* This provider provides a `navigateNoUpdate` method that can avoid re-rendering.
@ -96,12 +129,30 @@ export const useIsAdminPage = () => {
return React.useContext(IsAdminPageContext);
};
export const useCurrentPageUid = () => {
return React.useContext(CurrentPageUidContext);
};
export const useMatchAdmin = () => {
return React.useContext(MatchAdminContext);
};
export const useMatchAdminName = () => {
return React.useContext(MatchAdminNameContext);
};
export const CustomRouterContextProvider: FC = ({ children }) => {
return (
<NavigateNoUpdateProvider>
<LocationNoUpdateProvider>
<IsAdminPageProvider>
<LocationSearchProvider>{children}</LocationSearchProvider>
<LocationSearchProvider>
<CurrentPageUidProvider>
<MatchAdminProvider>
<MatchAdminNameProvider>{children}</MatchAdminNameProvider>
</MatchAdminProvider>
</CurrentPageUidProvider>
</LocationSearchProvider>
</IsAdminPageProvider>
</LocationNoUpdateProvider>
</NavigateNoUpdateProvider>

View File

@ -12,7 +12,7 @@ import { useSessionStorageState } from 'ahooks';
import { App, ConfigProvider, Divider, Layout } from 'antd';
import { createGlobalStyle } from 'antd-style';
import React, { FC, createContext, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { Link, Outlet, useMatch, useParams } from 'react-router-dom';
import { Link, Outlet, useParams } from 'react-router-dom';
import {
ACLRolesCheckProvider,
CurrentAppInfoProvider,
@ -33,7 +33,13 @@ import {
useSystemSettings,
useToken,
} from '../../../';
import { useLocationNoUpdate, useNavigateNoUpdate } from '../../../application/CustomRouterContextProvider';
import {
useCurrentPageUid,
useLocationNoUpdate,
useMatchAdmin,
useMatchAdminName,
useNavigateNoUpdate,
} from '../../../application/CustomRouterContextProvider';
import { Plugin } from '../../../application/Plugin';
import { useAppSpin } from '../../../application/hooks/useAppSpin';
import { useMenuTranslation } from '../../../schema-component/antd/menu/locale';
@ -82,12 +88,11 @@ const MenuEditor = (props) => {
const { setTitle: _setTitle } = useDocumentTitle();
const setTitle = useCallback((title) => _setTitle(t(title)), []);
const navigate = useNavigateNoUpdate();
const params = useParams<any>();
const location = useLocationNoUpdate();
const isMatchAdmin = useMatch('/admin');
const isMatchAdminName = useMatch('/admin/:name');
const defaultSelectedUid = params.name;
const isDynamicPage = !!defaultSelectedUid;
const isMatchAdmin = useMatchAdmin();
const isMatchAdminName = useMatchAdminName();
const currentPageUid = useCurrentPageUid();
const isDynamicPage = !!currentPageUid;
const { sideMenuRef } = props;
const ctx = useACLRoleContext();
const [current, setCurrent] = useState(null);
@ -126,7 +131,7 @@ const MenuEditor = (props) => {
if (!isMatchAdminName || !isDynamicPage) return;
// url 为 `admin/xxx` 的情况
const s = findByUid(schema, defaultSelectedUid);
const s = findByUid(schema, currentPageUid);
if (s) {
setTitle(s.title);
} else {
@ -147,7 +152,9 @@ const MenuEditor = (props) => {
if (sideMenuRef.current) {
const pageType =
properties &&
Object.values(properties).find((item) => item['x-uid'] === params.name && item['x-component'] === 'Menu.Item');
Object.values(properties).find(
(item) => item['x-uid'] === currentPageUid && item['x-component'] === 'Menu.Item',
);
const isSettingPage = location?.pathname.includes('/settings');
if (pageType || isSettingPage) {
sideMenuRef.current.style.display = 'none';
@ -155,7 +162,7 @@ const MenuEditor = (props) => {
sideMenuRef.current.style.display = 'block';
}
}
}, [data?.data, params.name, sideMenuRef, location?.pathname]);
}, [data?.data, currentPageUid, sideMenuRef, location?.pathname]);
const schema = useMemo(() => {
const s = filterByACL(data?.data, ctx);
@ -167,12 +174,12 @@ const MenuEditor = (props) => {
useEffect(() => {
if (isMatchAdminName) {
const s = findByUid(schema, defaultSelectedUid);
const s = findByUid(schema, currentPageUid);
if (s) {
setTitle(s.title);
}
}
}, [defaultSelectedUid, isMatchAdmin, isMatchAdminName, schema, setTitle]);
}, [currentPageUid, isMatchAdmin, isMatchAdminName, schema, setTitle]);
useRequest(
{
@ -213,14 +220,14 @@ const MenuEditor = (props) => {
);
const scope = useMemo(() => {
return { useMenuProps, onSelect, sideMenuRef, defaultSelectedUid };
return { useMenuProps, onSelect, sideMenuRef, defaultSelectedUid: currentPageUid };
}, []);
if (loading) {
return render();
}
return (
<SchemaIdContext.Provider value={defaultSelectedUid}>
<SchemaIdContext.Provider value={currentPageUid}>
<SchemaComponent distributed scope={scope} schema={schema} />
</SchemaIdContext.Provider>
);