1、通过移除和添加ReactDOM.createPortal(child, container)中的container
实现一个React的页面KeepAlive
import type { FC, ReactNode } from 'react';
import { useState, useRef, useLayoutEffect } from 'react';
import ReactDOM from 'react-dom';
type Props = {
active: boolean;
children: ReactNode;
};
const KeepAlive: FC<Props> = (props) => {
const [targetElement] = useState<HTMLDivElement>(() => document.createElement('div'));
const containerRef = useRef<any>(null);
const activatedRef = useRef<boolean>(false);
activatedRef.current = activatedRef.current || props.active;
useLayoutEffect(() => {
if (props.active) {
containerRef.current.appendChild(targetElement);
} else {
try {
containerRef.current.removeChild(targetElement);
} catch (e) {
console.log(e);
}
}
}, [props.active, targetElement]);
return (
<>
<div ref={containerRef} />
{activatedRef.current && // 如果“被激活过”,才渲染 children
ReactDOM.createPortal(props.children, targetElement)}
</>
);
};
export default KeepAlive;
2、利用css样式 display: block 和 display: none
实现一个React的Tabs页面
import React, { useState, useRef } from 'react'
import classNames from 'classnames'
import styles from './index.less'
type PanelProps = {
title: string
id: string
isActive?: boolean
isCache?: boolean
children: React.ReactNode
}
export const Panel: React.FC<PanelProps> = ({ isActive, isCache, children, ...others }) => {
const printRef = useRef<boolean>(true)
if (!isActive && printRef.current) return null
if (isCache && isActive) printRef.current = false
return (
<div {...others} className={classNames(styles['tab-item'], isActive && styles['tab-show'])}>
{children}
</div>
)
}
type TabsProps = {
activeKey: string
children: React.ReactNode
}
export const Tabs: React.FC<TabsProps> = (props) => {
const { activeKey, children } = props
const [currentIndex, setCurrent] = useState<string>(activeKey)
return (
<div className={styles['tab-wrap']}>
<ul className={styles['tab-title-wrap']}>
{React.Children.map(children, (element: any) => {
const { title, id } = element.props
return (
<li
key={id}
onClick={() => setCurrent(id)}
className={classNames(
styles['tab-title'],
id === currentIndex && styles['tab-active'],
)}
>
<div>{title}</div>
</li>
)
})}
</ul>
<div className={styles['tab-item-wrap']}>
{React.Children.map(children, (element: any) => {
const { id, ...others } = element.props
return <Panel {...others} isActive={id === currentIndex} id={id} />
})}
</div>
</div>
)
}
以下是样式
.tab-wrap {
position: relative;
width: 100%;
height: 100%;
}
.tab-title-wrap {
display: block;
width: 100%;
height: 40px;
padding: 0;
white-space: nowrap;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
.tab-title {
display: inline-block;
height: 40px;
margin: 0 24px;
cursor: pointer;
div {
position: relative;
display: inline-block;
height: 40px;
color: #666;
font-size: 16px;
line-height: 40px;
text-align: center;
user-select: none;
}
&:hover {
div {
color: #1890ff;
}
}
}
.tab-active {
div {
color: #1890ff;
&::after {
position: absolute;
bottom: 0;
left: 50%;
display: block;
width: 130%;
height: 2px;
background: #1890ff;
transform: translateX(-50%);
content: '';
}
}
}
}
.tab-item-wrap {
width: 100%;
height: calc(~'100% - 76px');
overflow-y: auto;
.tab-item {
display: none;
}
.tab-show {
display: block;
}
}