Ant Design Pro + Ant Design + React 踩坑记录

2023-11-19

1. 自定义封装组件

1.1 组件通信

1.1.1 父传子

在本项目中对因为删除组件比较通用,所以对删除组件进行了封装。

如下图我们对定义了通用删除组件,通过props传入回调方法,这样页面上只需要引用该组件,并传入自定义的删除函数即可引入。

我们可以使用props来进行组件间的通信,但是props入参是只读的,所以无法对其进行修改,所以需要传入回调函数

我们可以在父组件中传入函数并给入父组件的对象入参,在子组件中通过props传入的function函数来进行调用

父传子样例:

子组件

type IsvDeleteProps = {
  deleteCallBack: Function;
};

const IsvDelete: React.FC<IsvDeleteProps> = (props: any) => {
  function showDeleteConfirm() {
    confirm({
      title: '请问是否需要删除?',
      icon: <ExclamationCircleOutlined />,
      content: '',
      okText: '是',
      okType: 'danger',
      cancelText: '否',
      onOk() {
        props.deleteCallBack();
      },
      onCancel() {},
    });
  }

  return (
    <>
      <a
        onClick={() => {
          showDeleteConfirm();
        }}
      >
        删除
      </a>
    </>
  );
};

export default IsvDelete;

父组件:

function removeApp(appCode: string) {
    removeRule({ appCode }).then((resp) => {
      if (resp.success) {
        message.success('删除成功');
      }
      formRef.current.submit();
    });
  }


<IsvDelete
	deleteCallBack={() => {
  		removeApp(record.appCode);
	}}
/>,

1.1.2 子传父

我们也可以通过props传入回调函数,接收子组件传入的对象,来控制子组件的(例如:formRef,可以通过父组件控制子组件)

如样例:自组件在初始化时会执行props.formRef,此时将定义好的form传入回调函数中,即可将子组件表单的form对象作为入参传入回调函数,父组件中定义的回调函数接受接收到子组件入参后将对象保存在父组件中,即可通过该对象控制子组件的form表单

子传父样例:

父组件

 //回调赋值
  const baseCallback = (formRef) => {
    basicFormRef = formRef;
  };


<BasicInformationView
          fromRef={baseCallback}
/>

子组件:

 const [form] = Form.useForm();
  if ('fromRef' in props) {
    props.fromRef(form);
  }

2. 函数组件hook钩子

介绍: https://juejin.cn/post/6844904036433395720 基用React Hooks + Antd快速实现一个列表页

hook支持函数组件模拟类式组件生命周期

模拟组件初次挂载成功后钩子componentDidMount

如下,第二个参数为[]数组,那么在页面初次挂载时会触发一次,执行回调函数

useEffect(() => {
    xxxxx
  }, []);

模拟组件更新后挂载钩子componentDidUpdate

如下,第二个参数为监听的对象,那么在页面监听对象发生变化时,执行回调函数

statsAA,props.xx发生变化时会

useEffect(() => {
    xxxxx
  }, [statsAA,props.xx]);

模拟组件解除挂载时的钩子componentWillUnMount

如下,在箭头函数中添加返回值,那么在页面解除绑定时会调用return的钩子函数

 useEffect(() => {
    return () => {
      xxx
    };
  }, []);

3.定时器相关问题

useState与useRef的使用场景区别:

(1) useState的值在每个rernder中都是独立存在的。而useRef.current则更像是相对于render函数的一个全局变量,每次他会保持render的最新状态。
useState值的更新会触发组件重新渲染,而useRef的current不会出发重渲染。

(2) 在使用定时器时,定时器需要使用useRef进行存储,如果定时器使用useStatus存储会出现在接触挂载时,获取state的值为undefined 导致定时器无法清除。

const autoLoad = useRef();

useEffect(() => {
    getLog();
    autoLoad.current = setInterval(() => {
      getLog();
    }, 5000);
    return () => {
      clearInterval(autoLoad.current);
    };
  }, []);

  function onChange(checked) {
    clearInterval(autoLoad.current);
    if (checked == true) {
      autoLoad.current = setInterval(() => {
        getLog();
      }, 5000);
    }
  }

