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

在循环中使用useRef添加事件监听器的闭包问题及解决

问题解答

当前方案是否可行?

可行,但存在闭包作用域陷阱,导致所有监听器共享同一个循环变量,才出现点击任何元素都执行最后一次迭代函数的问题。只要修复作用域问题就能达到预期效果;不过更推荐使用React原生的事件绑定方式,更符合组件数据驱动的设计逻辑。

问题根源

你用var声明循环变量i,var是函数级作用域,循环里的所有点击回调共享同一个i引用。当循环结束后,i的值已经变成了5,所以所有点击事件触发时,都会输出最后一次的i值。

修改方案

方案1:将var替换为let

let是块级作用域,每次循环都会创建一个独立的i绑定,回调函数会捕获当前迭代的i值:

const App = () => {
 const data = ['item1', 'item2', 'item3', 'item4', 'item5'];
 const itemRef = React.useRef(data.map((x) => React.createRef(x)));

 React.useEffect(() => {
  // 把var改成let
  for (let i = 0; i < itemRef.current.length; i++) {
   itemRef.current[i].current.addEventListener('click', () => {
    console.log(`clicked data:${i}`);
   });
  }
 }, []);

 return (
  <div className="App">
   {data.map((x, i) => {
    return (
     <div className='item' ref={itemRef.current[i]} key={data[i]}>
      {x}
     </div>
    );
   })}
  </div>
 );
}
ReactDOM.render(<App/>, document.getElementById("root"));

方案2:用立即执行函数(IIFE)隔离作用域

通过IIFE为每次迭代创建独立的作用域,保存当前i的快照:

const App = () => {
 const data = ['item1', 'item2', 'item3', 'item4', 'item5'];
 const itemRef = React.useRef(data.map((x) => React.createRef(x)));

 React.useEffect(() => {
  for (var i = 0; i < itemRef.current.length; i++) {
   // 用IIFE封装当前i
   (function(index) {
    itemRef.current[index].current.addEventListener('click', () => {
     console.log(`clicked data:${index}`);
    });
   })(i);
  }
 }, []);

 return (
  <div className="App">
   {data.map((x, i) => {
    return (
     <div className='item' ref={itemRef.current[i]} key={data[i]}>
      {x}
     </div>
    );
   })}
  </div>
 );
}
ReactDOM.render(<App/>, document.getElementById("root"));

方案3:React原生事件绑定(推荐)

完全不需要手动操作DOM和ref,直接在渲染的元素上绑定onClick,这是React最推荐的方式,更符合数据驱动的设计理念:

const App = () => {
 const data = ['item1', 'item2', 'item3', 'item4', 'item5'];

 // 直接定义点击回调,接收当前索引或数据
 const handleItemClick = (index) => {
  console.log(`clicked data:${index}`);
 };

 return (
  <div className="App">
   {data.map((x, i) => {
    return (
     <div 
      className='item' 
      key={data[i]}
      onClick={() => handleItemClick(i)}
     >
      {x}
     </div>
    );
   })}
  </div>
 );
}
ReactDOM.render(<App/>, document.getElementById("root"));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 20:01:11