news 2026/8/9 22:04:51

【React + TypeScript 实现高性能多列多选组件】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【React + TypeScript 实现高性能多列多选组件】

引言

在现代Web应用中,多选组件是常见的UI元素,尤其是在需要用户从多个选项中进行选择的场景。本文将介绍如何使用React和TypeScript实现一个功能完整、性能优化的多列多选组件,支持"Select All"功能和垂直填充的多列布局。

组件功能介绍

本文实现的MultiCheck组件具有以下功能:

  1. 基础多选功能:支持用户选择多个选项
  2. "Select All"功能:一键全选/取消全选所有选项
  3. 多列布局:支持垂直填充的多列排列
  4. 受控组件设计:支持外部控制选中状态
  5. 性能优化:使用React Hooks优化渲染性能
  6. 类型安全:完整的TypeScript类型定义

技术栈

  • React 18:使用函数组件和Hooks
  • TypeScript:提供类型安全
  • CSS Grid:实现灵活的多列布局
  • React HooksuseState,useEffect,useCallback

核心实现

1. 类型定义

首先,我们定义组件的类型,确保类型安全:

exporttypeOption={label:string,value:string}typeProps={label?:string,options:Option[],columns?:number,values?:string[]onChange?:(options:Option[])=>void,}

2. 状态管理

使用useState钩子管理选中状态,并通过useEffect实现与外部状态的同步:

const[selectedValues,setSelectedValues]=useState<string[]>(values);// 监听外部values变化,实现双向更新useEffect(()=>{setSelectedValues(values);},[values]);

3. 全选功能实现

实现"Select All"功能需要以下逻辑:

// 判断是否全选constisAllSelected=options.length>0&&selectedValues.length===options.length;// 处理全选事件consthandleSelectAllChange=useCallback(()=>{constnewSelected=isAllSelected?[]:options.map(opt=>opt.value);setSelectedValues(newSelected);if(onChange){constselectedOptions=isAllSelected?[]:[...options];onChange(selectedOptions);}},[options,isAllSelected,onChange]);

4. 多列垂直填充布局

使用CSS Grid实现垂直填充的多列布局:

<div className='div-option-container'style={{display:'grid',gridTemplateColumns:`repeat(${Math.min(columns,options.length+1)}, auto)`,gridTemplateRows:`repeat(${Math.ceil((options.length+1)/columns)}, auto)`,gridAutoFlow:"column",// 关键:实现垂直填充width:'100%'}}>{/* 选项内容 */}</div>

5. 性能优化

使用useCallback缓存事件处理函数,避免不必要的重新渲染:

consthandleOptionChange=useCallback((value:string)=>{setSelectedValues(prev=>{constnewSelected=prev.includes(value)?prev.filter(v=>v!==value):[...prev,value];if(onChange){constselectedOptions=options.filter(opt=>newSelected.includes(opt.value));onChange(selectedOptions);}returnnewSelected;});},[options,onChange]);

完整组件代码

import'./MultiCheck.css';importReact,{useState,useEffect,useCallback}from'react';exporttypeOption={label:string,value:string}typeProps={label?:string,options:Option[],columns?:number,values?:string[]onChange?:(options:Option[])=>void,}constMultiCheck:React.FunctionComponent<Props>=({label,options,columns=1,values=[],onChange}):JSX.Element=>{const[selectedValues,setSelectedValues]=useState<string[]>(values);useEffect(()=>{setSelectedValues(values);},[values]);constisAllSelected=options.length>0&&selectedValues.length===options.length;consthandleSelectAllChange=useCallback(()=>{constnewSelected=isAllSelected?[]:options.map(opt=>opt.value);setSelectedValues(newSelected);if(onChange){constselectedOptions=isAllSelected?[]:[...options];onChange(selectedOptions);}},[options,isAllSelected,onChange]);consthandleOptionChange=useCallback((value:string)=>{setSelectedValues(prev=>{constnewSelected=prev.includes(value)?prev.filter(v=>v!==value):[...prev,value];if(onChange){constselectedOptions=options.filter(opt=>newSelected.includes(opt.value));onChange(selectedOptions);}returnnewSelected;});},[options,onChange]);return(<div className='div-container'style={{width:`${200*columns}px`}}>{label&&<div className='div-label-container'>{label}</div>}<div className='div-option-container'style={{display:'grid',gridTemplateColumns:`repeat(${Math.min(columns,options.length+1)}, auto)`,gridTemplateRows:`repeat(${Math.ceil((options.length+1)/columns)}, auto)`,gridAutoFlow:"column",width:'100%'}}><div className='div-option'><input type="checkbox"id="select-all"checked={isAllSelected}onChange={handleSelectAllChange}/><label htmlFor="select-all">Select All</label></div>{options.map(option=>(<div key={option.value}className='div-option'><input type="checkbox"id={`option-${option.value}`}checked={selectedValues.includes(option.value)}onChange={()=>handleOptionChange(option.value)}className='div-option-checkbox'/><label htmlFor={`option-${option.value}`}>{option.label}</label></div>))}</div></div>)}exportdefaultMultiCheck;

使用示例

importReactfrom'react';importMultiCheck,{Option}from'./MultiCheck';constApp:React.FC=()=>{constoptions:Option[]=[{label:'选项1',value:'1'},{label:'选项2',value:'2'},{label:'选项3',value:'3'},{label:'选项4',value:'4'},{label:'选项5',value:'5'},{label:'选项6',value:'6'},];consthandleChange=(selectedOptions:Option[])=>{console.log('选中的选项:',selectedOptions);};return(<div className="App"><MultiCheck label="多选组件示例"options={options}columns={2}values={['2','4']}onChange={handleChange}/></div>);};exportdefaultApp;

性能优化关键点

  1. 使用useCallback缓存事件处理函数:避免每次渲染都创建新函数
  2. 使用CSS Grid实现高效布局:减少JavaScript计算负担
  3. 合理设计依赖数组:确保Hooks只在必要时重新执行
  4. 函数式状态更新:避免闭包陷阱,确保基于最新状态更新

总结

本文实现的MultiCheck组件是一个功能完整、性能优化的多选组件,支持"Select All"功能和垂直填充的多列布局。通过使用React Hooks和TypeScript,我们实现了一个类型安全、性能优异的组件。

该组件的设计思路和实现方式可以作为开发其他复杂UI组件的参考,尤其是在需要处理大量选项和复杂布局的场景。

扩展建议

  1. 添加搜索功能:支持在大量选项中快速搜索
  2. 支持分组:实现选项的分组显示
  3. 添加键盘导航:提高可访问性
  4. 支持自定义样式:允许外部自定义组件样式
  5. 添加动画效果:提升用户体验

通过不断扩展和优化,这个组件可以适应更多复杂的业务场景,成为一个功能强大的多选组件库。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/5 14:10:29

Node.js 用 process.cpuUsage 监控CPU使用率

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Node.js CPU监控的深度实践&#xff1a;超越process.cpuUsage的陷阱与创新目录Node.js CPU监控的深度实践&#xff1a;超越proce…

作者头像 李华
网站建设 2026/8/4 6:41:02

【课程设计/毕业设计】基于JavaWeb寝室管理系统Web的学生宿舍管理系统【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/8/8 11:23:57

树状数组

lc2659树状数组统计已删除元素&#xff0c;按数值升序遍历元素位置分前后段计算每次删除的移动步数并累加&#xff0c;求解清空数组的总操作数。// 树状数组模板 class BIT {vector<int> tree; public:BIT(int n) : tree(n) {}// 将下标 i 上的数加一void inc(int i) {wh…

作者头像 李华
网站建设 2026/8/8 16:30:17

一道“找数”的题,为什么能成为算法世界的常青树?

一道“找数”的题,为什么能成为算法世界的常青树? 从 Missing Number 说起 一、引子:这题你肯定见过,但你真的“理解”了吗? 很多人第一次见到这道题,心里都会冒出一句话: “这也叫算法题?小学数学吧?” 题目很简单: 给你一个包含 0 ~ n 中 n 个不同数字 的数组, …

作者头像 李华
网站建设 2026/8/9 4:52:43

.nvue页面实现画笔绘制功能,用原生html导入nvue页面使用还可以截图(画笔 清空 橡皮擦 改颜色 禁用画笔 截图-是视频画面加绘制合成一张图片截图)-我花80块钱找淘宝都没弄出来,自己写的

功能 安卓app上面nvue 视频上方绘制&#xff08;vue2&#xff09;①新建一个draw.html文件&#xff08;里面功能有画笔 清空 橡皮擦 改颜色 禁用画笔 截图-是视频画面加绘制合成一张图片截图&#xff09;②webViewUrl: /static/draw.html,③<!-- 画布 绘制层 --><web-…

作者头像 李华