4.form表单相关问题

4.1 表单项给初始值但提交表单时该值未提交

表单值初始化需要在Form.Item节点上通过initialValue进行初始化,而不是通过输入框控件的defaultValue属性进行初始化值

			<Form.Item
              label="代码类型"
              name="codeType"
              rules={[{ required: true, message: '请选择代码类型' }]}
              initialValue={props.basicCodeType}
            >
              <Select
                placeholder="请选择代码类型!"
                // disabled={isBan()}
                disabled={true}
                onChange={handleChangeCodeType}
              >
                {optionCodeTypes.map((itm) => (
                  <option key={itm}>{itm}</option>
                ))}
              </Select>
            </Form.Item>

4.2 表单复选框反显问题

表单中复选框在返显时,如果未添加valuePropName="checked"属性,导致复选框一直使用initialValue默认值,而不是使用表单的name传过来的值

				<Form.Item
                    name="enableLogCollect"
                    valuePropName="checked"
                    rules={[{ required: false }]}
                    noStyle={true}
                    initialValue={true}
                  >
                    <Checkbox disabled={isHide()}>系统自动配置</Checkbox>
                  </Form.Item>

4.3表单输入框联动方案

通过表单api : onValuesChange ,在表单值变化时都会调用起该函数,通过该函数监听指定name输入框的值,可以实现两个输入框值同步修改的问题

const formChange = (changedValues) => {
    if (changedValues.appName != undefined) {
      form.setFieldsValue({ gitAppName: changedValues.appName });
    }
  };

 <Form form={form} {...formItemLayout} onValuesChange={formChange}>
              <Form.Item
                  name="gitAppName"
                  rules={[{ required: true, message: '请输入代码仓库' }]}
                  noStyle
                >
                  <Input style={{ width: '35%' }} disabled={true} />
                </Form.Item>
            <Form.Item
              label="应用名称"
              name="appName"
              rules={[
                { required: true, message: '请输入应用名称含英文字母,数值,下划线,中横线等字符!' },
              ]}
            >
              <Input
                placeholder="applictaion1,含英文字母,数值,下划线,中横线等字符!"
                disabled={isBan() || props.optionType == 'edit'}
              />
            </Form.Item>
  </Form>

5. 表格相关问题

5.1 表格刷新由其他表单组件控制

修改其他表单项数据时,自动刷新表格

使用表格组件为proTable,通过params参数监听state对象值变化,并且在提交表格表单时会自动将params监听的值作为参数传入表格初始化的request的函数中。

proTable会在初始化时或者提交proTable自动生成的表单时触发request中的promise方法,当识别到promise方法返回内容重success属性为true时,才会将返回值渲染到表格中进行展示。

通过search api 可以对proTable的自动生成的搜索表单项进行自定义。

其中optionRender可以自定义表单按钮

rowkey一定要指定为改行数据的唯一值,否则会出现表格刷新后数据显示异常

ProColumns对象为对表格每列的定义,其中

hideInSearch 表示这列是否会自动生成检索表单

hideInTable 表示该列是否展示在表格中

render 可以控制该列显示的内容(比如需要对数据做处理后展示,或者该列为操作框)

valueType 可以定义通过该列自动生成的表单选项框的类型

