成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Ahooks 的 UseClickAway 在 React 17 中不工作了,該怎么辦?

開發 前端
經常用到的組件掛載以及卸載的 useMount、useUnmount,還有支持自動請求、手動請求、防抖等各種功能請求 useRequest,以及可以將狀態同步存取到 localStorage 的 useLocalStorageState。

最近公司的前端項目從 React 16 升級到了 React 17,導致 ahooks 的 useClickAway 不能按預期工作。

下面西瓜哥我就來說說到底發生了什么事。

ahooks 中的 useClickAway

ahooks 是阿里巴巴維護的第三方 React Hook 庫,里面封裝了很多好用的 hook。

比如經常用到的組件掛載以及卸載的 useMount、useUnmount,還有支持自動請求、手動請求、防抖等各種功能請求 useRequest,以及可以將狀態同步存取到 localStorage 的 useLocalStorageState。

當你想要寫一個與業務無關的第三方 ahooks,你可以去 ahooks 里面找找,大概率能夠找到,是比較優秀的 hook 庫。

其中,useClickAway 的作用是 監聽目標元素外的點擊事件。

useClickAway 接受的第一個參數是一個事件回調函數。

第二個參數是被排除的目標元素,可以是 ref 或 DOM 元素,或者是它們組成的數組,

第三個是需要監聽的事件類型字符串或事件字符串數組。第三個參數是可選的,不使用的話默認用點擊事件 'click'

下面是一個常用的寫法:

useClickAway(() => {
console.log('點擊到元素外的地方');
}, ref);

useClickAway 的核心底層原理

核心底層原理是,是在 document 上綁定了一個冒泡事件。當事件冒泡到 document 時,會判斷事件目標元素是否為傳入的 ref 下的子元素。

如果是,什么都不做。如果不是,執行回調函數。

這里給出 useClickAway 的源碼地址,感興趣的話可以研究一下:

https://github.com/alibaba/hooks/blob/v3.5.0/packages/hooks/src/useClickAway/index.ts。

useClickAway 的問題

如果你在 React 16 中使用 useClickAway,一切都表現良好。

但如果是 React 17 及以上版本使用,在一些情況下會有問題。

我們有這么一個場景。

點擊一個搜索按鈕,會出現一個輸入框,此時用戶需要在這個輸入框內輸入文字來搜索。如果點擊到搜索按鈕外的地方,輸入框會消失。

ahooks 的 useClickAway 在 React 17 中不工作了

核心實現如下:

function App() {
const [visible, setVisible] = useState(false);
const inputRef = useRef();
useClickAway(() => {
setVisible(false);
}, inputRef);
return (
<div>
<button onClick={() => setVisible(true)}>搜索</button>
{visible && <input ref={inputRef} autoFocus />}
</div>
);
}

這里提供一個線上 demo(用的是 React 17 版本):

https://codesandbox.io/s/f54siy。

在 React 16 的時候,上面的寫法是正常的。但升級到 17 后,你會發現點擊 button 后什么事情都沒有發生。

React 17 的事件系統改造

?原因在于 React 17 對事件系統進行了改造。

16 升級到 17 后,React 將事件委托到 ReactDOM 掛載的根節點上,比如 div#app,而不再是原來 document。

ahooks 的 useClickAway 在 React 17 中不工作了

?首先,我們要知道的是,當調用 setVisible(true) 改變組件狀態時,組件就立即被重新渲染了,然后調用了 useClickAway。狀態更新后的組件重渲染是同步的,此時我們的事件流其實還沒有結束。

需要注意的是,更新狀態后的組件重新渲染,可能是同步,也可能是異步的。?

在 React 16 中,事件都委托到了 document 上。

我們點擊 button 元素,產生了一個事件流,當點擊事件流動到 document 時,我們將 visible 設置為 true,組件進行了一次同步的重新渲染,并調用 useClickAway,做了個 document 上的冒泡事件綁定。

就像下面這樣:

document.addEventListener('click', () => {
console.log('顯示輸入框')
// React 16 中 useClickAway 綁定事件的時機
document.addEventListener('click', () => {
console.log('隱藏輸入框');
});
});
// 點擊后的輸出內容為:
// 顯示輸入框

在一個元素的事件觸發過程中,往這個元素上注冊新的相同類型的事件響應函數,這個新的響應函數不會在此次事件流上立即觸發。

所以,前面的 useClickAway 寫法在 React 16 是正常的。

但是,在 React 17 中就不同了,事件委托下放到了 div#app 中。

ahooks 的 useClickAway 在 React 17 中不工作了

點擊按鈕,事件流冒泡到 div#app 元素,執行事件回調函數將 visible 設置為了 true,并重新渲染組件,執行 useClickAway 再給 document 綁定了新的事件響應函數。

此時事件流沒有結束,繼續冒泡到 document,將 visible 又設置回了 false。

所以,visible 在短暫地變成 true 后,又變回了 false,無事發生。

