You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 18:09:01