在循环中使用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
相关产品推荐
相关产品推荐

