记录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.rhkb.cn/news/359587.html

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

相关文章

纯css星空动画

让大家实现一个这样的星空动画效果,大家会怎么做? js,不! 其实使用css就能写 我也不藏着掖着,源码直接放下面了 <script setup></script><template><div class"box"><div v-for"i in 5" :key"i" :class"layer…

文件上传漏洞-上篇

一、概述 文件上传漏洞可以说是日常渗透测试中用得最多的一个漏洞&#xff0c;用它获得服务器权限最快最直接。在web程序中&#xff0c;经常需要用到文件上传的功能。如用户或者管理员上传图片&#xff0c;或者其它文件。如果没有限制上传类型或者限制不严格被绕过&#xff0c…

Hadoop+Spark大数据技术(微课版)总复习

期末试卷组成 一、选择题(每小题2分&#xff0c;共20分) 二、判断题(每小题2分&#xff0c;共20分) 三、简答题(每小题5分&#xff0c;共20分) 四、程序分析题 (第1-5小题各6分&#xff0c;第6题10分&#xff0c;共40分) 图1 Hadoop开发环境 图2 HDFS 图3 MapReduce 图4 H…

Web 应用开源项目大全

Web 应用开源项目大全结合巴比达内网穿透实现WEB公开访问。 下面是一个Web应用的开源列表。没什么可说的&#xff0c;太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想&#xff0c;有些开源项目的源码写得挺不好的&#xff0c;尤其是性能方面。或许你会以为改一改他们就可以成…

【SCAU数据挖掘】数据挖掘期末总复习题库应用题及解析

1. 给定圆的半径为e &#xff0c;令 MinPts3&#xff0c;考虑下面两幅图。 &#xff08;1&#xff09;哪些对象是核心对象? m,p,o,r(因为这些核心对象在半径e的范围内都至少包含MinPts3个对象) &#xff08;2&#xff09;哪些对象是直接密度可达的? 对象q是…

智慧在线医疗在线诊疗APP患者端+医生端音视频诊疗并开处方

智慧在线医疗&#xff1a;音视频诊疗新纪元 &#x1f310; 智慧医疗新篇章 随着科技的飞速发展&#xff0c;智慧医疗正逐步走进我们的生活。特别是在线医疗&#xff0c;凭借其便捷、高效的特点&#xff0c;已成为许多患者的首选。而其中的“智慧在线医疗患者端医生端音视频诊疗…

使用Mixamo极简绑骨,导入unity中使用

如果你只想专注于角色建模&#xff0c;对于动画设计没有过多精力&#xff1b;如果你想白嫖别人的角色动画&#xff0c;用到自己的模型上&#xff1b;那么&#xff0c;这个网站很适合你&#xff1a;https://www.mixamo.com/ 操作步骤&#xff1a; 首先将自己的模型上传到这个网…

【kaggle数据集无法下载解决办法】

kaggle数据集无法下载的解决办法 当我们在做机器学习相关问题的时候&#xff0c;需要到kaggle网站上下载数据集&#xff0c;但是很多时候速度很慢或者连接超时等问题&#xff0c;此时解决办法如下&#xff1a; 在本地安装Kaggle API包 打开终端输入如下指令&#xff1a; pip i…

FL Studio 21.2.3官方中文版重磅发布,手把手教你图文安装

FL Studio 21.2.3官方中文版重磅发布纯正简体中文支持&#xff0c;更快捷的音频剪辑及素材管理器&#xff0c;多样主题随心换&#xff01; 在数字音乐制作领域&#xff0c;FL Studio一直以其强大的功能和用户友好的界面而备受赞誉。随着技术的不断进步和音乐制作需求的日益增长…

Linux根目录挂载点(/dev/mapper/centos-root)扩容

如果我们在安装系统是采用自定义分区的话&#xff0c;就可以提前规划好这个事情。但是如果平常没注意就直接采用默认安装的方式的话。一旦 根目录的容量耗尽&#xff0c;将会影响业务的运行。今天我们来扩容逻辑卷。 默认安装的话会给home目录分比较多的空间&#xff0c;我们可…

python桌面应用

py文件 import osimport wx import wx.html2class MyFrame(wx.Frame):def __init__(self, parent):wx.Frame.__init__(self, parent, title"启动啦", size(1000, 700))# 创建一个Web视图组件self.browser wx.html2.WebView.New(self)# 加载本地HTML文件# self.brow…

常见硬件工程师面试题(二)

大家好&#xff0c;我是山羊君Goat。 对于硬件工程师&#xff0c;学习的东西主要和电路硬件相关&#xff0c;所以在硬件工程师的面试中&#xff0c;对于经验是十分看重的&#xff0c;像PCB设计&#xff0c;电路设计原理&#xff0c;模拟电路&#xff0c;数字电路等等相关的知识…

Web3 学习

之前学习 web3&#xff0c;走了不少弯路&#xff0c;最近看到了 hackquest&#xff0c;重新刷了一遍以太坊基础&#xff0c;感觉非常nice&#xff0c;而且完全免费&#xff0c;有需要的可以试试&#xff0c;链接hackquest.io。

Vue项目环境搭建及git仓库新建

不会安装的 可以找下博客&#xff0c;我会在另外一个博客 单独讲解git的安装 接下后启动对应的git bash文件&#xff0c;可以发送快捷方式到桌面 启动git命令的窗口 之后就可以在对应的文件下&#xff0c;启动git命令窗口了 3.码云-项目代码管理仓库 链接&#xff1a;https:…

充电桩---特斯拉NACS接口介绍

一、NACS接口发展 NACS是由特斯拉内部开发的&#xff0c;作为交流和直流充电的专有充电解决方案。2022年11月11日&#xff0c;特斯拉在官网上开放了自家的充电接口设计&#xff0c;并将特斯拉充电接口更名为NACS&#xff08;North American Charging Standard&#xff09;&…

数字货币与区块链生态

前言&#xff1a;区块链技术与数字货币这一文详细介绍了区块链技术&#xff0c;感兴趣的可以先看看这篇文章 1.比特币之后的数字货币与区块链发展 2.区块链形态 • 无许可区块链&#xff08;permissionless blockchain&#xff09; 用户无需许可即可加入区块链网络 • 许…

鸿蒙HarmonyOS服务卡片实战

引言 在现代开发中&#xff0c;服务卡片是不可或缺的一部分&#xff0c;比如音乐&#xff0c;天气类等应用&#xff0c;官网的介绍中写道&#xff1a;卡片让您便捷地预览服务信息&#xff0c;例如查看天气或日历日程等内容。您可将卡片添加到屏幕上&#xff0c;让这类信息触手…

【面试题】前端 移动端自适应?_前端移动端适配面试题

设备像素比 设备像素比 (DevicePixelRatio) 指的是设备物理像素和逻辑像素的比例 。比如 iPhone6 的 DPR 是2。 设备像素比 物理像素 / 逻辑像素。可通过 window.devicePixelRatio 获取&#xff0c;CSS 媒体查询代码如下 media (-webkit-min-device-pixel-ratio: 3), (min-…

初识 GPT-4 和 ChatGPT

文章目录 LLM 概述理解 Transformer 架构及其在 LLM 中的作用解密 GPT 模型的标记化和预测步骤 想象这样⼀个世界&#xff1a;在这个世界里&#xff0c;你可以像和朋友聊天⼀样快速地与计算机交互。那会是怎样的体验&#xff1f;你可以创造出什么样的应用程序&#xff1f;这正是…

【大数据】Hadoop学习笔记

基本概念 Hadoop组成 HDFS: Hadoop分布式文件存储系统, 在Haddop中处于底层/核心地位YARN: 分布式通用的集群资源管理系统和任务调度平台, 支撑各种计算引擎执行MapReduce: 第一代分布式计算引擎, 但因为部分原因, 许多企业都不直接使用MapReduce, 但许多底层软件仍然在使用Ma…