React同行动态更新按钮状态:点击新按钮时清除旧按钮高亮状态
问题根因
当前每个StackItem单独维护自己的选中状态,同组内其他按钮的状态无法互相感知,才会出现同一行多个按钮同时高亮的问题,我们可以通过状态提升的方案解决,把选中状态收归到父组件统一管控。
修改方案
第一步:Stack组件新增全局选中状态管理
在Stack组件的状态定义区新增一行,用于存储每行的选中按钮:
// 新增选中状态,key为行level,value为当前行选中的按钮文本 const [selectedItems, setSelectedItems] = useState({});
然后在handleClick函数中,获取到当前点击行id、当前按钮currBtn后,新增更新选中状态的逻辑:
// 新增:更新选中状态,清空当前行及后续所有行的旧选中记录 const updatedSelected = {...selectedItems}; Object.keys(updatedSelected).forEach(rowLevel => { if (Number(rowLevel) >= id) delete updatedSelected[rowLevel]; }); updatedSelected[id] = currBtn; setSelectedItems(updatedSelected);
同时在partition切换的useEffect里,清空选中状态:
useEffect(() => { setLevel(0); setPrevLevel(-1); setSelected([]); setSelectedItems({}); // 新增清空选中 }, [partition]);
最后在渲染StackRow的时候,把selectedItems作为props传递:
<StackRow data={card} partition={partition} level={index} index={cards.indexOf(card)} onClick={handleClick} getInfo={getInfo} isApp={isApp} selectedItems={selectedItems} // 新增传参 />
第二步:StackRow透传选中状态
修改StackRow组件的入参,拿到selectedItems后透传给所有StackItem,没用的handleSelectedButtons相关逻辑可以直接删除:
// 入参新增selectedItems function StackRow({ data, partition, level, index, onClick, getInfo, isApp, selectedItems }) { const classes = useStyles({ level: level }); const rowRef = useRef(); return ( <Card key={partition + '_' + index + '_' + level} className={classes.card} id={level} ref={rowRef} > {data.map((field) => { return ( <StackItem key={partition + '_' + field[0] + '_' + level} data={field[0]} info={field[1]} level={level} onClick={onClick} getInfo={getInfo} isApp={isApp} selectedItems={selectedItems} // 透传参数 /> ); })} </Card> ); }
第三步:StackItem改用父级状态判断选中
删掉StackItem内部的flag状态,直接用父级传的selectedItems判断是否高亮:
function StackItem({ data, info, level, onClick, getInfo, isApp, selectedItems // 新增入参 }) { // 删掉原来的const [flag, setFlag] = useState(false); const btnRef = useRef(); // 直接判断当前按钮是否为当前行选中项 const isSelected = selectedItems[level] === data; const styleProps = { backgroundColor: isSelected ? '#06d6a0' : level % 2 === 0 ? '#22223b' : '#335c67', }; const classes = useStyles(styleProps); return ( <Button ref={btnRef} isselected={isSelected.toString()} key={data} className={classes.button} variant="outlined" onClick={(event) => { onClick(event); // 删掉原来传的setFlag、btnRef,没用了 getInfo(info, level, isApp); }} disableElevation={true} > {data} </Button> ); }
同时可以把原来handleClick里接收的setFlag、btnRef参数删掉,原有逻辑中的setFlag(true)也可以删除,完全靠selectedItems驱动高亮。
修改后所有行的选中状态由父组件统一管控,同一行永远只会有一个按钮处于高亮状态,点击前面行的按钮时,后面行的高亮也会自动清空,完全符合需求。
内容的提问来源于stack exchange,提问作者Caon
相关产品推荐
相关产品推荐

