最近在看之前的代码时,发现年初在熟悉react hooks新特性时写下了这样一段代码:javascript
let i = 0; function Test(props) { const { loading, error, startFetch } = props; useEffect(() => { const $btn = document.querySelector('.btn'); // .info-con 存在于外层的dom中 $btn.addEventListener('click', () => { const action =`action data${i++}`; console.log('resbtn', action); }, false); return $btn.removeEventListener('click', () => {}); }); if (error) { return <div>{error.msg}</div>; } return ( <div> {loading && <h3>loading</h3>} <h3 className="mt-10 mb-10">finished</h3> <button onClick={startFetch}>获取数据</button> <h4 className="mt-10"><span className="btn">Saga模拟测试</span></h4> </div> ); }
我用了addEventListener和removeEventListener来尝试useEffect的挂载和清除功能,细心的你,发现这段代码有几个错误呢?
自我观察,自认为是有以下几个的:html
第1个和第3个错误,是能够原谅的,当时本身对hooks还不熟悉。但第二个错误,是不可原谅的,这是须要反省的。本文后面不会对useEffect作深刻讲解,官方文档已经足够清楚,后面围绕removeEventListener来剖析。前端
第二个错误,拆开看,又可分为两个方面:removeEventListener使用方式多余与语法错误。java
关于语法错误,官方文档中有这样一段描述: react
因为我在添加监听时,使用的是箭头函数,因此删除时没法找到相同引用的监听事件,因此第一件事就是改变监听函数的写法。完善后,写法是下面这样的:面试
let i = 0; function Test(props) { const { loading, error, startFetch } = props; useEffect(() => { const $btn = document.querySelector('.info-con'); const eventAction = () => { const action =`action data${i++}`; console.log('resbtn:', action); }; $btn.addEventListener('click', eventAction); return () => { $btn.removeEventListener('click', eventAction); }; }, []); if (error) { return <div>{error.msg}</div>; } return ( <div> {loading && <h3>loading</h3>} <h3 className="mt-10 mb-10">finished</h3> <button onClick={startFetch}>获取数据</button> <h4 className="mt-10"><span className="btn">Saga模拟测试</span></h4> </div> ); }
到此,看似已经结束了。但既然已经打开了,就深刻的学习一下这个api吧,经常使用的前两个参数,咱们都很熟悉,第三个参数写成布尔值也偶尔会有,可是否已足够了解呢?api
addEventListener和removeEventListener传参是同样的,第三个可选参数都是一个对象或者一个布尔值:数组
target.addEventListener(type, listener[, options]); target.addEventListener(type, listener[, useCapture]); target.removeEventListener(type, listener[, options]); target.removeEventListener(type, listener[, useCapture]);
当参数为布尔值时,意指useCapture,是否在捕获阶段触发监听函数。而为对象时,可用选项以下: 闭包
之因此第三个参数有两种形态,是在旧版本中只存在一个布尔值,即useCapture属性;但随着时间推移以及发展的须要,须要支持设置更多的特性设置,因此有了options选项这个对象传参,又为了兼容之前的老程序,因此对二者进行了兼容。once和passive属性很是有趣,但我还没想到合适的使用它们的场景。查看官方文档,发现里面确实有好多之前没有关注到的东西,值得细细品味。
其实在js不少api中,咱们都只用了一些经常使用的用法,而忽略了一些存在且也很适用的不经常使用传参,好比下面这些:框架
function test(){ for (var i=0; i<5; i ) { setTimeout( function timer() { console.log(new Date(),i); }, 1000); } console.log('end',new Date(),i); }
这一次关于对removeEventListener的使用错误,让我不得不意识到,对于本身下一步技术提高的着重点仍是该多关注基础,不要学的太宽泛,而成为泛而不精的人,最后一无可取。最近这一年确实太痴迷(yilai)于框架(React),基础关注的太少。框架与基础,框架层出不穷,但基础只是持续在演进。