React 缓存页面的两种方式

2,739 阅读1分钟

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);

  // 增加一个 ref 记录组件是否“被激活过”
  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;
  }
}