Skip to content

Commit

Permalink
feat✨: add KeepAlive
Browse files Browse the repository at this point in the history
  • Loading branch information
eternallycyf committed Apr 23, 2023
1 parent bf2ee64 commit ac4c6f0
Show file tree
Hide file tree
Showing 14 changed files with 262 additions and 9 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- ~~docker 部署方案~~
- ~~封装虚拟列表,集成到 antd table 中~~
- 使用哈希重构数据格式 增加查询速度
- 自己实现一个 keep-alive
- 单元 测试
- ~~UI 自动化测试 puppeteer~~
- threejs 着色器
Expand Down Expand Up @@ -298,3 +299,18 @@ export default compose(
layout,
},
```

## keepAlive

```tsx | pure
import { KeepAlive } from '@/core/base/KeepAlive';

<KeepAlive></KeepAlive>;

export default compose<typeof Activity>(
withRoutePage,
withRouter,
connect(({ global, login }: ConnectState) => ({ token: login.token })),
KeepAliveTransfer,
)(Activity);
```
14 changes: 10 additions & 4 deletions src/core/Enhance/Authorized.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { History } from 'history';
import React, { useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import { RouteComponentProps } from '@umijs/renderer-react';
import { withRoutePage } from './withRoutePage';
import { compose } from 'redux';

interface IProps extends RouteComponentProps<any> {
token: string;
Expand Down Expand Up @@ -31,7 +33,11 @@ const Authorized: React.FC<IProps> = (props) => {
return <Outlet />;
};

export default connect(({ login, global }: ConnectState) => ({
token: login.token,
userInfo: global.userInfo,
}))(withRouter<IProps>(Authorized));
export default compose<IProps>(
withRoutePage,
withRouter,
connect(({ global, login }: ConnectState) => ({
token: login.token,
userInfo: global.userInfo,
})),
)(Authorized);
73 changes: 73 additions & 0 deletions src/core/base/KeepAlive/KeepAlive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { FC, useCallback, useReducer } from 'react';
import { IKeepAliveContext, KeepAliveContext } from './KeepAliveContext';
import { KeepAliveActions, keepAliveReducer, KeepAliveState } from './KeepAliveReducer';
import * as actionTypes from './actionTypes';

interface IKeepAliveProps {
children: React.ReactNode;
}

export type ISetKeepAliveState = (arg: KeepAliveActions['payload']) => void;

/**
* {
* keepAliveId: 'class'
* reactElement: ReactElement
* nodes: Node[]
* status: 'CREATING' | 'CREATED'
* }
*/

const KeepAlive: FC<IKeepAliveProps> = (props) => {
const [keepAliveStates, dispatch] = useReducer(keepAliveReducer, {});

const setKeepAliveState: ISetKeepAliveState = useCallback(
({ reactElement, keepAliveId }) => {
if (!keepAliveStates[keepAliveId]) {
dispatch({
type: actionTypes.CREATING,
payload: {
keepAliveId,
reactElement,
},
});
}
},
[keepAliveStates],
);

return (
<KeepAliveContext.Provider
value={{
keepAliveStates,
setKeepAliveState,
dispatch,
}}
>
{props.children}
{Object?.values(keepAliveStates).map((state) => {
const { keepAliveId, reactElement } = state;
return (
<div
key={keepAliveId}
ref={(node) => {
if (node && !keepAliveStates[keepAliveId]?.nodes) {
dispatch({
type: actionTypes.CREATED,
payload: {
keepAliveId,
nodes: Array.from(node.childNodes) as any[],
},
});
}
}}
>
{reactElement}
</div>
);
})}
</KeepAliveContext.Provider>
);
};

export default KeepAlive;
11 changes: 11 additions & 0 deletions src/core/base/KeepAlive/KeepAliveContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from 'react';
import { ISetKeepAliveState } from './KeepAlive';
import { KeepAliveActions, KeepAliveState } from './KeepAliveReducer';

export interface IKeepAliveContext {
keepAliveStates: KeepAliveState;
setKeepAliveState: ISetKeepAliveState;
dispatch: React.Dispatch<KeepAliveActions>;
}

export const KeepAliveContext = createContext<IKeepAliveContext>({} as IKeepAliveContext);
61 changes: 61 additions & 0 deletions src/core/base/KeepAlive/KeepAliveReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as actionTypes from './actionTypes';

export interface KeepAliveState {
[key: string]: {
keepAliveId: string;
reactElement?: React.ReactElement;
status: string;
nodes?: Array<React.ReactElement>;
};
}

const initialState = {};

export type KeepAliveActions =
| {
type: 'CREATING';
payload: {
keepAliveId: string;
reactElement?: React.ReactElement;
nodes?: Array<HTMLDivElement>;
};
}
| {
type: 'CREATED';
payload: {
keepAliveId: string;
reactElement?: React.ReactElement;
nodes?: Array<HTMLDivElement>;
};
};

export function keepAliveReducer(state: KeepAliveState | any, action: KeepAliveActions): KeepAliveState {
const { type, payload } = action;
const { keepAliveId, reactElement, nodes } = payload;
switch (action.type) {
case actionTypes.CREATING: {
return {
...state,
[keepAliveId]: {
keepAliveId,
reactElement,
status: type,
nodes: null,
},
};
}
case actionTypes.CREATED: {
return {
...state,
[keepAliveId]: {
...state[keepAliveId],
status: type,
nodes,
},
};
}
default: {
return state;
}
}
}
35 changes: 35 additions & 0 deletions src/core/base/KeepAlive/KeepAliveTransfer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useContext, useEffect } from 'react';
import { KeepAliveContext } from './KeepAliveContext';

interface IKeepAliveTransferProps {
KeepAliveComponent: React.ComponentType;
keepAliveId: string;
}

const KeepAliveTransfer = <Props extends IKeepAliveTransferProps>(
KeepAliveComponent: React.ComponentType<Props> | any,
keepAliveId?: string,
) => {
const displayName = KeepAliveComponent.displayName || KeepAliveComponent.name || keepAliveId;

return (props: Omit<Props, keyof IKeepAliveTransferProps>) => {
const _ref = React.useRef<HTMLDivElement>(null!);
const { keepAliveStates, setKeepAliveState } = useContext(KeepAliveContext);

useEffect(() => {
const state = keepAliveStates[displayName];
if (state && state?.nodes) {
state.nodes.forEach((node: any) => _ref.current.appendChild(node));
} else {
setKeepAliveState({
keepAliveId: displayName,
reactElement: <KeepAliveComponent {...(props as Props)} />,
});
}
}, [keepAliveStates, setKeepAliveState, props]);

return <div ref={_ref} />;
};
};

export default KeepAliveTransfer;
24 changes: 24 additions & 0 deletions src/core/base/KeepAlive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Context

- 将组件 dom 缓存保存在一个对象中
- id: {nodes,ReactElement,status}

## 结构

- Provider => 方法 属性
- 组件 => nodes => 设置缓存的方法 组件的缓存

## use

```tsx | pure
import { KeepAlive } from '@/core/base/KeepAlive';

