react封装指令式组件

react指令式这个说法第一次接触是在antd组件库中,当时感觉眼前一亮,就觉得很多东西会变得很方便,将很多无关紧要的组件从显式声明解耦出来,也减少了页面中变量状态的维护

于是当时扒了扒源码,借鉴了一下,封装了一个万用的组件

首先封装了一个hook,这个hook决定了调用声明式组件的api和组件显示的位置

ts 复制代码
import usePatchElement from 'antd/es/_util/hooks/usePatchElement';
import React, { useMemo, useState } from 'react';
import { useCreation } from 'ahooks';
import { createPortal } from 'react-dom';
import { InstructionMountArgs, TsUtils } from '@/utils/TsUtils';

interface Apis {
  closeFn: (result?: any) => void,
  promise: Promise<any>
}

export const useElementsContextHolder = () => {
  const [elements, patchElement] = usePatchElement();
  const [instanceMap] = useState(new Map<string, Apis>());

  const fns = useCreation(() => ({
    instructionMountPromise<P, R>({ Component, props, getContainer }: InstructionMountArgs<P>) {
      const { promise, resolve } = TsUtils.getSeparatePromise<R>();
      const key = TsUtils.getUUID();
      let element: React.ReactElement = React.createElement(Component, { ...props, key, closeResolve: (data?: R) => this.closeElement(key, data) });
      if (getContainer) {
        element = createPortal(element, getContainer);
      }
      const removeElementFn = patchElement(element);
      const closeFn: Apis['closeFn'] = (result) => {
        removeElementFn();
        instanceMap.delete(key);
        resolve(result);
      };
      instanceMap.set(key, {
        closeFn,
        promise,
      });

      return promise;
    },
    closeElement<R>(key: string, result?: R) {
      instanceMap.get(key).closeFn(result);
    },
  }), []);

  return [fns, (
    <React.Fragment>
      {elements}
    </React.Fragment>
  )] as const;
};

api最好是全局调用,当然不是全局也可以,毕竟迭代中总会出现一些额外的情况

这里我将api挂载在一个全局的appInstance中

ts 复制代码
export class AppInstance {
  ContextHolder: ReturnType<typeof useElementsContextHolder>[0];

  getContextHolder() { return this.ContextHolder; }

  setContextHolder(ContextHolder: ContextHolderPlugin['ContextHolder']) {
    this.ContextHolder = ContextHolder;
  }

  ContextHolderComponent() {
    const [contextHolderApi, contextHolder] = useElementsContextHolder();
    useMount(() => {
      this.setContextHolder(contextHolderApi);
    });

    return contextHolder;
  }
}

<AntdConfigProvider>
   <appInstance.ContextHolderComponent />
</AntdConfigProvider>

最后一步就是包装指令式命令了,我这里用antd的选择文件举例,antd的选择文件需要显式声明,而包装之后只需在点击中promise等待就够了

ts 复制代码
import React, { FC, useRef } from 'react';
import { Upload, UploadProps } from 'antd';
import { css, cx } from '@emotion/css';
import { useMount } from 'ahooks';
import { InstructionMountProps } from '@/utils/TsUtils';
import { appInstance } from '@/runTime';

const SelectFiles: FC<UploadProps & InstructionMountProps> = (props) => {
  const ref = useRef<HTMLDivElement>();

  useMount(() => {
    ref.current.click();
  });

  return (
    <div className={cx(css`height: 0;overflow: hidden`)}>
      <Upload
        {...props}
        beforeUpload={(file, FileList) => {
          props.closeResolve(FileList);
          return Upload.LIST_IGNORE;
        }}
      >
        <div ref={ref}>上传</div>
      </Upload>
    </div>
  );
};

export const openSelectFilesPromise = (args: UploadProps) => {
  return appInstance.getContextHolder().instructionMountPromise<UploadProps, File[]>({
    Component: SelectFiles,
    props: args,
  });
};

例如我有一个流程:弹框输入文件名->选择文件->上传文件->弹框提示上传成功

按照声明式流程,至少需要显式声明两个弹框一个上传按钮,还需要变量控制是否显示弹框,但是使用指令式封装之后只需要全部在异步流程中就可以了

ts 复制代码
async click() {
    const name = await openInputModal();
    const [file] = await openSelectFilesPromise();
    await uploadFile();
    await showUploadSuccessModal();
}

显式声明写的太麻烦了,这里就不写了,比指令式要麻烦不知道多少

相关推荐
半生过往33 分钟前
2025 前端动效实战指南:Vue Bits & React Bits 深度拆解(功能 / 复用 / 高频问题处理)
前端·vue.js·react.js
程序员包打听35 分钟前
Vitest 4.0 重磅发布:Browser Mode 正式稳定,前端测试进入新纪元
前端
BumBle36 分钟前
UniApp 多页面编译优化:编译时间从10分钟到1分钟
前端
星链引擎40 分钟前
大语言模型的技术突破与稳定 API 生态的构建
前端
还是大剑师兰特40 分钟前
TypeScript 面试题及详细答案 100题 (71-80)-- 模块与命名空间
前端·javascript·typescript
BumBle40 分钟前
使用 SortableJS 实现vue3 + Element Plus 表格拖拽排序
前端·vue.js·element
玉宇夕落40 分钟前
HTML5 音乐敲击乐静态界面
前端
海在掘金6112741 分钟前
告别"拼写错误":TS如何让你的代码"字字精准"
前端
用户479492835691541 分钟前
什么是XSS攻击,怎么预防,一篇文章带你搞清楚
前端·javascript·安全
摸着石头过河的石头42 分钟前
深入理解JavaScript事件流:从DOM0到DOM3的演进之路
前端·javascript·性能优化