const columns: ProColumns<DeploymentInformationListItem>[] = [
    {
      title: '环境选择',
      dataIndex: 'deployEnvId',
      hideInTable: true,
      valueType: 'select',
      formItemProps: {
        rules: [
          {
            required: true,
            message: '此项为必填项',
          },
        ],
      },
      valueEnum: optionEnv,
    },
    {
      title: 'ID',
      dataIndex: 'publishId',
      hideInSearch: true,
    },
    {
      title: '操作人',
      dataIndex: 'modifierName',
      hideInSearch: true,
    },
    {
      title: '操作类型',
      dataIndex: 'publishType',
      hideInSearch: true,
      render: (_, record) => {
        switch (record.publishType) {
          case 0:
            return '重启';
          case 1:
            return '正常发布';
          case -1:
            return '回滚';
        }
      },
    },
    {
      title: '状态',
      dataIndex: 'publishResult',
      hideInSearch: true,
      render: (_, record) => {
        switch (record.publishResult) {
          case -1:
            return '失败';
          case 0:
            return '构建中';
          case 1:
            return '成功';
        }
      },
    },
    {
      title: '代码分支',
      dataIndex: 'publishGitBranch',
      valueType: 'select',
      valueEnum: optionBranches,
      formItemProps: {
        rules: [
          {
            required: optionType,
            message: '此项为必填项',
          },
        ],
      },
    },
    {
      title: '所属环境',
      dataIndex: 'deployEnvTitle',
      valueType: 'textarea',
      hideInSearch: true,
    },
    {
      title: '发布备注',
      dataIndex: 'publishDesc',
      valueType: 'textarea',
      hideInTable: true,
    },
    {
      title: '操作时间',
      dataIndex: 'gmtModified',
      valueType: 'dateTime',
      hideInSearch: true,
    },
    {
      title: '操作',
      dataIndex: 'option',
      valueType: 'option',
      render: (_, record) => {
        let result = [];
        result.push(
          <a
            onClick={() => {
              history.push({
                pathname: '/deploymentmanagement/applicationdetails',
                state: {
                  applicationDetails_appCode: props.appCode,
                  applicationDetails_publishId: record.publishId,
                },
              });
            }}
          >
            详情
          </a>,
        );
        if (record.publishResult === 1) {
          result.push(
            <a
              onClick={() => {
                publishRollback(record.publishId);
                actionRef.current.reload();
              }}
            >
              回滚至当前版本
            </a>,
          );
        }
        return result;
      },
    },
  ];


<ProTable<DeploymentInformationListItem, DeploymentInformationListPagination>
            columns={columns}
            actionRef={actionRef}
            formRef={formRef}
            pagination={{
              defaultPageSize: 10,
            }}
            rowKey="publishId"
            optionAppNames={false}
            params={confirmAppCode}
            search={{
              //默认展开状态去掉"收起"选项
              defaultCollapsed: false,
              collapseRender: () => null,
              //宽度
              labelWidth: 'auto',
			  // 自定义table表单中的按钮信息
              optionRender: (searchConfig, formProps, dom) => [
                <a>当前K&S部署:</a>,
                <Button
                  type="primary"
                  onClick={() => {
                    setOptionType(true);
                    isPublishOrRestart(1);
                  }}
                >
                  发布
                </Button>,
                <Button
                  type="primary"
                  onClick={() => {
                    setOptionType(false);
                    isPublishOrRestart(0);
                  }}
                >
                  重启
                </Button>,
                <Button
                  type="primary"
                  onClick={() => {
                    setOptionType(false);
                    tableSearch();
                  }}
                >
                  搜索
                </Button>,
              ],
            }}
            request={(params, sorter, filter) => DeploymentTableList({ ...params, sorter, filter })}
            form={{
              ignoreRules: false,
            }}
          />

5.2 可编辑表格问题

初始化时展示对象必须给[],若未undefined,则整个控件无法显示

6.控制台报错问题

6.1 在componentWillMount钩子函数中不能修改state,只能有函数调用

如下例,因为在钩子函数中对state做了修改所以会导致报错

 useEffect(() => {
    getMenuAll(initialState.currentUser.tenantId).then((resp) => {
      const treeMenus = getMenu(resp.allowPageList);
      setTreeMenus(treeMenus);
    });
    if (props.optionType != 'add') {
      init();
    }
  }, []);

 if ('selectMenu' in props) {
    props.selectMenu(allCheckedKeys);
 }

在这里插入图片描述

修改为以下形式可以修复该报错

