记录react实现选择框一二级联动出现的问题

news2024/11/17 17:45:11

需求:用户在选择第一个选择框的选项后,第二个选择框的选项会根据第一个选择框的选择动态更新。如图所示

在这里插入图片描述
在这里插入图片描述

出现的问题

一级分类选择之后二级分类没有数据,第二次重新选择一级分类的时候,二级分类就会有值。

第一次点击截图:

在这里插入图片描述

第二次点击截图:

在这里插入图片描述

解决方法

因为之前的代码使用我们自己封装的ProTable组件,最后没有使用组件,而是直接使用ProTable,没有报错

代码如下:

const HeadSlideManager = () => {
	// 其他部分省略
	const [firstClassify, setFirstClassify] = useState([]);//一级分类
	const [secondClassify, setSecondClassify] = useState([]);//二级分类
	const [valueEnumList, setValueEnumList] = useState([]);//二级分类所有数据
	
	useEffect(() => {
       const fetchData = async () => {
           try {
               // const res = await getClassify(1) 请求一级分类数据
               const res = await API.getFirstClassify();
               if (res.code === 0) {
                   setFirstClassify(res.data.map(item => ({ value: item.id, label: item.name })))
               } else {
                   console.error('Error fetching data:', res.message);
               }
               // const res = await getClassify(2) 请求二级分类数据
               const ress = await API.getSecondClassify();
               if (ress.code === 0) {
                   setValueEnumList(ress.data) // 保存二级分类所有数据
               } else {
                   console.error('Error fetching data:', res.message);
               }
           } catch (error) {
               console.error('Error fetching data:', error);
           }
       }
       fetchData();
   	}, [])
   	
	const handleFirstClassifyChange = (value) => {
		setTimeout(() => {
		// 设置具体的二级分类数据,也就是根据一级分类定二级分类
		setSecondClassify(valueEnumList.filter(item => item.pid === value).map(item => ({ value: item.id, label: item.name })))
		  }, 10)
	}
	
	const columns = [
	     {
	         title: <FormattedMessage id="pages.cms.table.video.head.slide.classify.1" />,
	         dataIndex: 'first_classify',
	         ellipsis: true,
	         align: 'center',
	         valueType: 'select', // 表单搜索设置为选择框
	         fieldProps: {
	             options: firstClassify,
	             onChange: handleFirstClassifyChange,
	         },
	     },
	     {
	         title: <FormattedMessage id="pages.cms.table.video.head.slide.classify.2" />,
	         dataIndex: 'second_classify',
	         ellipsis: true,
	         align: 'center',
	         valueType: 'select',
	         fieldProps: {
	             options: secondClassify,
	         },
	     },
	]
	
	return (
       <PageHeaderWrapper ghost={false}>
           <div>
               <ProTable
                   columns={columns}
                   rowKey="id"
                   options={false}
                   request={API.videoHeadSlide}
                   pagination={{
                       pageSize: 10,
                       current: 1,
                       showSizeChanger: true,
                       showQuickJumper: true,
                       pageSizeOptions: [10, 20, 30]
                   }}
                   toolBarRender={() => [addButton]}
                   scroll={{ x: 'max-content' }}
               />
           </div>
       </PageHeaderWrapper>
   );
};
export default HeadSlideManager;

我们封装的ProTable组件

