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

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

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

出现的问题

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

第一次点击截图:

在这里插入图片描述

第二次点击截图:

在这里插入图片描述

解决方法

因为之前的代码使用我们自己封装的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><ProTablecolumns={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变1page = 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 {// 参数修改后设置页数为1let same;if (!defaultParams) same = false;elsesame = 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 (<ProTableparams={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.mzph.cn/web/33504.shtml

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

相关文章

024.两两交换链表中的节点,用递归和 while 循环

题意 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 难度 中等 示例 输入&#xff1a;head [1,2,3,4] 输出&#xff1a;[…

什么是车载测试?车载测试怎么学!

1、车载测试是什么&#xff1f; 车载测试分很多种&#xff0c;有软件测试、硬件测试、性能测试、功能测试等等&#xff0c;每一项测试的内容都不一样&#xff0c;我们所说的车载测试主要指的是汽车软件的功能测试&#xff0c;也就是针对汽车实现的某一个功能&#xff0c;而进行…

vue3 vxe-grid列中绑定vxe-switch实现数据更新

1、先上一张图&#xff1a; <template #valueSlot"{ row }"><vxe-switch :value"getV(row.svalue)" change"changeSwitch(row)" /></template>function getV(value){return value 1;};function changeSwitch(row) {console.l…

Trilium windows上修改笔记目录,创建多个笔记空间方法

一开始使用trilium会非常的不舒服&#xff0c;不像是obsidian可以创建多个笔记空间&#xff0c;指定多个笔记目录。这里摸索到了解决方案 修改目录的方法一 ——修改系统环境变量 打开控制面板-系统-高级系统设置 新增如上条目 修改目录的方法二——直接写bat脚本运行 新建位…

深入理解Kubernetes:CNI源码解析

在容器化环境中&#xff0c;有效管理网络是至关重要的。容器网络接口&#xff08;CNI&#xff09;是一个标准&#xff0c;定义了容器应如何配置网络。本文将深入探讨 CNI 的基础知识&#xff0c;并带你了解 CNI 与 CRI 的关系。 什么是 CNI&#xff1f; CNI&#xff08;容器网…

Redis哨兵集群搭建

一、安装Redis 1.安装依赖 yum install -y gcc tcl2.将Redis压缩包解压到对应的目录 tar -zxvf redis-2.8.0.tar.gz mv redis-2.8.0 /usr/local3.编译 cd /usr/local/redis-2.8.0 make && make install4.配置redis.conf # 任意ip都可以访问 bind 0.0.0.0 # 关闭保…

网安大咖说·镜鉴(下)| 把握安全新脉搏:企业CSO的领航之道

网安大咖说镜鉴栏目通过对网安大咖说嘉宾访谈内容的深度提炼&#xff0c;撷取群英论道之精髓&#xff0c;汇聚众智谋策之高远&#xff0c;为从业者提供宝贵的经验和启迪。集思广益、博采众长&#xff0c;意在以镜为鉴&#xff0c;观网安之百态&#xff0c;立防范之策略&#xf…

Linux 内核缓存一致性相关机制和接口梳理

From 程序员秘书 缓存一致性是一个非常关键的问题&#xff0c;特别是在多核处理器和直接内存访问&#xff08;DMA&#xff09;场景下。原因如下&#xff1a; 多核CPU与cache的缓存一致性问题&#xff1a;每个CPU core都有自己的cache&#xff0c;由于cache的写回机制&#xf…

AI助力科研:自动化科学构思生成系统初探

科学研究作为推动创新和知识进步的关键活动&#xff0c;在解决复杂问题和提升人类生活水平方面发挥着至关重要的作用。然而&#xff0c;科学研究的固有复杂性、缓慢的进展速度以及对专业专家的需求&#xff0c;限制了其生产力的提升。为了增强科研效率&#xff0c;本文提出了一…

力扣78 子集

给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的 子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[],[1],[2],[1,2],[3],…

Linux使用pthread多线程时cmake新增语句

https://cmake.org/cmake/help/latest/module/FindThreads.html 在子文件夹中的CMakeList.txt&#xff1a; aux_source_directory(. DIR_SUB_SRCS) add_library(echatlib SHARED ${DIR_SUB_SRCS}) # STATIC SHARED# 方法一 find_package(Threads REQUIRED) target_link_librar…

鸿蒙开发Ability Kit(程序框架服务):【FA模型绑定Stage模型ServiceExtensionAbility】

FA模型绑定Stage模型ServiceExtensionAbility 本文介绍FA模型的三种应用组件如何绑定Stage模型的ServiceExtensionAbility组件。 PageAbility关联访问ServiceExtensionAbility PageAbility关联访问ServiceExtensionAbility和PageAbility关联访问ServiceAbility的方式完全相同…

重学java 84.Java枚举

那些你暗自努力的时光&#xff0c;终究会照亮你前行的路 —— 24.6.24 一、枚举介绍&#xff08;开发中表示状态&#xff09; 1.概述&#xff1a; 五大引用数据类型&#xff1a;类型、数组、接口、注解、枚举 2.定义&#xff1a; public enum 枚举类名{} 所有的枚举类父类…

集成Stata分布式事务

一.事务协调器TC 1&#xff0e;安装Seata-server 下载&#xff1a;https://github.com/seata/seata/tags 启动&#xff1a;seata-server.bat -p 8091 -h 127.0.0.1 -m file 关闭其他服务&#xff0c;先启动它 2&#xff0e;配置文件修改 二.主业务端TM 1&#xff0e;导入依赖…

贝锐花生壳内网穿透

贝锐花生壳内网穿透使用步骤 首先你得去官网购买一个域名配置一下内网穿透映射官网下载一个客户端修改代码配置 首先你得去官网购买一个域名 配置一下内网穿透映射 官网下载一个客户端 注意&#xff0c;一定要下载客户端&#xff0c;不然用不了 当然&#xff0c;本地我已经提前…

SpringBoot-配置文件中使用随机值和使用变量

1、配置文件中使用随机值 2.在配置文件使用引用变量 如果没定义还可以设置默认值

NIO(三) Selector使用(NIO综合)

Selector&#xff08;选择器&#xff09;能够管理一到多个Channel&#xff08;通道&#xff09;&#xff0c;监听通道是否为事件做好准备。 一&#xff0c;使用Selector的好处 只需少量线程来处理多个通道&#xff0c; 从而管理多个网络连接。 二&#xff0c;Selector示例 服务…

环境安装-GIT

下载 git官网下载 https://git-scm.com/ 安装 点击下载的安装包&#xff0c;并点击下一步 选择安装路径&#xff0c;照例改选自定义路径 选择默认的即可 选择GIT编辑器&#xff0c;默认选择vim即可 设置初始化新项目(本地仓库)的主分支名&#xff0c;按默认即可&#xff0c;点…

keysight 34901A (安捷伦)多路复用器

34970A 数据采集/开关单元的 Keysight 34901A&#xff08;安捷伦&#xff09;模块是通用扫描中最通用的多路复用器。它将密集的多功能开关与 60 通道/秒的扫描速率相结合&#xff0c;可满足广泛的数据采集应用。两线和四线通道可以混合在同一模块上。两个额外的保险丝输入&…

Hadoop 面试题(八)

1. 在 Hadoop 集群的配置文件中有如下两个配置&#xff0c;请问假如集群中有一个节点宕机&#xff0c;主节点 namenode 需要多长时间才能感知到&#xff08;&#xff09; &#xff1f; dfs.heartbeat.interval 3heartbeat.recheck.interval 2000A&#xff1a;26秒 B&#xff1…