ReactJS中如何实现多筛选条件下点击按钮切换展示表格数据
React 多筛选条件表格切换实现方案
需求说明
主页表格需要支持多维度筛选切换,交互规则如下:
- 用户首次进入页面时,表格默认展示分页加载的
currentUserData - 点击「Today」按钮,表格展示当日筛选结果
todayData - 点击「This Week」按钮,表格展示当周筛选结果
thisWeekData - 点击「This Month」按钮,表格展示当月筛选结果
thisMonthData - 点击「Custom」按钮,表格展示自定义日期区间筛选结果
customData - 点击「Today」板块下拉菜单中的用户选项,表格展示对应用户的筛选结果
dropDownUserData
关联数据定义
userData:数据库存储的全量原始业务数据todayData:从全量数据中筛选出的当日数据,点击Today按钮时触发筛选/加载thisWeekData:从全量数据中筛选出的当周数据,点击This Week按钮时触发筛选/加载thisMonthData:从全量数据中筛选出的当月数据,点击This Month按钮时触发筛选/加载customData:从全量数据中筛选出的自定义日期区间数据,点击Custom按钮选择完日期范围后触发筛选/加载dropDownUserData:从全量数据中筛选出的下拉菜单选中用户的对应数据,点击下拉用户选项时触发筛选/加载currentUserData:默认分页加载的用户列表数据,支持根据searchInput做用户名模糊匹配搜索
原有代码问题
现有实现通过多层嵌套三元运算符,判断各数据集的长度来决定渲染内容,存在三个核心问题:
- 交互逻辑完全失效:只要优先级高的数据集(比如todayData)不为空,无论用户点击哪个筛选按钮,都只会展示优先级最高的数据集内容,根本不会响应用户的切换操作
- 代码冗余度极高:同一段表格行渲染逻辑重复写了6遍,后续改字段、调样式需要改6处,维护成本极高
- 细节不规范:直接在
<tbody>标签下渲染Loading文本不符合HTML表格嵌套规则,会导致布局错乱;filter回调返回数据项而非布尔值,写法不严谨。
原有实现代码如下:
<tbody> { // 👇️判断当日数据是否存在,存在则渲染 todayData.length > 0 ? todayData.map((user, id) => <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr>) : // 👇️判断当周数据是否存在,存在则渲染 thisWeekData.length > 0 ? thisWeekData.map((user, id) => <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr> ) : // 👇️判断当月数据是否存在,存在则渲染 thisMonthData.length > 0 ? thisMonthData.map((user, id) => <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr> ) : // 👇️判断自定义日期筛选数据是否存在,存在则渲染 customData.length > 0 ? customData.map((user, id) => <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr> ) : // 👇️判断下拉用户选中数据是否存在,存在则渲染 dropDownUserData ? dropDownUserData.map((user, id) => <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr> ) : // 👇️判断分页数据是否存在,存在则走搜索逻辑后渲染,否则展示Loading currentUserData && currentUserData.length > 0 ? currentUserData.filter((value) => { if (searchInput === '') { return value } else if (value.name.toLowerCase().includes(searchInput.toLowerCase())) { return value } }) .map((user, id) => <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr> ) : 'Loading' } </tbody>
优化实现方案
核心思路是新增状态记录当前激活的筛选类型,用户点击对应筛选入口时更新这个状态,渲染时直接根据激活状态匹配对应数据源即可,同时抽离公共的表格行渲染逻辑消除重复代码。
- 首先定义筛选类型枚举和激活状态,默认值设为默认分页,满足首次加载展示
currentUserData的需求:
// 筛选类型枚举,避免硬编码写错 const FILTER_TYPES = { DEFAULT: 'default', TODAY: 'today', THIS_WEEK: 'thisWeek', THIS_MONTH: 'thisMonth', CUSTOM: 'custom', DROPDOWN_USER: 'dropdownUser' } // 组件内声明状态 const [activeFilter, setActiveFilter] = useState(FILTER_TYPES.DEFAULT)
- 给所有筛选交互入口绑定状态更新逻辑,点击时切换激活的筛选类型:
// Today按钮点击事件 const handleTodayClick = () => { // 如果数据没有预加载,可以在这里加请求/筛选todayData的逻辑 setActiveFilter(FILTER_TYPES.TODAY) } // This Week按钮点击事件 const handleWeekClick = () => { setActiveFilter(FILTER_TYPES.THIS_WEEK) } // This Month按钮点击事件 const handleMonthClick = () => { setActiveFilter(FILTER_TYPES.THIS_MONTH) } // Custom按钮确认事件 const handleCustomFilterSet = () => { // 日期选择完成、customData生成后调用 setActiveFilter(FILTER_TYPES.CUSTOM) } // 下拉菜单用户选中事件 const handleUserSelect = () => { // 选中用户、dropDownUserData生成后调用 setActiveFilter(FILTER_TYPES.DROPDOWN_USER) }
- 抽离公共的表格行渲染方法,统一处理搜索过滤、空态/加载态展示:
const renderTableRows = (dataSource) => { // 只有默认分页数据需要走用户名搜索过滤 const displayData = activeFilter === FILTER_TYPES.DEFAULT ? dataSource?.filter(item => { const keyword = searchInput.trim().toLowerCase() if(!keyword) return true return item.name.toLowerCase().includes(keyword) }) : dataSource // 数据未加载/为空时展示Loading,套在跨列单元格里避免布局错乱 if(!displayData || displayData.length === 0) { return ( <tr> <td colSpan={6} className="table-loading">Loading</td> </tr> ) } return displayData.map((user, id) => ( <tr key={id} className='table-row'> <td className='table-item'>{user.date}</td> <td className='table-item'>{user.from}</td> <td className='table-item'>{user.to}</td> <td className='table-item'>{user.duration}</td> <td className='table-item'>{user.hangup}</td> <td className='table-item'>{user.action}</td> </tr> )) }
- 简化表格主体渲染逻辑,根据当前激活的筛选类型传入对应数据源即可,完全去掉嵌套三元:
<tbody> { (() => { switch(activeFilter) { case FILTER_TYPES.TODAY: return renderTableRows(todayData) case FILTER_TYPES.THIS_WEEK: return renderTableRows(thisWeekData) case FILTER_TYPES.THIS_MONTH: return renderTableRows(thisMonthData) case FILTER_TYPES.CUSTOM: return renderTableRows(customData) case FILTER_TYPES.DROPDOWN_USER: return renderTableRows(dropDownUserData) case FILTER_TYPES.DEFAULT: default: return renderTableRows(currentUserData) } })() } </tbody>
内容的提问来源于stack exchange,提问作者Labeeb OPC
相关产品推荐
相关产品推荐

