React状态存储异常:多输入排序器初始化与值过滤问题
表格数据排序器的状态初始化与选项过滤问题修复
问题概述
开发含3个输入框的表格排序器时遇到两个核心问题:
- 调用
initSelects初始化select状态,预期生成{sorterParam1: 'Date', sorterParam2: '', sorterParam3: ''},但实际仅保留fields数组最后一项的键值对 - 未实现「已选中的值在后续输入框下拉列表中隐藏」的需求
需求细节
- 第一个输入框默认值固定为
Date,可切换为name、username等其他选项 - 已选中的值需在后续输入框下拉列表中隐藏:选中
Date后,第二、第三个输入框不能显示Date;第二个输入框选中Earth后,第三个输入框不能显示Date和Earth
问题代码
Sorter 组件
/*eslint-disable*/ import React, { useState, useMemo } from 'react'; import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; import { GridSortModel } from '@mui/x-data-grid'; import SorterField from './SorterField'; const initSelects = (fields) => { let object = {}; fields.map((item) => { console.log(item, 'item'); object = { ...item, [item.name]: item.defaultValue ? item.defaultValue : '', }; }); return object; }; const Sorter = ({ menuItemsValue, setSortData }: SortProps) => { const fields: SorterProps[] = [ { name: 'sorterParam1', title: 'Sort by', optional: false, defaultValue: 'Date', }, { name: 'sorterParam2', title: 'Then by', optional: true, }, { name: 'sorterParam3', title: 'Then by', optional: true, }, ]; const [select, setSelect] = useState<any>(() => initSelects(fields)); const getMenuItems = useMemo(() => { return menuItemsValue.filter((item) => select.sorterParam1 !== item); }, [menuItemsValue, select]); const handleSelectChange = ( e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> ) => { const { name, value } = e.target; console.log(111, name, value); setSelect({ ...select, [name]: value }); setSortData(sortOptions); }; const handleClearAllInputs = () => { setSelect({ sorterParam1: '', sorterParam2: '', sorterParam3: '', }); }; const handleConfirm = () => {}; return ( <TextField label="Advanced Sorting" className={styles.sorter__inputs} id="sorter-parameter-1" variant="standard" InputProps={{ disableUnderline: true, }} select SelectProps={{ IconComponent: (props) => <NewIcon {...props} />, }} sx={{ fontSize: '12px', width: '100%', '& .MuiInputBase-input:focus': { backgroundColor: 'transparent !important', }, '& .MuiInputLabel-root': { color: '#9E9E9E', }, '& .MuiTextField-root': { fontSize: '13px', }, '& .MuiOutlinedInput-root': { backgroundColor: '#fff', }, }} > {fields.map((option, index) => ( <SorterField key={option.name} menuItemsValue={getMenuItems} name={option.name} option={option} count={fields.length} handleChange={handleSelectChange} index={index + 1} /> ))} <div className={styles.sorter__inputControllers}> <Button className={styles.sorter__clearAllInput} onClick={() => handleClearAllInputs()} > Clear All </Button> <Button onClick={() => handleConfirm()} className={styles.sorter__confirmInput} > Confirm </Button> </div> </TextField> ); }; export default Sorter;
SorterField 组件
/*eslint-disable*/ import React, { useState } from 'react'; import TextField from '@mui/material/TextField'; import { MenuItem } from '@mui/material'; import { SorterProps } from '../../types/Sorter'; import { ReactComponent as SorterLine } from '../../assets/img/sortLine.svg'; import styles from '../../assets/components/Sorter/sorter.module.scss'; type SorterFieldProps = { menuItemsValue: string[]; option: SorterProps; count: number; name: string; handleChange: ( e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> ) => void; index: number; }; function SorterField({ option, count, menuItemsValue, handleChange, index, }: SorterFieldProps) { const handleSorting = () => {}; return ( <div className={styles.sorter__container}> <div className={styles.sorter__header}> <p className={styles.sorter__label}> {option.title}{' '} {option.optional && ( <sup className={styles.sorter__optional}>*Optional</sup> )} </p> <div className={styles.sorter__numbers__container}> {Array.from({ length: count }, (_, i) => i + 1).map((number) => ( <> {number === index ? ( <> <span className={styles.sorter__number}>{index}</span> </> ) : ( <> <span className={styles.sorter__numbers}>{number}</span> </> )} </> ))} </div> </div> <div className={styles.sorter__inputs}> <TextField className={[styles.sorter__input, styles.sorter__field__input].join( ' ' )} variant="outlined" label="Select" select SelectProps={{ IconComponent: () => <NewIcon />, }} value={option.defaultValue} onChange={handleChange} name={option.title} size="small" > {menuItemsValue.map((title, idx) => ( <MenuItem key={idx} value={title}> {title} </MenuItem> ))} </TextField> <div onClick={handleSorting} className={styles.sorter__sortOrder}> <div className={styles.sorter__sortOrder__alphabetical}> <span>A</span> <span>Z</span> </div> <div className={styles.sorter__sortOrder__sortLine}> <SorterLine /> </div> </div> </div> </div> ); } export default SorterField;
修复方案
1. 修复状态初始化函数initSelects
原函数使用map循环时每次直接覆盖对象,导致最终仅保留最后一项数据。改用reduce累加字段:
const initSelects = (fields) => { return fields.reduce((acc, item) => { return { ...acc, [item.name]: item.defaultValue ?? '' }; }, {}); };
2. 实现全局选中值过滤逻辑
修改getMenuItems,收集所有已选中的非空值并排除,确保后续输入框不显示已选选项:
const getMenuItems = useMemo(() => { const selectedValues = Object.values(select).filter(val => val); return menuItemsValue.filter(item => !selectedValues.includes(item)); }, [menuItemsValue, select]);
3. 修复SorterField组件的状态绑定错误
- 将
TextField的name属性改为option.name,确保handleChange能正确识别字段 - 将
value属性绑定到select[option.name],实时显示当前选中状态 - 新增传递
select状态到子组件
修改后的Sorter组件渲染部分:
<SorterField key={option.name} menuItemsValue={getMenuItems} name={option.name} option={option} count={fields.length} handleChange={handleSelectChange} index={index + 1} select={select} />
修改后的SorterField组件中TextField部分:
<TextField className={[styles.sorter__input, styles.sorter__field__input].join(' ')} variant="outlined" label="Select" select SelectProps={{ IconComponent: () => <NewIcon />, }} value={select[option.name] ?? ''} onChange={handleChange} name={option.name} size="small" >
同时更新SorterField的props类型:
type SorterFieldProps = { menuItemsValue: string[]; option: SorterProps; count: number; name: string; handleChange: ( e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> ) => void; index: number; select: Record<string, string>; };
4. 修正清空按钮的默认值
清空后第一个输入框应恢复默认的Date,而非空值:
const handleClearAllInputs = () => { setSelect({ sorterParam1: 'Date', sorterParam2: '', sorterParam3: '', }); };
内容的提问来源于stack exchange,提问作者Stiven Ballshi
相关产品推荐
相关产品推荐