useEffect(() => {
    initMenu()
    if (props.optionType != 'add') {
      init();
    }
  }, []);

  const initMenu = () => {
    getMenuAll(initialState.currentUser.tenantId).then((resp) => {
      const treeMenus = getMenu(resp.allowPageList);
      setTreeMenus(treeMenus);
    });
  }

 useEffect(() => {
    if ('selectMenu' in props) {
      props.selectMenu(allCheckedKeys);
    }
  },[allCheckedKeys])

6.2 proTable自定义表单按钮时需要指定key

如下案例optionRender中的自定义按钮未指定key会导致一下报错

 <ProTable<UserListItem, UserListPagination>
        actionRef={actionRef}
        rowKey="uid"
        options={false}
        pagination={{
          pageSize: 10,
        }}
        formRef={formRef}
        search={{
          //默认展开状态去掉"收起"选项
          defaultCollapsed: false,
          collapseRender: () => null,
          //宽度
          labelWidth: 'auto',
          optionRender: (searchConfig, formProps, dom) => [
            // ...dom.reverse(),把重置和查询去掉
            <Button
              type="primary"
              icon={<PlusOutlined />}
              onClick={() =>
                history.push({
                  pathname: '/usermanagement/user/add',
                  state: {
                    optionType: 'add',
                  },
                })
              }
            >
              新建用户
            </Button>,
            <Button
              type="primary"
              onClick={() => {
                formRef.current.submit();
              }}
            >
              查询
            </Button>,
          ],
        }}
        request={userTableList}
        columns={columns}
      />

在这里插入图片描述

6.3 树形控件加载顺序导致显示问题

在这里插入图片描述

useEffect(() => {
    initMenu()
    if (props.optionType != 'add') {
      init()
    }
  }, []);

出现原因:由于initMenu为加载整个树形结构数据,但是init为加载勾选的节点,因为initMenu与init都为异步的,所以会出现加载勾选的节点加载完成的,但完整树形结构数据未加载完成,从而导致这个错误

优化后代码:

useEffect(() => {
    initMenu()
  }, []);

  const initMenu = () => {
    getMenuAll(initialState.currentUser.tenantId).then((resp) => {
      const treeMenus = getMenu(resp.allowPageList);
      setTreeMenus(treeMenus);
      if (props.optionType != 'add') {
        init()
      }
    });
  }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ant Design Pro + Ant Design + React 踩坑记录 的相关文章