const PaginationTable = ({
  request: defaultRequest,
  onChange,
  pagination: defaultPagination,
  params: defaultParams,
  columns: defaultColumns,
  form: defaultForm,
  queryTransfer,
  actionRef: defaultAction,
  ...props
}) => {
  const aRef = useRef();
  const actionRef = defaultAction || aRef;

  const [first, setFirst] = useState(true); // 第一次进入页面
  const [pagination, setPagination] = useState({
    pageSize: Number(history.location.query.size || 10),
    current: Number(history.location.query.page || 1),
    showQuickJumper: true,
    size: 'default',
    ...defaultPagination,
  });
  const [lastSorter, setLastSorter] = useState(
    history.location.query.sort || history.location.query.sort_by
      ? {
          sort: history.location.query.sort,
          sort_by: history.location.query.sort_by,
        }
      : undefined,
  );

  const correctQuery = ({ page = 1, size = 10, ...other }) => {
    setPagination({ ...pagination, current: page, pageSize: size });
    history.replace({ query: { page, size, ...other } });
  };

  const request = async ({ current, pageSize: size, ...p }, rSorter, filter) => {
    setFirst(false);
    let page = current;
    if (first) {
      // fix: 第一次进入页面表单提交导致page变1
      page = pagination.current;
      if (actionRef.current?.pageInfo) actionRef.current.pageInfo.current = page;
    }
    let sorter;
    if (rSorter) {
      const keys = Object.keys(rSorter);
      if (keys.length > 0) {
        sorter = { sort: { ascend: '1', descend: '-1' }[rSorter[keys[0]]], sort_by: keys[0] };
      }
    }
    setLastSorter(sorter);
    if (!simpleIsSame(lastSorter, sorter)) {
      page = 1;
    }
    const notEmptyParams = { ...p, page, size };
    Object.keys(notEmptyParams).forEach((key) => {
      const val = notEmptyParams[key];
      if (val === null || val === undefined || val.toString().length === 0) {
        delete notEmptyParams[key];
      }
    });
    correctQuery({ ...notEmptyParams, ...sorter });
    if (defaultRequest) {
      const res = await defaultRequest({ ...notEmptyParams, ...sorter, ...filter });
      if (res.code === 0) {
        const total = typeof res.total === 'number' ? res.total : 0;
        if (!res.data?.length) {
          let prevPage = page - 1;
          if (typeof res.total === 'number') {
            const totalPage = Math.ceil(res.total / size);
            if (prevPage > totalPage) prevPage = totalPage;
          }
          if (page > 1) page = prevPage;
        }
        setPagination((currentPg) => ({ ...currentPg, current: page, total }));
      }
      return res;
    }
    return { success: false, data: [] };
  };

  // query由form, sorter, pagination和params组成
  const getFormAndParamsFromQuery = () => {
    const { page, size, sort, sort_by: sortBy, ...query } = history.location.query;
    Object.keys(query).forEach((q) => {
      if (query[q] === '') {
        query[q] = undefined;
      }
    });
    return query;
  };

  const [form] = Form.useForm();

  const [params, setParams] = useState(defaultParams);

  useEffect(() => {
    const queryParams = getFormAndParamsFromQuery();
    if (first) {
      // 第一次进入设置表单数据
      let fieldsValue;
      if (queryTransfer) {
        fieldsValue = queryTransfer(queryParams);
      } else {
        fieldsValue = {};
        Object.keys(queryParams).forEach((k) => {
          if (defaultColumns?.find((c) => c.dataIndex === k)) fieldsValue[k] = queryParams[k];
        });
      }
      if (fieldsValue && Object.keys(fieldsValue).length > 0) {
        form?.setFieldsValue(fieldsValue);
        form?.submit(); // page会变1
      }
    } else {
      // 参数修改后设置页数为1
      let same;
      if (!defaultParams) same = false;
      else
        same = Object.keys(defaultParams).every((k) => {
          if (queryParams[k] === defaultParams[k]) return true;
          if (Array.isArray(defaultParams[k]) && typeof queryParams[k] === 'string')
            return defaultParams[k].length === 1 && defaultParams[k][0] === queryParams[k];
          if (Array.isArray(defaultParams[k]) && Array.isArray(queryParams[k]))
            return defaultParams[k].join(',') === queryParams[k].join(',');
          return false;
        });
      if (!same) setPagination({ ...pagination, current: 1 });
    }
    setParams(defaultParams);
  }, [defaultParams]);

  const columns = useMemo(() => {
    const { query } = history.location;
    if (query.sort_by)
      return defaultColumns.map((v) => {
        if (v.sorter && query.sort_by === v.dataIndex) {
          return { ...v, defaultSortOrder: { 1: 'ascend', '-1': 'descend' }[query.sort] };
        }
        return { ...v, defaultSortOrder: undefined };
      });
    return defaultColumns;
  }, [defaultColumns]);

  return (
    <ProTable
      params={params}
      request={request}
      onChange={(tPagination, filter, sorter, extra) => {
        setPagination(tPagination);
        if (onChange) onChange(tPagination, filter, sorter, extra);
      }}
      actionRef={actionRef}
      form={{ form, autoFocusFirstInput: false, ...defaultForm }}
      pagination={pagination.current === 0 ? undefined : pagination}
      columns={columns}
      search={{ defaultCollapsed: false }}
      tableAlertRender={false}
      revalidateOnFocus={false}
      {...props}
    />
  );
};

