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

TypeScript中为数组元素设置Ref:点击Span仅最后一个生效问题求解

React组件中点击多个Span仅最后一个触发active类切换的问题

问题描述

组件渲染了一组<span>标签,但点击任意Span时,始终只有最后一个Span的active类会被添加或移除,其余Span无响应。

相关代码

class MyComponent ... {
  private targetSpan = HTMLElement;

  componentDidMount() {
    // 此处获取数据并填充一个包含6个值的数组
  }

  private setActive(value) {
    let target = this.targetSpan;

    if (target.classList.contains('active')) {
      target.classList.remove('active');
      // 执行其他逻辑
    } else {
      target.classList.add('active');
      // 执行更多逻辑
    }
  }

  private splitMyThings() {
    const returnThings: any[] = [];

    forEach(array, thing => {
      returnThings.push(
        <span class="thing" onClick={() => this.setActive(thing)} ref={elem => this.targetSpan = elem}>
          {thing}
        </span>
      );
    });
    return returnThings; // 原代码遗漏了return语句,需补充
  }

  render() {
    return <div class="container">
      ...
      <div class="myTargets">
        {this.splitMyThings()}
      </div>
      ...
    </div>;
  }
}

问题根源

所有<span>的ref都绑定到了同一个实例变量this.targetSpan上。循环渲染时,每一个后续的Span都会覆盖这个变量的值,最终this.targetSpan只会保留最后一个Span的DOM元素。因此无论点击哪个Span,setActive方法操作的都是最后一个Span。

快速解决方案

方案1:通过事件对象直接获取点击元素(无需ref)

修改setActive方法,利用事件参数拿到当前点击的Span,完全不需要使用ref:

// 修改setActive方法,添加event参数
private setActive(value, event) {
  const target = event.currentTarget; // 确保获取到绑定点击事件的元素

  if (target.classList.contains('active')) {
    target.classList.remove('active');
  } else {
    target.classList.add('active');
  }
  // 后续逻辑可正常使用传入的value
}

// 调用时传入event
<span class="thing" onClick={(event) => this.setActive(thing, event)}>
  {thing}
</span>

同时可以删除private targetSpan = HTMLElement;这个无用变量。

方案2:用React状态管理激活项(更符合React设计理念)

避免直接操作DOM,通过组件状态跟踪当前激活的项,渲染时动态添加类名:

class MyComponent ... {
  // 初始化状态,存储当前激活的项
  private state = {
    activeThing: null
  };

  componentDidMount() {
    // 获取数据并填充数组
  }

  private setActive(value) {
    // 切换激活状态:如果当前项已激活则取消,否则设置为激活
    this.setState(prevState => ({
      activeThing: prevState.activeThing === value ? null : value
    }));
    // 执行其他逻辑
  }

  private splitMyThings() {
    const { activeThing } = this.state;
    const returnThings: any[] = [];

    forEach(array, thing => {
      returnThings.push(
        <span 
          className={`thing ${activeThing === thing ? 'active' : ''}`} 
          onClick={() => this.setActive(thing)}
        >
          {thing}
        </span>
      );
    });
    return returnThings;
  }

  render() {
    // 注意:React中JSX需用className代替class
    return <div className="container">
      ...
      <div className="myTargets">
        {this.splitMyThings()}
      </div>
      ...
    </div>;
  }
}

内容的提问来源于stack exchange,提问作者erniberni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:57:05