DOM在Ahooks中的處理過程
導讀 | 一個優秀的工具庫應該有自己的一套輸入輸出規範,一來能夠支援更多的場景,二來可以更好的在內部進行封裝處理,三來使用者能夠更加快速熟悉和使用相應的功能,能做到舉一反三。 |
本篇文章探討一下 ahooks 對 DOM 類 Hooks 使用規範,以及原始碼中是如何去做處理的。
這一章節,大部分參考官方文件的 DOM 類 Hooks 使用規範[1]。
第一點,ahooks 大部分 DOM 類 Hooks 都會接收 target 引數,表示要處理的元素。
target 支援三種型別 React.MutableRefObject(透過 useRef 儲存的 DOM)、HTMLElement、() => HTMLElement(一般運用於 SSR 場景)。
第二點,DOM 類 Hooks 的 target 是支援動態變化的。如下所示:
export default () => { const [boolean, { toggle }] = useBoolean(); const ref = useRef(null); const ref2 = useRef(null); const isHovering = useHover(boolean ? ref : ref2); return ( <>{isHovering ? 'hover' : 'leaveHover'}{isHovering ? 'hover' : 'leaveHover'}); };
那 ahooks 是怎麼處理這兩點的呢?
獲取到對應的 DOM 元素,這一點主要相容以上第一點的入參規範。
假如是函式,則取執行完後的結果。
假如擁有 current 屬性,則取 current 屬性的值,相容React.MutableRefObject 型別。
最後就是普通的 DOM 元素。
export function getTargetElement(target: BasicTarget, defaultElement?: T) { // 省略部分程式碼... let targetElement: TargetValue; if (isFunction(target)) { // 支援函式獲取 targetElement = target(); // 假如 ref,則返回 current } else if ('current' in target) { targetElement = target.current; // 支援 DOM } else { targetElement = target; } return targetElement; }
這個方法,主要是為了支援第二點,支援 target 動態變化。
其中 packages/hooks/src/utils/useEffectWithTarget.ts 是使用 useEffect。
import { useEffect } from 'react'; import createEffectWithTarget from './createEffectWithTarget'; const useEffectWithTarget = createEffectWithTarget(useEffect); export default useEffectWithTarget;
另外 其中 packages/hooks/src/utils/useLayoutEffectWithTarget.ts 是使用 useLayoutEffect。
import { useLayoutEffect } from 'react'; import createEffectWithTarget from './createEffectWithTarget'; const useEffectWithTarget = createEffectWithTarget(useLayoutEffect); export default useEffectWithTarget;
兩者都是呼叫的 createEffectWithTarget,只是入參不同。
直接重點看這個 createEffectWithTarget 函式:
createEffectWithTarget 返回的函式useEffectWithTarget接受三個引數,前兩個跟 useEffect 一樣,第三個就是 target。
useEffectType 就是 useEffect 或者 useLayoutEffect。注意這裡呼叫的時候,沒傳第二個引數,也就是每次都會執行。
hasInitRef 判斷是否已經初始化。lastElementRef 記錄的是最後一次 target 元素的列表。lastDepsRef 記錄的是最後一次的依賴。unLoadRef 是執行完 effect 函式(對應的就是 useEffect 中的 effect 函式)的返回值,在元件解除安裝的時候執行。
第一次執行的時候,執行相應的邏輯,並記錄下最後一次執行的相應的 target 元素以及依賴。
後面每次執行的時候,都判斷目標元素或者依賴是否發生變化,發生變化,則執行對應的 effect 函式。並更新最後一次執行的依賴。
元件解除安裝的時候,執行 unLoadRef.current?.() 函式,並重置 hasInitRef 為 false。
const createEffectWithTarget = (useEffectType: typeof useEffect | typeof useLayoutEffect) => { /** * @param effect * @param deps * @param target target should compare ref.current vs ref.current, dom vs dom, ()=>dom vs ()=>dom */ const useEffectWithTarget = ( effect: EffectCallback, deps: DependencyList, target: BasicTarget| BasicTarget[], ) => { const hasInitRef = useRef(false); const lastElementRef = useRef<(Element | null)[]>([]); const lastDepsRef = useRef([]); const unLoadRef = useRef(); // useEffect 或者 useLayoutEffect useEffectType(() => { // 處理 DOM 目標元素 const targets = Array.isArray(target) ? target : [target]; const els = targets.map((item) => getTargetElement(item)); // init run // 首次初始化的時候執行 if (!hasInitRef.current) { hasInitRef.current = true; lastElementRef.current = els; lastDepsRef.current = deps; // 執行回撥中的 effect 函式 unLoadRef.current = effect(); return; } // 非首次執行的邏輯 if ( // 目標元素或者依賴發生變化 els.length !== lastElementRef.current.length || !depsAreSame(els, lastElementRef.current) || !depsAreSame(deps, lastDepsRef.current) ) { // 執行上次返回的結果 unLoadRef.current?.(); // 更新 lastElementRef.current = els; lastDepsRef.current = deps; unLoadRef.current = effect(); } }); useUnmount(() => { // 解除安裝 unLoadRef.current?.(); // for react-refresh hasInitRef.current = false; }); }; return useEffectWithTarget; };
一個優秀的工具庫應該有自己的一套輸入輸出規範,一來能夠支援更多的場景,二來可以更好的在內部進行封裝處理,三來使用者能夠更加快速熟悉和使用相應的功能,能做到舉一反三。
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2906855/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Nucleus中斷處理過程!!!!
- python中PCA的處理過程PythonPCA
- MYSQL匯入中斷處理過程MySql
- OGG 配置過程中的錯誤處理
- oracle處理SQL的過程OracleSQL
- 【解讀 ahooks 原始碼系列】DOM篇(一)Hook原始碼
- ovm安裝過程及中斷處理
- 異常處理過程
- SQL語句的處理過程SQL
- 分散裝運處理的過程
- Linux 核心處理中斷全過程解析Linux
- 如何處理六西格瑪過程中遇到的問題?
- DML 語句處理過程
- SQL語句的處理過程修正SQL
- OnWndMsg函式的處理過程函式
- 【故障處理】一次RAC故障處理過程
- 11g包dbms_parallel_execute在海量資料處理過程中的應用Parallel
- jquery的ajax傳遞資料過程中的資料處理jQuery
- 揭秘Vue從Virtual DOM生成真實DOM的過程Vue
- 資料庫變慢的處理過程資料庫
- 【原始碼】Redis命令處理過程原始碼Redis
- 某次BW 異常處理過程
- ora-14452處理過程
- HSG80故障處理過程
- ora-04031處理過程
- hibernate如何處理儲存過程中的複雜邏輯儲存過程
- IMP過程中報ORA-00907錯誤的處理
- oracle 的DML命令的詳細處理過程Oracle
- 在Spring Bean例項過程中,如何使用反射和遞迴處理的Bean屬性填充?SpringBean反射遞迴
- [MySQL光速入門]017 儲存過程中的"異常處理"MySql儲存過程
- 一次壞塊的處理過程(一)
- 一次壞塊的處理過程(二)
- MySQL儲存過程的異常處理方法MySql儲存過程
- fastHttp服務端處理請求的過程ASTHTTP服務端
- Flink流處理過程的部分原理分析
- GC析構物件和列表的處理過程GC物件
- undo表空間損壞的處理過程
- 一次壞塊的處理過程 [轉]