<KeepAlive></KeepAlive>;

export default compose<typeof Activity>(
withRoutePage,
withRouter,
connect(({ global, login }: ConnectState) => ({ token: login.token })),
KeepAliveTransfer,
)(Activity);
```
2 changes: 2 additions & 0 deletions src/core/base/KeepAlive/actionTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const CREATING = 'CREATING';
export const CREATED = 'CREATED';
4 changes: 4 additions & 0 deletions src/core/base/KeepAlive/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import KeepAlive from './KeepAlive';
import KeepAliveTransfer from './KeepAliveTransfer';

export { KeepAlive, KeepAliveTransfer };
4 changes: 2 additions & 2 deletions src/core/base/Login/index.less
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.site-pro-form-login-container {
position: fixed;
z-index: -999;
z-index: 999;
display: grid;
overflow: auto;
width: 100%;
Expand Down Expand Up @@ -36,4 +36,4 @@
font-family: -apple-system, BlinkMacSystemFont, segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif,
apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
line-height: 1.5714285714285714;
}
}
12 changes: 10 additions & 2 deletions src/core/layouts/BasicLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import GlobalHeader from '@/core/base/GlobalHeader';
import TagsNav from '@/core/base/TagsNav';
import { ConnectState } from '@/typings/connect';
import { connect, Dispatch, getDvaApp, Helmet, History, Outlet, withRouter } from '@umijs/max';
import { RouteComponentProps } from '@umijs/renderer-react';
import { RouteComponentProps, useAppData, useLocation } from '@umijs/renderer-react';
import { ConfigProvider, Layout, Spin, theme as antdTheme, TourProps, Tour, FloatButton } from 'antd';
import _ from 'lodash';
import { BellOutlined } from '@ant-design/icons';
Expand All @@ -15,6 +15,8 @@ import { FC, Fragment, useEffect, useState, useRef } from 'react';
import ColorPicker from '../base/GlobalHeader/ColorPicker';
import styles from './index.less';
import IndexPage from './IndexPage';
import { KeepAlive, KeepAliveTransfer } from '../base/KeepAlive';

const { Sider, Content, Header } = Layout;
const { title } = config;

Expand All @@ -34,6 +36,7 @@ export interface IRgba {
}

const BasicLayout: FC<IBasicLayout> = (props) => {
const { routes } = useAppData();
const [isDark, setIsDark] = useState<boolean>(false);
const [color, setColor] = useState<IRgba>({
r: '25',
Expand All @@ -48,6 +51,9 @@ const BasicLayout: FC<IBasicLayout> = (props) => {
const ref3 = useRef<HTMLDivElement>(null!);
const { menuList, breadcrumbNameMap, children, theme, collapsed, location, sliderMenuState, dispatch, userInfo } =
props;
const currentRoutesObj = Object.values(routes).filter((item) => item?.path == location.pathname)?.[0];
// TODO: routes 添加keepAlive配置
// const hasKeepAlive = currentRoutesObj?.keepAlive;

const steps: TourProps['steps'] = [
{
Expand Down Expand Up @@ -161,7 +167,9 @@ const BasicLayout: FC<IBasicLayout> = (props) => {
<Sider theme={theme} style={{ background: 'transparent', display: collapsed ? 'none' : '' }}>
<SiderMenu {...siderMenuProps} ref0={ref0} />
</Sider>
<Content>{location.pathname !== '/' ? <Outlet /> : <IndexPage />}</Content>
<Content>
<KeepAlive>{location.pathname !== '/' ? <Outlet /> : <IndexPage />}</KeepAlive>
</Content>
</Layout>
</WaterMark>
</Layout>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/Component/class/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getFieldComp } from '@/core/helpers';
import { withRoutePage } from '@/core/Enhance/withRoutePage';
import { compose } from 'redux';
import { ConnectState } from '@/typings/connect';
import { KeepAliveTransfer } from '@/core/base/KeepAlive';
const { apiPrefixMock } = projectConfig;

interface IProps {}
Expand Down Expand Up @@ -253,4 +254,5 @@ export default compose<typeof Activity>(
withRoutePage,
withRouter,
connect(({ global, login }: ConnectState) => ({ token: login.token })),
KeepAliveTransfer,
)(Activity);
2 changes: 2 additions & 0 deletions src/pages/Component/hook/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { compose } from 'redux';
import _ from 'lodash';
import { withRouter } from '@/core/Enhance/withRouter';
import { KeepAliveTransfer } from '@/core/base/KeepAlive';

interface IProps {
IndexPageRef: Ref<IHandle>;
Expand Down Expand Up @@ -257,6 +258,7 @@ const NewIndexPage = compose<typeof IndexPage>(
pure: undefined,
}),
forwardRef,
KeepAliveTransfer,
)(IndexPage);

const Content = () => {
Expand Down
11 changes: 10 additions & 1 deletion src/pages/FileViewer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
import FileView from '@/components/FileViewer/demo/demo';
export default FileView;
import { KeepAliveTransfer } from '@/core/base/KeepAlive';
import { withRoutePage } from '@/core/Enhance/withRoutePage';
import { withRouter } from '@umijs/max';
import { compose } from 'redux';

const IndexPage = () => {
return <FileView />;
};

export default compose(withRoutePage, withRouter, KeepAliveTransfer)(IndexPage);

0 comments on commit ac4c6f0

Please sign in to comment.