记录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>
               <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]);

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB数据库的安装和删除

MongoDB数据库的删除和安装 1、删除MongoDB数据库2、下载MongoDB数据库1)、自定义安装2)、注意可视化可以取消勾选 1、删除MongoDB数据库 没有下载过的&#xff0c;可以直接跳到下面的安装过程↓ 我们电脑中如果有下载过MongoDB数据库&#xff0c;要更换版本的话&#xff0c;其…

IntelliJ IDEA2024 for Mac Java代码编辑器

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…

基于Java蛋糕甜品商城系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

Python使用策略模式绘制图片分析多组数据

趋势分析&#xff1a;折线图静态比较&#xff1a;条形图分布分析&#xff1a;箱线图离散情况&#xff1a;散点图 import matplotlib.pylab as plt from abc import ABC, abstractmethod import seaborn as sns import pandas as pd import plotly.graph_objects as go import p…

人机恋爱新趋势:与AI男友谈恋爱的甜蜜与挑战

"我曾经把ChatGPT当成工具&#xff0c;从未追过星&#xff0c;也没有嗑过CP。没想到&#xff0c;到了36岁&#xff0c;我竟然嗑上了AI男友。Open AI&#xff0c;你赢了。你不仅是最好的AI公司&#xff0c;还是乙女游戏公司。" 转行大龄互联网人&#xff0c;走遍20国…

Go 语言学习笔记之字典 Map

Go 语言中的字典 Map 大家好&#xff0c;我是码农先森。 概念 在 Go 语言中&#xff0c;字典被称为 map&#xff0c;它是一种无序的集合&#xff0c;用于存储键值对。每个键在 map 中必须是唯一的&#xff0c;并且对应一个值。map 是一种非常常用的数据结构&#xff0c;用于…

03-Shell编程之循环语句与函数

目录 3.1 for循环语句 3.1.1for语句的结构 3.1.2 for语句应用实例 3.2 使用whlie循环语句 1.打印数字1到5 3.3 使用until循环语句 3.3.1until的实例 1.打印数字1到5&#xff08;使用until的逆向逻辑&#xff09; 2.等待用户输入特定内容 3.4 函数 3.4.1Shell函数的基…

msvcp120.dll丢失怎么办,找不到msvcp120.dll的多种解决方法

最近&#xff0c;我在运行一个程序时遇到了一个错误&#xff0c;系统提示找不到msvcp120.dll文件&#xff0c;无法继续执行代码。这让我感到非常困扰&#xff0c;因为这个问题导致我无法正常运行这个程序。经过一番搜索和尝试&#xff0c;我找到了几种修复这个问题的方法&#…

Mysql索引底层数据结构——Java全栈知识(28)

Mysql索引底层数据结构 1、什么是索引 索引在项目中还是比较常见的&#xff0c;它是帮助MySQL高效获取数据的数据结构&#xff0c;主要是用来提高数据检索的效率&#xff0c;降低数据库的IO成本&#xff0c;同时通过索引列对数据进行排序&#xff0c;降低数据排序的成本&…

【UEFI实战】HttpBoot

环境配置 首先下载tftpd工具&#xff0c;可以在phjounin / tftpd64 / Downloads — Bitbucket下载到&#xff0c;建议不要安装到C盘&#xff0c;因为可能无法修改其配置。配置tftpd工具的DHCP服务&#xff1a; 注意这里的IP地址需要跟实际网卡IP匹配。 下载Apache&#xff0c…

探秘神经网络激活函数:Sigmoid、Tanh和ReLU,解析非线性激活函数的神奇之处

引言 在神经网络中&#xff0c;激活函数扮演着至关重要的角色。它们赋予神经网络非线性的能力&#xff0c;使得网络具备学习和表示复杂函数关系的能力。本文将详细解析三种常见的激活函数&#xff1a;Sigmoid、Tanh和ReLU&#xff0c;揭开它们在神经网络中的奥秘。无论你是初学…

MOE学习笔记

MOE网络结构 和传统的 transformer 网络结构相比&#xff0c;我们将 Transformer 模型的每个 FFN 层替换为 MoE 层&#xff0c;MoE 层由门网络&#xff08;Router&#xff09;和一定数量的专家&#xff08;Expert&#xff09;组成。 这些 Expert 其实也是 FFN 层&#xff0c;…

光伏半导体的种类

光照射半导体材料时&#xff0c;其电导率发生变化的实质是光生载流子的产生。在半导体中&#xff0c;价带中的电子受到一定能量的光子激发后&#xff0c;可以跃迁到导带&#xff0c;形成自由电子和空穴对&#xff0c;即光生载流子。这些光生载流子会增加半导体的导电能力&#…

思考-生涯思考-GPT-5对人们的影响

GPT-5 一年半后发布&#xff1f;对此你有何期待&#xff1f; IT之家6月22日消息&#xff0c;在美国达特茅斯工程学院周四公布的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂被问及GPT-5是否会在明年发布&#xff0c;给出了肯定答案并表示将在一年半后发布。此外&#xff0c;…

MOC和MCS通讯流程分析

半导体行业-SECS/GEM协议 半导体设备通讯SECS协议是由国际半导体设备与材料协会&#xff08;SEMI&#xff09;的会员一起构建的连接性标准。它最初是为了在半导体/电子行业的自动化中实现设备与主机系统之间的通信而制定的。 SECS/GEM不仅允许客户查看设备的功能&#xff0c;…

使用 MongoDB 剖析开放银行:技术挑战和解决方案

开放银行&#xff08;或开放金融&#xff09;在银行业掀起了一股颠覆性浪潮&#xff0c;它迫使金融机构&#xff08;银行、保险公司、金融科技公司、企业甚至政府机构&#xff09;迎接一个透明、协作和创新的新时代。这种模式转变要求银行与第三方提供商&#xff08;TPP&#x…

双 μC 的 PWM 频率和分辨率

该方法是过滤 PWM 信号的 HF 分量&#xff0c;只留下与占空比成正比的 LF 或 DC 分量。然而&#xff0c;低通滤波器并不能完全滤除PWM频率&#xff0c;因此LF/DC信号一般会有一些纹波。 有两种方法可以降低 PWM DAC 的纹波。可以降低低通滤波器的截止频率&#xff0c;或者提高…

编译原理大题自解(活前缀DFA、LR(0)分析表)

目录 4. (简答题) &#xff08;1&#xff09;给出识别活前缀的DFA &#xff08;2&#xff09;设计此文法的 LR(0)分析表 第一种解法 第二种解放 首先声明这是作者的写法&#xff08;不保证正确&#xff01;&#xff09;仅供参考。本题因为可能存在冲突的原因&#xff0c;所…

SAPUI5基础知识9 - JSON Module与数据绑定

1. 背景 在前面的博客中&#xff0c;我们已经学习了SAPUI5中视图和控制器的使用&#xff0c;在本篇博客中&#xff0c;让我们学习下MVC架构中的M-模型了。 SAPUI5中的JSON Model是一个客户端模型&#xff0c;可以用于在SAPUI5应用程序中处理和操作JSON数据。SAPUI5提供了绑定…

爬虫笔记15——爬取网页数据并使用redis数据库set类型去重存入,以爬取芒果踢V为例

下载redis数据库 首先需要下载redis数据库&#xff0c;可以直接去Redis官网下载。或者可以看这里下载过程。 pycharm项目文件下载redis库 > pip install redis 然后在程序中连接redis服务&#xff1a; from redis import RedisredisObj Redis(host127.0.0.1, port6379)…