模态框的两种管理思路

考试周回到学校反而有时间整理实习期间的随笔

第一种思路是以功能为聚合,例如点击按钮弹出模态框的话那么就写一个按钮组件,这种思路的优点是在一处进行管理功能,用到的时候只需要将按钮嵌入页面就好,但是可能没法在同一处管理某页面用到的所有模态框

第二种思路就是通过钩子函数内部对模态框的状态进行管理,那么外部调用只需要调用方法即可

在内部管理状态用到了Reducer统一管理多种模态框时的action、state

复制代码
// 模态框状态类型
interface ModalState {
  qrcodeDetail: {
    visible: boolean;
    mode: 'edit' | 'create';
    data: QrCodeRecord | null;
  };
  docConfig: {
    visible: boolean;
  };
  qrcodeView: {
    visible: boolean;
    imgUrl: string;
  };
}

// Action 类型定义
type ModalAction =
  | { type: 'SHOW_QRCODE_DETAIL'; mode: 'edit' | 'create'; data?: QrCodeRecord }
  | { type: 'CLOSE_QRCODE_DETAIL' }
  | { type: 'SHOW_DOC_CONFIG' }
  | { type: 'CLOSE_DOC_CONFIG' }
  | { type: 'SHOW_QRCODE_VIEW'; imgUrl: string }
  | { type: 'CLOSE_QRCODE_VIEW' };

// 初始状态
const initialModalState: ModalState = {
  qrcodeDetail: {
    visible: false,
    mode: 'create',
    data: null,
  },
  docConfig: {
    visible: false,
  },
  qrcodeView: {
    visible: false,
    imgUrl: '',
  },
};

// Reducer 函数
const modalReducer = (state: ModalState, action: ModalAction): ModalState => {
  switch (action.type) {
    case 'SHOW_QRCODE_DETAIL':
      return {
        ...state,
        qrcodeDetail: {
          visible: true,
          mode: action.mode,
          data: action.data || null,
        },
      };
    case 'CLOSE_QRCODE_DETAIL':
      return {
        ...state,
        qrcodeDetail: {
          visible: false,
          mode: 'create',
          data: null,
        },
      };
    case 'SHOW_DOC_CONFIG':
      return {
        ...state,
        docConfig: {
          visible: true,
        },
      };
    case 'CLOSE_DOC_CONFIG':
      return {
        ...state,
        docConfig: {
          visible: false,
        },
      };
    case 'SHOW_QRCODE_VIEW':
      return {
        ...state,
        qrcodeView: {
          visible: true,
          imgUrl: action.imgUrl,
        },
      };
    case 'CLOSE_QRCODE_VIEW':
      return {
        ...state,
        qrcodeView: {
          visible: false,
          imgUrl: '',
        },
      };
    default:
      return state;
  }
};

// 弹窗管理Hook
export const useModals = (handleSuccess?: () => void) => {
  const [state, dispatch] = useReducer(modalReducer, initialModalState);

  const showEditModal = (mode: 'edit' | 'create', record?: QrCodeRecord) => {
    dispatch({ type: 'SHOW_QRCODE_DETAIL', mode, data: record });
  };

  const closeEditModal = () => {
    dispatch({ type: 'CLOSE_QRCODE_DETAIL' });
  };

  const showConfigModal = () => {
    dispatch({ type: 'SHOW_DOC_CONFIG' });
  };

  const closeConfigModal = () => {
    dispatch({ type: 'CLOSE_DOC_CONFIG' });
  };

  const showViewModal = (qrCodeUrl: string) => {
    dispatch({ type: 'SHOW_QRCODE_VIEW', imgUrl: qrCodeUrl });
  };

  const closeViewModal = () => {
    dispatch({ type: 'CLOSE_QRCODE_VIEW' });
  };

  const modalHandlers = {
    onShowEditModal: showEditModal,
    onShowViewModal: showViewModal,
    onShowConfigModal: showConfigModal,
  };

  const modals = (
    <>
      {state.qrcodeDetail.visible && (
        <QrcodeDetailModal
          formData={state.qrcodeDetail.data}
          mode={state.qrcodeDetail.mode}
          onClose={closeEditModal}
          onSuccess={() => {
            handleSuccess?.();
            closeEditModal();
          }}
          visible={state.qrcodeDetail.visible}
        />
      )}
      {state.docConfig.visible && (
        <DocConfigModal
          onClose={closeConfigModal}
          onSuccess={() => {
            handleSuccess?.();
          }}
          visible={state.docConfig.visible}
        />
      )}
      {state.qrcodeView.visible && (
        <QrcodeViewModal
          onClose={closeViewModal}
          qrcodeImgUrl={state.qrcodeView.imgUrl}
          visible={state.qrcodeView.visible}
        />
      )}
    </>
  );

  return {
    modalHandlers,
    modals,
  };
};