export default PaginationTable;

错误分析过程

通过查看第一次点击截图,我发现第一次valueEnumList没有值,第二次选择一级分类的时候valueEnumList有值,所以我首先怀疑是这段代码引起的问题,因为在React中setXxx方法是一个异步函数,可能导致在 handleFirstClassifyChange 函数中的 valueEnumList 并没有及时更新到最新的状态。

seEffect(() => {
    const fetchData = async () => {
        try {
            
            const ress = await API.getSecondClassify();
            if (ress.code === 0) {
            	// 这行代码引发问题
                setValueEnumList(ress.data) // 
            } else {
                console.error('Error fetching data:', res.message);
            }
        } catch (error) {
            console.error('Error fetching data:', error);
        }
    }
    fetchData();
}, [])

最后根据百度,gpt提示,使用 useEffect 来监听 valueEnumList 的变化,并在变化后更新 secondClassify 的状态,同时使用函数式更新来设置 secondClassify 状态。函数式更新可以确保在设置状态时访问到的状态是最新的。也就是添加下面的代码

// 监听 valueEnumList 变化,更新 secondClassify
useEffect(() => {
    // 默认第一个选择框的值为空,第二个选择框也应该为空
    setSecondClassify([]);
}, [valueEnumList]);
const handleFirstClassifyChange = (value) => {
    // 使用函数式更新确保访问到的 valueEnumList 是最新的状态
    setSecondClassify(prevSecondClassify => {
        // 根据第一个选择框的值筛选出第二个选择框的选项
        return valueEnumList.filter(item => item.pid === value).map(item => ({ value: item.id, label: item.name }));
    });
};

添加之后还是不行,最后试了各种方法,一句话就是不行。

讲讲最后为什么突然使用原生的ProTable,而没有使用封装的ProTable(* 原理还没有弄明白)

前面自己百度,gpt那么多,都没有解决,只好找博士师兄帮忙,师兄也是研究了很长时间,最后不知道为什么点到封装的ProTable组件页面,就从头到尾看了一遍代码,然后就怀疑是useMemo这个Hooks影响的,最后就尝试使用原生的ProTable,然后发现就没有问题了。师兄就忙自己的事了。

const columns = useMemo(() => {
    const { query } = history.location;
    if (query.sort_by)
      return defaultColumns.map((v) => {
        if (v.sorter && query.sort_by === v.dataIndex) {
          return { ...v, defaultSortOrder: { 1: 'ascend', '-1': 'descend' }[query.sort] };
        }
        return { ...v, defaultSortOrder: undefined };
      });
    return defaultColumns;
}, [defaultColumns]);

工程人嘛,先解决问题,最后再研究原理(嘿嘿嘿)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

提升办公效率的利器—OnlyOffice文档8.1深度体验

