react實現阻止父容器滾動
react阻止父容器滾動
最近在做代碼遷移的時候出現一個問題,發(fā)現之前自己寫好的一個自定義滾動條組件有個bug,那就滾動時父容器也會滾動。
看一下代碼,代碼做了簡化
export default ()=>{
return return (
<div
className={classNames(getCls('container'), isDragRef.current ? 'active' : '', className)}
ref={scrollDOMRef}
onWheelCapture={(e: any) => {
e.preventDefault();
if (e.deltaY < 0) {
// 向上
setDragY(dragY - dragSpeed);
} else {
// 向下
setDragY(dragY + dragSpeed);
}
}}>
</div>
);
}
既然父容器會滾動那就阻止默認行為就好e.preventDefault();,但是沒用。
這里我猜測應該因為react的事件是合成事件的緣故,所有事件都注冊document上,所以導致阻止的默認行為并沒有阻止到父容器上。
那就用原生的唄。
useEffect(() => {
if (scrollDOMRef.current) {
scrollDOMRef.current.addEventListener('wheel', (e) => {
e.preventDefault();
if (e.deltaY < 0) {
// 向上
setDragY(dragY - dragSpeed);
} else {
// 向下
setDragY(dragY + dragSpeed);
}
});
}
}, [scrollDOMRef.current]);
原生事件和react事件一起用時要注意,阻止冒泡要考慮清楚,因為可能會導致react合成事件失效。
那這么做后就可以了嗎?
確實
父容器不滾動了
但是又掉進了react的閉包陷阱
注冊函數并沒有及時更新,dragY 和 dragSpeed的閉包導致出現了bug。
那么又應該怎么做呢?
useEffect(() => {
if (scrollDOMRef.current) {
scrollDOMRef.current.addEventListener('wheel', (e) => {
e.preventDefault();
if (e.deltaY < 0) {
// 向上
setDragY(dragY - dragSpeed);
} else {
// 向下
setDragY(dragY + dragSpeed);
}
});
}
}, [scrollDOMRef.current, dragY, dragSpeed]);
又一個問題出現了
每次應該把之前的事件銷毀,然后在注冊才對。
不然多個事件同時觸發(fā)導致了bug。
useEffect(() => {
const handle = (e: any) => {
e.preventDefault();
if (e.deltaY < 0) {
// 向上
setDragY(dragY - dragSpeed);
} else {
// 向下
setDragY(dragY + dragSpeed);
}
};
if (scrollDOMRef.current) {
scrollDOMRef.current.addEventListener('wheel', handle, {
passive: false,
});
}
return () => {
if (scrollDOMRef.current) {
scrollDOMRef.current.removeEventListener('wheel', handle);
}
};
}, [scrollDOMRef.current, dragY, dragSpeed]);
passive
passive為false時,瀏覽器執(zhí)行完回調函數才知道有沒有調用preventDefault,如果沒有調用preventDefault,再去執(zhí)行默認行為,就是滾動。這樣就回造成滾動不流暢。
passive為true,就是告訴瀏覽器不會調用preventDefault,瀏覽器直接執(zhí)行滾動就行,不用考慮回調函數了。
這時,即使你在回調函數里調用preventDefault也不會生效。
mdn中說,在有些瀏覽器(特別是Chrome和Firefox)中,你監(jiān)聽window、document或者document.body上的touchstart和touchmove,會將passive默認設置為true。
還是要提醒大家,在你不需要調用preventDefault的時候,監(jiān)聽scroll或者touchmove,將passive設置為true
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
react16.8.0以上MobX在hook中的使用方法詳解
這篇文章主要為大家介紹了react16.8.0以上MobX在hook中的使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07
React中完整實例講解Recoil狀態(tài)管理庫的使用
這篇文章主要介紹了React中Recoil狀態(tài)管理庫的使用,Recoil的產生源于Facebook內部一個可視化數據分析相關的應用,在使用React的實現的過程中,因為現有狀態(tài)管理工具不能很好的滿足應用的需求,因此催生出了Recoil,對Recoil感興趣可以參考下文2023-05-05
解決React報錯Expected an assignment or funct
這篇文章主要為大家介紹了React報錯Expected an assignment or function call and instead saw an expression解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12

