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
相关产品推荐
相关产品推荐