目录 1. 前言 2. 安装 3. 基本使用 3.1 文档编辑 3.2 表格处理 3.3 演示文稿 4. 个人评价 5. 总结 1. 前言 在当今信息化时代&#xff0c;办公软件已经成为我们日常工作中不可或缺的工具。无论是撰写文档、处理数据&#xff0c;还是制作演示文稿&#xff0c;办公软件的…

数电大作业-四输入表决器

&#xff08;PCB和multisim仿真画的有很大问题&#xff0c;没有VCC输入和GND&#xff0c;没学过直接裸画的&#xff0c;之后会好好看视频学习&#xff09; 应用背景&#xff1a; 四个评委&#xff0c;三个及以上评委同时按下通过按钮时&#xff0c;选手才能通过。否则不通过。…

【Flink metric(3)】chunjun是如何实现脏数据管理的

文章目录 一. 基础逻辑二. DirtyManager1. 初始化2. 收集脏数据并check3. 关闭资源 三. DirtyDataCollector1. 初始化2. 收集脏数据并check3. run&#xff1a;消费脏数据4. 释放资源 四. LogDirtyDataCollector 一. 基础逻辑 脏数据管理模块的基本逻辑是&#xff1a; 当数据消…

适用于轨道交通专用的板卡式网管型工业以太网交换机

是网管型 CompactPCI板卡式冗余环网交换机。前面板带有6个 10/100/1000Base-T(X)M12接口。后面的CPCI接口有 8个10/100/1000Base-T (X) 以太网接口。 是特别为轨道交通行业EN50155标准要求而设计的坚固型交换机。它同时具有以下特性&#xff1a; ● 支持2线以太网距离扩展端口&…

Crypto++ 入门

一、简介 Crypto&#xff08;也称为CryptoPP、libcrypto或cryptlib&#xff09;是一个免费的开源C库&#xff0c;提供了多种加密方案。它由Wei Dai开发和维护&#xff0c;广泛应用于需要强大加密安全的各种应用程序中。该库提供了广泛的加密算法和协议的实现&#xff0c;包括&…

【通用技巧】自动获取日志存放路径,无需手动修改配置文件

我们在部署环境的时候&#xff0c;常常会手动修改一些配置文件的存放地址&#xff0c;比如日志的路径、截图的路径&#xff0c;这是因为我们的环境不一样&#xff0c;部署应用的位置也不一样导致的。如果位置写死了&#xff0c;那么就会造成通用性很差&#xff0c;所以我们经常…

明明设置允许跨域,为什么还会出现跨域请求的问题

一、问题 在微服务项目中&#xff0c;明明已经设置允许跨域访问&#xff1a; 为什么还会出现跨域请求问题&#xff1f; 二、为什么 仔细查看错误提示信息&#xff1a;When allowCredentials is true, allowedOrigins cannot contain the special value "*" since t…

NestJs连接数据库

文章目录 一、下载 MySql 数据库二、下载VsCode插件查询、插入数据 一、下载 MySql 数据库 NestJS连接数据库我选择的是MySql&#xff0c;首先先安装nestjs/typeorm 、typeorm、 mysql2 执行命令&#xff1a; pnpm install nestjs/typeorm typeorm mysql2 -S 连接数据库需要你…

C语言小例程28/100

题目&#xff1a;利用递归方法求5!。 程序分析&#xff1a;递归公式&#xff1a;fnfn_1*4! #include <stdio.h>int main() {int i;int fact(int);for(i0;i<6;i){printf("%d!%d\n",i,fact(i));} } int fact(int j) {int sum;if(j0){sum1;} else {sumj*fac…

震惊!这样制作宣传册,效果竟然如此惊人!

在当今社会&#xff0c;宣传册作为一种重要的宣传手段&#xff0c;其制作质量直接影响到宣传效果。而令人震惊的是&#xff0c;现在有些制作宣传册的方法&#xff0c;其效果竟然如此惊人&#xff01;今天&#xff0c;教大家如何制作宣传册吧&#xff01; 首先&#xff0c;我们要…

