時(shí)機(jī))
react-use 中的 useCustomCompareEffect用自定義比較器掌控 Effect 依賴觸發(fā)時(shí)機(jī)【免費(fèi)下載鏈接】react-useReact Hooks — 項(xiàng)目地址: https://gitcode.com/gh_mirrors/re/react-use導(dǎo)讀useCustomCompareEffect是 react-use 提供的一個(gè)useEffect增強(qiáng)版 Hook核心能力是允許開(kāi)發(fā)者傳入自定義比較函數(shù)depsEqual來(lái)判定依賴數(shù)組是否變化從而擺脫 React 默認(rèn)的引用相等reference equality比較策略。當(dāng)你的 Effect 依賴中混入了每次渲染都會(huì)被重新創(chuàng)建的對(duì)象、數(shù)組或函數(shù)時(shí)這個(gè) Hook 能顯著減少不必要的副作用執(zhí)行。讀完本文你將掌握它的用法、簽名、實(shí)現(xiàn)原理、開(kāi)發(fā)期警告機(jī)制以及它與useDeepCompareEffect、useShallowCompareEffect的演進(jìn)關(guān)系。為什么需要自定義依賴比較引用相等的痛點(diǎn)React 內(nèi)置的useEffect采用Object.is級(jí)別的引用比較來(lái)檢查依賴是否變化。只要依賴數(shù)組中的某個(gè)元素是每次渲染都會(huì)新建的對(duì)象字面量即便其結(jié)構(gòu)、內(nèi)容完全一致React 也會(huì)認(rèn)為依賴變了從而在每次渲染后重新執(zhí)行 Effectconst options { step: 2 }; // 每次渲染都是全新對(duì)象引用不同 useEffect(() { // 每次渲染都會(huì)執(zhí)行即使 options 內(nèi)容從未改變 }, [options]);這在傳遞 props、讀取配置對(duì)象、傳入回調(diào)函數(shù)等場(chǎng)景下非常常見(jiàn)會(huì)造成副作用被反復(fù)觸發(fā)甚至引發(fā)死循環(huán)。useCustomCompareEffect正是為了解決這一問(wèn)題而設(shè)計(jì)它允許你用自己的比較器如深比較isEqual、淺比較或任意自定義規(guī)則代替引用相等判斷?;居梅ü俜轿臋n示例根據(jù) useCustomCompareEffect.md 的說(shuō)明其用法與useEffect幾乎一致只是多傳了第三個(gè)參數(shù)——比較函數(shù)。官方示例使用lodash/isEqual做深比較import {useCounter, useCustomCompareEffect} from react-use; import isEqual from lodash/isEqual; const Demo () { const [count, {inc: inc}] useCounter(0); const options { step: 2 }; useCustomCompareEffect(() { inc(options.step) }, [options], (prevDeps, nextDeps) isEqual(prevDeps, nextDeps)); return ( div puseCustomCompareEffect with deep comparison: {count}/p /div ); };當(dāng)options對(duì)象的內(nèi)容保持不變時(shí)即使父組件重新渲染導(dǎo)致options的引用發(fā)生變化比較函數(shù)仍判定前后依賴相等Effect 便不會(huì)重復(fù)執(zhí)行。函數(shù)簽名與參數(shù)說(shuō)明官方 Reference 給出的 TypeScript 簽名如下useCustomCompareEffect(effect: () void | (() void | undefined), deps: any[], depsEqual: (prevDeps: any[], nextDeps: any[]) boolean);對(duì)照 src/useCustomCompareEffect.ts 中的泛型實(shí)現(xiàn)三個(gè)參數(shù)的含義為參數(shù)類型說(shuō)明effectEffectCallback與useEffect相同的副作用回調(diào)可返回清理函數(shù)cleanup在卸載或依賴變化前執(zhí)行depsTDeps extends DependencyList依賴數(shù)組類型上約束為 React 的DependencyList即任意值的數(shù)組depsEqual(prevDeps: TDeps, nextDeps: TDeps) boolean自定義比較器接收上一次依賴數(shù)組與本次依賴數(shù)組返回true表示兩者相等不觸發(fā) Effect比較函數(shù)應(yīng)遵循一個(gè)關(guān)鍵約定返回true表示前后依賴相等、Effect 不重新執(zhí)行返回false表示依賴已變化、Effect 重新執(zhí)行。返回值的語(yǔ)義與 lodash 的isEqual、fast-deep-equal等工具完全一致可以直接復(fù)用。源碼級(jí)原理ref 緩存 條件更新依賴從實(shí)現(xiàn)看useCustomCompareEffect的底層邏輯非常簡(jiǎn)潔見(jiàn) src/useCustomCompareEffect.tsconst ref useRefTDeps | undefined(undefined); if (!ref.current || !depsEqual(deps, ref.current)) { ref.current deps; } useEffect(effect, ref.current);核心機(jī)制可以拆解為三步用一個(gè)useRef保存上一次被認(rèn)定為有效的依賴數(shù)組每次渲染時(shí)用depsEqual將本次依賴與 ref 中緩存的上次依賴做比較只有比較結(jié)果為false即依賴真正變化時(shí)才更新 ref 中保存的依賴將 ref 中穩(wěn)定的依賴數(shù)組傳給真正的useEffect。由于對(duì)象內(nèi)容不變時(shí) ref 始終保持同一個(gè)引用useEffect內(nèi)部的引用比較永遠(yuǎn)通過(guò)Effect 便不會(huì)重復(fù)執(zhí)行。這里需要特別說(shuō)明比較過(guò)程發(fā)生在渲染階段render 階段而不是 Effect 階段。因此傳入的depsEqual應(yīng)當(dāng)是純函數(shù)且不應(yīng)在其中產(chǎn)生副作用以符合 React 渲染階段的約束。另外依賴數(shù)組本身仍被useEffect以引用方式比較所以depsEqual本質(zhì)上替代的是判斷本次依賴是否值得更新這一環(huán)節(jié)這也是它比普通useEffect更靈活的根本原因。開(kāi)發(fā)期安全警告何時(shí)不該用它源碼在NODE_ENV ! production條件下內(nèi)置了三組開(kāi)發(fā)期警告見(jiàn) src/useCustomCompareEffect.ts幫助開(kāi)發(fā)者在開(kāi)發(fā)環(huán)境及早發(fā)現(xiàn)誤用依賴為空數(shù)組useCustomCompareEffect不應(yīng)用于無(wú)依賴的場(chǎng)景此時(shí)應(yīng)直接使用React.useEffect依賴全部是原始值若deps中所有元素都是原始類型通過(guò)isPrimitive (val) val ! Object(val)判斷引用比較與自定義比較沒(méi)有區(qū)別直接用useEffect即可無(wú)需引入額外開(kāi)銷未傳入比較函數(shù)depsEqual必須是一個(gè)函數(shù)否則無(wú)法完成依賴比較。這些警告表明這個(gè) Hook 是為依賴中包含對(duì)象、數(shù)組、函數(shù)等引用類型的場(chǎng)景量身定制的濫用反而會(huì)帶來(lái)不必要的性能與認(rèn)知成本。與兄弟 Hook 的關(guān)系深比較、淺比較的通用底座useCustomCompareEffect在 react-use 中是更通用、更底層的實(shí)現(xiàn)另外兩個(gè)常用 Hook 正是基于它封裝的useDeepCompareEffect.ts 內(nèi)部調(diào)用useCustomCompareEffect(effect, deps, isDeepEqual)其中isDeepEqual來(lái)自 misc/isDeepEqual.ts實(shí)際是fast-deep-equal/react的再導(dǎo)出對(duì)依賴數(shù)組做深比較useShallowCompareEffect.ts 內(nèi)部調(diào)用useCustomCompareEffect(effect, deps, shallowEqualDepsList)其中shallowEqualDepsList基于fast-shallow-equal的equal對(duì)數(shù)組逐項(xiàng)做淺比較src/useShallowCompareEffect.ts。也就是說(shuō)useDeepCompareEffect相當(dāng)于深比較版useShallowCompareEffect相當(dāng)于淺比較版而useCustomCompareEffect把比較策略完全開(kāi)放給調(diào)用方。三者在 README 中被并列收錄見(jiàn) README.md使用時(shí)可根據(jù)依賴結(jié)構(gòu)的復(fù)雜度選擇依賴較淺用淺比較、依賴嵌套較深用深比較、有特殊業(yè)務(wù)規(guī)則時(shí)則直接編寫(xiě)自定義比較器。測(cè)試驗(yàn)證引用不變不觸發(fā)、卸載執(zhí)行清理倉(cāng)庫(kù)中的測(cè)試用例 tests/useCustomCompareEffect.test.ts 印證了上述行為第一個(gè)用例對(duì)比了原生useEffect與useCustomCompareEffect配合isDeepEqual當(dāng)options { max: 10 }被反復(fù)賦值為相同內(nèi)容的新對(duì)象并重渲染時(shí)原生useEffect的 mock 被調(diào)用了 3 次而useCustomCompareEffect的 mock 始終只執(zhí)行了 1 次——這正是深比較帶來(lái)的效果第二個(gè)用例驗(yàn)證了清理函數(shù)語(yǔ)義Effect 返回的 cleanup 在組件卸載unmount()時(shí)被調(diào)用與useEffect的生命周期行為保持一致。這兩個(gè)用例分別覆蓋了抑制重復(fù)執(zhí)行與清理函數(shù)正常觸發(fā)兩條核心行為可作為你理解或遷移該 Hook 時(shí)的行為基準(zhǔn)。實(shí)踐建議與注意事項(xiàng)優(yōu)先使用官方封裝的深/淺比較版本如果lodash/isEqual或fast-deep-equal的深比較、fast-shallow-equal的淺比較已滿足需求優(yōu)先選用useDeepCompareEffect/useShallowCompareEffect代碼更簡(jiǎn)潔需要自定義規(guī)則如忽略某些字段、比較數(shù)組子集時(shí)才直接使用useCustomCompareEffect。比較器必須穩(wěn)定且純depsEqual每次渲染都會(huì)執(zhí)行應(yīng)避免在其中做耗時(shí)操作如深比較超大對(duì)象或產(chǎn)生副作用必要時(shí)可對(duì)依賴做 memo 化。理解返回語(yǔ)義depsEqual返回true表示相等、不觸發(fā)與直覺(jué)上變化才返回 true相反編碼時(shí)容易寫(xiě)反需特別留意。善用開(kāi)發(fā)期警告若依賴全為原始值或?yàn)榭諗?shù)組開(kāi)發(fā)控制臺(tái)會(huì)提示改用React.useEffect這通常意味著當(dāng)前用法并不需要該 Hook。依賴數(shù)組本身要保持穩(wěn)定deps數(shù)組的引用變化不會(huì)直接影響觸發(fā)邏輯因?yàn)楸容^的是數(shù)組內(nèi)容但請(qǐng)確保每次渲染傳入的依賴數(shù)組與 Effect 中實(shí)際讀取的值一致遵循 React Hooks 的依賴完整性原則。小結(jié)useCustomCompareEffect是 react-use 中自定義 Effect 依賴比較這一能力的通用底座它以depsEqual回調(diào)替代 React 默認(rèn)的引用相等比較配合useRef緩存實(shí)現(xiàn)了內(nèi)容相等即不觸發(fā)的效果useDeepCompareEffect與useShallowCompareEffect則是它針對(duì)深比較、淺比較兩種常見(jiàn)策略的現(xiàn)成封裝。理解它的參數(shù)語(yǔ)義與底層實(shí)現(xiàn)能幫助你在面對(duì)每次渲染都新建對(duì)象導(dǎo)致 Effect 重復(fù)執(zhí)行的經(jīng)典問(wèn)題時(shí)做出正確、高效的選擇。【免費(fèi)下載鏈接】react-useReact Hooks — 項(xiàng)目地址: https://gitcode.com/gh_mirrors/re/react-use創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考