document.querySelector('#app').addEventListener('click', () => {
console.log('顯示輸入框')
// React 17 中 useClickAway 綁定事件的時機
document.addEventListener('click', () => {
console.log('隱藏輸入框');
});
});
// 點擊后的輸出內容為:
// 顯示輸入框
// 隱藏輸入框

解決方案

方案 1:阻止冒泡

<button
onClick={(e) => {
e.stopPropagation();
setVisible(true);
}}
>

我們給按鈕加上阻止事件冒泡,提前結束事件流,使其不流到 document 上,就不會觸發 document 的點擊事件。

但這樣也是有隱患的,e.stopPropagation 是破壞性的。

如果我們在其他的地方要寫一些特殊的判斷失焦邏輯,也要用到類似 useClickAway 的做法,我們點到這個 button 上就會讓其他地方的邏輯走不通。

CSS 中的 overflow: hidden; 也具有破壞性,如果設置了該屬性的容器內部的元素超出了容器范圍,會被截斷。

方案 2:修改綁定事件類型為 mousedown / touchstart

useClickAway(
() => setVisible(false),
inputRef,
['mousedown', 'touchstart']
);

mousedown 在 click 事件之前就結束了,所以在 click 事件流過程中不會觸發它。

touchstart 是為了兼容移動設備的情況。因為觸屏時,touchstart 一定會觸發,mousedown 不一定,順帶一提,click 也不一定。

其他的比較優秀的第三方 React Hooks 庫,比如 react-use 的 useClickAway,其實就是用 mousedown 和 touchstart 作為默認事件類型。

還有百度的 react-hooks 庫,其下的 useClickOutside 不支持自定義事件類型,但也是用的 mousedown 和 touchstart。

方案 3:將 button 元素也傳給 useClickAway

useClickAway(
() => setVisible(false),
[inputRef, buttonRef]
);

這樣就可以把 button 也排除在觸發條件外。

但這樣寫很繁瑣。如果輸入框要封裝成一個組件,你還得把 buttonRef 傳入到這個組件中。

方案 4:延遲輸入框出現時機

<button
onClick={() => {
setTimeout(() => {
setVisible(true);
});
}}
>

通過 setTimeout 的方式,確保輸入框的出現在同步的事件流之后才出現,然后才觸發 useClickAway 綁定邏輯。

結尾

React 16 升級為 17 后,React 中混合事件托管綁定到了 React 組件樹掛載的 div#app 上,不再是之前的 document。

這讓默認注冊為 click 事件類型的 useClickAway 在一些場景下,表現上和 React 16 有一些不同。

對于上面的場景以及解決方案,我認為最好的是第二種:給 useClickAway 的事件類型設置為 mousedown 和 touchstart。這種方法更有普適性。

責任編輯:姜華 來源: 今日頭條
相關推薦

2021-06-16 06:14:29

React17Toast組件React bug

2019-06-03 10:53:49

MySQL硬盤數據庫

2019-01-04 10:20:48

桌面圖標 圖標閃爍Windows 10

2022-04-22 10:30:07

框架JavaScript前端

2011-06-30 17:58:30

網站被K

2021-03-17 10:55:14

Redis哈希數據庫

2022-06-08 08:01:20

useEffect數組函數

2018-08-20 19:39:14

區塊鏈職業崗位

2010-11-17 11:06:34

跳槽

2016-11-24 14:44:49

云計算

2021-01-26 08:02:04

Redis內存數據庫

2010-05-20 16:08:01

亞馬遜故障

2024-10-17 10:25:34

2017-12-08 11:14:21

2013-10-30 13:19:12

2015-10-22 09:09:59

BAT投資VC

2021-02-23 11:10:33

人工智能機器人工作

2022-09-08 11:39:35

架構

2012-08-13 10:53:28

IT運維

2013-07-15 09:51:04

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 午夜电影网 | 天天综合网天天综合色 | 国产电影一区二区 | 一区中文字幕 | 在线观看日本高清二区 | 国产一区二区三区在线看 | jlzzjlzz国产精品久久 | 国产精品大片 | 四虎永久免费在线 | 爱爱免费视频网站 | 天天干亚洲 | www.亚洲国产精品 | 欧美xxxx色视频在线观看免费 | 91精品在线观看入口 | 国产欧美日韩一区 | 欧美日韩久久久 | 欧美黄在线观看 | 成人av播放 | 天堂色综合| 99久久99 | 久久精品中文字幕 | 久久久久久久一级 | www.久草.com | 中文字幕av中文字幕 | 特级黄一级播放 | 亚洲欧洲激情 | 国产精品美女久久久av超清 | 精品国产欧美 | 成人三级av | 欧美天堂 | 国产小视频在线 | 91久久久久 | 精品欧美一区二区精品久久久 | 久久99久久98精品免观看软件 | 亚洲高清视频在线观看 | 亚洲国产精品一区二区三区 | 精品国产乱码久久久久久牛牛 | 久久日韩精品一区二区三区 | 凹凸日日摸日日碰夜夜 | 久久乐国产精品 | 天天综合网天天综合色 |