南京邮电大学计算机网络实验二(网络路由器配置RIP协议)

文章目录 一、 实验目的和要求二、 实验环境(实验设备)三、 实验步骤四、实验小结&#xff08;包括问题和解决方法、心得体会、意见与建议等&#xff09;五、报告资源 一、 实验目的和要求 掌握思科路由器的运行过程&#xff0c;掌握思科路由器的硬件连线与接口&#xff0c;掌…

【研究】国内外大模型公司进展

2022年11月&#xff0c;OpenAI推出基于GPT-3.5的ChatGPT后&#xff0c;引发全球AI大模型技术开发与投资热潮。AI大模型性能持续快速提升。以衡量LLM的常用评测标准MMLU为例&#xff0c;2021年底全球最先进大模型的MMLU 5-shot得分刚达到60%&#xff0c;2022年底超过70%&#xf…

稀奇古怪的解压视频都哪里找的?6个古怪稀奇解压素材网站分享

在这个信息泛滥的时代&#xff0c;解压视频已经成为我们日常生活中的调味剂。特别是那些奇特而有趣的视频&#xff0c;它们能够立刻抓住我们的眼球&#xff0c;带来独一无二的视觉享受和心理上的放松。但你可能会好奇&#xff0c;这些引人注目的解压视频都可以在哪里找到呢&…

华为eNSP模拟器下载地址

一、依赖程序 VirtualBox&#xff1a;https://cloud.rsecc.cn/softlink/VirtualBox-5.2.26-128414-Win.exe WinPcap&#xff1a;https://cloud.rsecc.cn/softlink/WinPcap_4_1_3.exe Wireshark&#xff1a;https://cloud.rsecc.cn/softlink/Wireshark-win64-3.0.6.exe 需要…

代码随想录-Day38

509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;其中 …

【Android】我的手机在...自己下载...那个(浅析Intent基础运用)

【Android】我的手机在…自己下载…那个&#xff08;浅析Intent基础运用&#xff09; 在Android开发中&#xff0c;Intent&#xff08;意图&#xff09;是一个非常重要的概念。它不仅仅是用于在应用程序的各个组件之间进行通信的工具&#xff0c;也是启动新的Activity、Servic…

FydeOS导入VMware虚拟机之后,如何扩展系统硬盘大小?

前言​ 最近查询FydeOS系统的小伙伴不在少数啊&#xff01;可见这个系统是相当nice的&#xff0c;小伙伴们都是尝试尝试。 看到有不少小伙伴通过VMware虚拟机使用FydeOS&#xff0c;那么你就肯定知道官方包导入VMware之后&#xff0c;硬盘只显示分区了20GB。 如果这时候使用Fy…

【Java核心技术13】Java中的构造器与析构器:深入解析与代码示例

引言 所有文章均为原创验证&#xff0c;您随手的 关注、点赞、收藏 是我创作最大的动力。 示例代码地址&#xff1a;https://gitee.com/code-in-java/csdn-blog.git 在面向对象编程语言中&#xff0c;构造器和析构器是类生命周期管理的关键部分。构造器负责初始化新创建的对象&…

Java NIO(一) 概述

NIO主要用于以少量线程来管理多个网络连接&#xff0c;处理其上的读写等事件。在大量连接情况下&#xff0c;不管是效率还是空间占用都要优于传统的BIO。 Java NIO 由以下几个核心部分组成&#xff1a; Channel Buffer Selector Selector 如果你的应用打开了多个连接&#x…

Hack The Box-Axlle【更新中】

总体思路 XLL-EXEC->hta反弹shell->重置用户密码->重写二进制文件 信息收集&端口利用 nmap -sSVC axlle.htb开放了一大堆端口&#xff0c;这里先挑重点的80端口和445端口查看 80端口主页只有一个邮箱账号&#xff0c;对其目录扫描和子域名扫描 dirsearch -u h…