还可以进一步简化,从打开和关闭维度看只有两个action,只不过打开和关闭的是不同的模态框,即不同的ModalState中的Key。

复制代码
export const useModals = (handleSuccess?: () => void) => {
  const [state, dispatch] = useReducer(modalReducer, initialModalState);

  const show = (key: ModalKey, payload?: any) =>
    dispatch({ type: 'SHOW', key, payload });
  const close = (key: ModalKey) => dispatch({ type: 'CLOSE', key });

  const modalHandlers = {
    onShowEditModal: (mode: 'edit' | 'create', data?: QrCodeRecord) =>
      show('qrcodeDetail', { mode, data }),
    onShowConfigModal: () => show('docConfig'),
    onShowViewModal: (imgUrl: string) => show('qrcodeView', { imgUrl }),
  };

  const modals = (
    <>
      {state.qrcodeDetail.visible && (
        <QrcodeDetailModal
          formData={state.qrcodeDetail.data}
          mode={state.qrcodeDetail.mode}
          onClose={() => close('qrcodeDetail')}
          onSuccess={() => {
            handleSuccess?.();
            close('qrcodeDetail');
          }}
          visible
        />
      )}
      {state.docConfig.visible && (
        <DocConfigModal
          onClose={() => close('docConfig')}
          onSuccess={handleSuccess}
          visible
        />
      )}
      {state.qrcodeView.visible && (
        <QrcodeViewModal
          onClose={() => close('qrcodeView')}
          qrcodeImgUrl={state.qrcodeView.imgUrl}
          visible
        />
      )}
    </>
  );

  return { modalHandlers, modals };
};

后续如果还想添加页面只需要在ModalState、initialModalState以及钩子内部的modalHandlers中、JSX部分加就好

相关推荐
骑着小黑马1 分钟前
Electron + Vue3 + AI 做了一个新闻生成器:从 0 到 1 的完整实战记录
前端·人工智能
Sailing3 分钟前
LLM 调用从 60s 卡死降到 3s!彻底绕过 tiktoken 网络阻塞(LangChain.js 必看)
前端·langchain·llm
洋洋技术笔记3 分钟前
计算属性与侦听器
前端·vue.js
用户814486958114 分钟前
“马上”有惊喜:在 Rokid 灵珠平台上构建 FPS 级 AR 红包雷达应用
前端
李剑一10 分钟前
拿来就用!Vue3+Cesium 飞入效果封装,3D大屏多场景直接复用
前端·vue.js·cesium
SimonKing22 分钟前
OpenCode AI编程助手如何添加Skills,优化项目!
java·后端·程序员
天蓝色的鱼鱼38 分钟前
都2026年了还不会Vite插件开发?手写一个版本管理插件,5分钟包会!
前端·vite
苏武难飞1 小时前
分享一个33号远征队的效果!
前端
鹏程十八少1 小时前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
亿元程序员1 小时前
这款值68亿的游戏,你不实战一下吗?安排!
前端