👊 【React Day 3】Dialog Refactor

回头看了看几个月前搓的 Dialog 组件,代码十分糅杂,一时间不知道如何评价
不过当时本着学习 React 的心态,第一目标是实现,或许没考虑那么多 🫢

重构后的代码
import React, { ReactNode, useCallback, useEffect, useReducer, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { AnimatePresence, motion } from 'framer-motion';
import { Icon } from '@iconify/react';

function DialogContent({ node, instance }: { node: ReactNode; instance: DialogInstance }) {
  const [show, setShow] = useState(true);

  const forArr = () => {
    const index = dialogArr.indexOf(instance);
    if (index > -1) {
      dialogArr.splice(index, 1);
    }
    if (dialogArr.length < 1) {
      document.removeEventListener('keydown', onEsc);
    }
  };

  const close = useCallback(() => {
    forArr();

    setShow(false);
    setTimeout(() => {
      instance.div.remove();
    }, 200);
  }, [instance]);

  instance.close = close;

  return (
    <AnimatePresence>
      {show && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          transition={{ duration: 0.2 }}
          className="fixed top-0 left-0 h-full w-full bg-white bg-opacity-20 grid place-content-center"
          onClick={close}
        >
          <motion.div
            className="min-w-[500px] min-h-[400px] bg-white rounded-md flex flex-col"
            initial={{ opacity: 0, scale: 0.8 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.8 }}
            transition={{ duration: 0.2, ease: 'easeInOut' }}
            onClick={e => e.stopPropagation()}
          >
            <div className="border-b border-b-solid border-b-gray-300 p-[8px_16px] flex">
              <div className="flex gap-[5px] items-center">
                <span className="text-[18px]">🧀</span>
              </div>
              <div className="ml-auto">
                <motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} onClick={close}>
                  <Icon icon="heroicons:x-circle-20-solid" className="text-[25px] text-red-500" />
                </motion.button>
              </div>
            </div>
            {node}
          </motion.div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}

const dialogArr: DialogInstance[] = [];

type DialogInstance = {
  div: HTMLDivElement;
  close?: () => void;
};

function show(content: ReactNode) {
  const instance: DialogInstance = {
    div: document.createElement('div'),
  };
  createRoot(instance.div).render(<DialogContent node={content} instance={instance} />);
  document.body.appendChild(instance.div);

  dialogArr.push(instance);
  document.addEventListener('keydown', onEsc);
  return instance;
}

function onEsc(event: KeyboardEvent) {
  if (event.key === 'Escape') {
    const instance = dialogArr.pop();
    instance?.close?.();
  }
}

export default {
  show,
};

本质上的操作逻辑并无变化,优化了实现过程
排除了之前的用于处理 ESC 监听 hooks ,改为了在外部维护对象,调用方可以依靠 show 返回的对象来关闭弹窗 🤪

消息盒子
# 您需要首次评论以获取消息 #
# 您需要首次评论以获取消息 #

只显示最新10条未读和已读信息