随机推荐

  • Matlab导出动态链接库dll

    1 新建 m文件 内容 function c Add a b c a b end 保存为 Add m 2 命令行输入 gt gt mex setup MEX configured to use Microsoft Visual C 2013
  • 各种注释总结

    jsp注释 html注释
  • C语言—指针

    文章目录 1 指针 1 1 指针的定义 1 2 和 1 3 指针与堆内存 1 4 指针运算 1 5 常量指针与指针常量 1 5 1 常量指针 1 5 2 指针常量 1 6 函数指针 2 指针与数组 3 指针与函数 4 指针与链表 4 1 链
  • cmake中的编译选项

    CMake是一个跨平台的构建系统 它可以根据简单的配置文件生成各种平台的构建工具 例如Makefile Visual Studio项目文件等 CMake使用CMakeLists txt文件来描述项目的构建规则和依赖关系 在这个文件中 可以设
  • 浅谈opencv3.2中各个模块的简介

    3 2版本的模块说明 Opencv3 2模块 首先打开opencv modules hpp文件 可以看到对于各个功能模块的定义如下 This file defines the list of modules available in cur
  • 分享几个项目中用到的设计模式

    前言 之前项目中出于扩展性和有雅性的考虑 使用了多种设计模式进行项目框架的设计 主要的一些设计模式是单例模式 工厂模式 策略模式 责任链模式 代理模式这几种 现在依次讲讲这几个的主要是实现方式和在我们项目中的应用场景 核心设计模式分享 单例
  • WPF TextBlock 实现点击事件

    TextBlock 标签里定义MouseLeftButtonDown 事件 xaml cs
  • ICCV 2023

    ICCV 2023 MPI Flow 从单视角构建的多平面图像中学习光流 引言 主要贡献 Motivation 算法细节 Optical Flow Data Generation Independent Object Motions Dep
  • Node之使用dns模块解析域名

    引 在网络编程中 开发者更倾向于使用域名 而不是IP地址来指定网络连接的目标地址 在Node js中 提供dns模块 以实现域名查找及域名解析的处理 在dns模块中 提供了三个主方法及一系列便捷方法 其中三个主方法分别为用于将一个域名解析为
  • MySQL使用查询结果生成临时表

    MySQL中不支持对同一个表使用其查询结果更新or删除本表内数据 也就是update或delete后的where条件为针对相同表的select 解决方案是创建临时表做过度保存中间数据 可以直接使用查询结果来形成临时表 CREATE TABL
  • verilog奇数分频器的问题讲解(7分频为例)

    先不多哔哔 直接上代码 verilogHDL 代码的后面讲原理 module fenpin3 clk clk7 rst input clk rst 设置rst的目的是当rst 1的时候给cnt0和cnt1赋初值 output clk7 re
  • python sslerror_如何解决“不良握手”问题利用python请求时的SSLErrors

    I m trying to get access to the BambooHR API documentation here but I receive the following error params user username p
  • GREASELM: GRAPH REASONING ENHANCED LANGUAGE MODELS FOR QUESTION ANSWERING

    本文是LLM系列文章 针对 GREASELM GRAPH REASONING ENHANCED LANGUAGE MODELS FOR QUESTION ANSWERING 的翻译 GREASELM 图推理增强的问答语言模型 摘要 1 引言
  • 小霸王其乐无穷之函数回调

    小霸王游戏机是中国上一代备受欢迎的家用游戏机 它在1990年代初期开始流行 当时 由于游戏软件受限 国内的游戏市场相对匮乏 这使得小霸王游戏机成为许多70 80后童年时光中难忘的一部分 小霸王游戏机分为两大主要部分 游戏机本身和卡带 游戏机
  • Python中的CALL_FUNCTION指令

    在Python字节码中 CALL FUNCTION指令后跟的数字代表这次函数调用需要从栈上取出的参数的数量 具体来说 这个数字包括位置参数和关键字参数的数量 这个数字的低两位表示位置参数的数量 然后每两位表示一个关键字参数的数量 因此 如果
  • LLVM Language Reference Manual---阅读笔记

    文档地址 http llvm org docs LangRef html LLVM IR的标示符有两种基本类型 全局的和局部的 全局标示符以 开头 局部标示符以 开头 LLVM IR的标示符有三种形式 命名的 未命名的 常量 每一个Moud
  • Python pyecharts数据可视化

    Python pyecharts数据可视化 绘制精美图表 一 数据可视化 1 pyecharts介绍 2 初入了解 1 快速上手 2 简单的配置项介绍 3 案例实战 1 柱状图Bar 2 地图Map 省份 城市 地区 3 饼图Pie Pie
  • 【SMTP】【POP】电子邮件相关协议分析

    一 实验环境 通过普通路由器连接英特网的计算机一台 通过VMWare安装的Linux虚拟机一台 抓包工具 Wireshark 邮件处理软件 Foxmail 二 实验原理 SMTP工作原理 SMTP提供了一种邮件传输的机制 当收件方和发件方都
  • 公司实战 ElasticSearch+Kafka+Redis+MySQL

    一 需求 前一段时间公司要进行数据转移 将我们ES数据库中的数据转移到客户的服务器上 并且使用定时将新增的数据同步 在这过程中学到了很多 在此记录一下 二 技术栈 Mysql Redis ElasticSearch Kafka 三 方案 为
  • Ant Design Pro + Ant Design + React 踩坑记录

    1 自定义封装组件 1 1 组件通信 1 1 1 父传子 在本项目中对因为删除组件比较通用 所以对删除组件进行了封装 如下图我们对定义了通用删除组件 通过props传入回调方法 这样页面上只需要引用该组件 并传入自定义的删除函数即可引入 我