美文网首页
🌧️辰 ❤️京!为了少写两行代码,我居然写了这样的hooks

🌧️辰 ❤️京!为了少写两行代码,我居然写了这样的hooks

作者: 布可booker | 来源:发表于2023-01-02 12:29 被阅读0次

    背景是个常见的需求,表格场景中的常见crud交互会涉及这几个元素:

    • 新增按钮
    • 表格row中的 编辑按钮
    • 新增用的form表单弹窗
    • 编辑用的form表单弹窗

    每次重复写modal的visible以及复显旧值,让我十分恶心🤢,所以封装了一个hooks,来解决这个问题。

    demo

    import useModalForm from '@/utils/useModalForm';
    import {
      ModalForm,
      ProFormDatePicker,
      ProFormText,
    } from '@ant-design/pro-components';
    import { Button, Space, Form, Divider } from 'antd';
    import * as React from 'react';
    
    export interface ModalFormDemoProps {
      children: any;
    }
    const ModalFormDemo: React.FC<ModalFormDemoProps> = () => {
      const [form] = Form.useForm();
    
      const formModalCtx = useModalForm({
        onCancel() {
          form.resetFields();
        },
        trigger() {
          return <Button>新建</Button>;
        },
        editTrigger({ entity }) {
          return (
            <Button
              onClick={() => {
                formModalCtx.setEntity(entity);
                form.resetFields();
                form.setFieldsValue(entity);
              }}
            >
              编辑 {entity.name}
            </Button>
          );
        },
        modal({ entity }) {
          const txt = entity ? '编辑' : '新建';
    
          return (
            <ModalForm
              title={`${txt}表单`}
              form={form}
              onFinish={async (values) => {
                if (entity) {
                  console.log('编辑接口', entity, values);
                } else {
                  console.log('新建接口', values);
                }
              }}
            >
              <ProFormText label="名称" name="name" />
              <ProFormDatePicker label="日期" name="date" disabled={!!entity} />
            </ModalForm>
          );
        },
      });
      return (
        <div>
          <Space>{formModalCtx.renderTrigger()}</Space>
          <Divider />
          {
            // 模拟表格列表等情况中接口获取的数据
            [
              { id: 1, name: '原名称', date: '2023-01-01' },
              { id: 2, name: '另一个', date: '2023-02-01' },
            ].map((item) => (
              <div>{formModalCtx.renderEditTrigger({ entity: item })}</div>
            ))
          }
          {formModalCtx.renderModal()}
        </div>
      );
    };
    export default ModalFormDemo;
    
    

    ui

    新建时:


    add.png

    编辑时:


    edit.png

    最后表单提交时拿到的数据:


    finish.png

    useModalForm.tsx

    import * as React from 'react';
    import { useCallback, useRef, useState } from 'react';
    
    interface Params<E> {
      modal: (params: { entity: E | null }) => React.ReactElement;
      trigger: (any?: any) => React.ReactElement;
      editTrigger?: (any?: any) => React.ReactElement;
      onCancel?: () => void;
      isUseOnCancel?: boolean;
    }
    
    function useModalForm<Entity = any>({
      modal,
      trigger,
      editTrigger,
      onCancel,
      isUseOnCancel = true,
    }: Params<Entity>) {
      const [open, setOpen] = useState(false);
      const entity = useRef<any>(null);
      const setEntity = useCallback((p) => (entity.current = p), []);
    
      const onVisibleChange = (visible: boolean) => {
        if (!visible) {
          setEntity(null);
          onCancel?.();
        }
        setOpen(visible);
      };
    
      const renderTrigger = (any: any = {}) => {
        // @ts-ignore
        const dom = trigger({ ...any });
    
        return React.cloneElement(dom, {
          ...dom.props,
          onClick: async (e: any) => {
            setOpen((s) => !s);
            dom.props?.onClick?.(e);
          },
          className: `${dom.props.className || ''} trigger`,
        });
      };
    
      const renderEditTrigger = (any: any = {}) => {
        if (!editTrigger) {
          return null;
        }
        // @ts-ignore
        const dom = editTrigger({ ...any });
        return React.cloneElement(dom, {
          ...dom.props,
          onClick: async (e: any) => {
            setOpen((s) => !s);
            dom.props?.onClick?.(e);
          },
          className: `${dom.props.className || ''} trigger`,
        });
      };
    
      const renderModal = (rest: any = {}) => {
        // @ts-ignore
        const dom = modal({ entity: entity.current, ...rest });
        const newProps = {
          ...dom.props,
          open,
          visible: open,
          onVisibleChange,
          onCancel() {},
        };
        if (isUseOnCancel) {
          newProps.onCancel = () => {
            setEntity(null);
            onCancel?.();
            setOpen(false);
          };
        }
    
        return React.cloneElement(dom, newProps);
      };
    
      return {
        renderModal,
        renderTrigger,
        renderEditTrigger,
        setEntity,
      };
    }
    
    export default useModalForm;
    
    

    useModalForm可以用在类似的场景当中。我在demo中使用了@ant-design/pro-components的ModalForm,不得不说真香。用普通的modal,稍稍封装的兼容逻辑也是一样可以的。

    或许有的朋友会觉得为了每次少写下visible变量,整这么麻烦没必要。我是这样想的,这一套表格的crud逻辑十分常见,而且流程十分的类似。封装重复逻辑,不仅是每次少写点代码,而且可以在写代码时更关注于业务本身,这种重复逻辑可以有一种一笔带过的顺畅感。

    另外考虑的一个问题是,我参考了ahooks库以及其他关于hooks的文章,都没有见到用hooks返回render的写法。我自己的理解,render也是一种函数,理论上应该完全可以封装进hooks。

    这个版本是刚出炉的版本,大家给点意见。

    相关文章

      网友评论

          本文标题:🌧️辰 ❤️京!为了少写两行代码,我居然写了这样的hooks

          本文链接:https://www.haomeiwen.com/subject/mgwhcdtx.html