網(wǎng)站首頁 編程語言 正文
在?React Hooks
?中使用第三方庫的事件時(shí),很多人會寫成這樣(指的就是我):
const [count, setCount] = useState(0); useEffect(() => { const library = new Library(); library.on("click", () => { console.log(count); // 拿不到最新的 count }); }, []);
這樣寫會有問題:
它只會在這個(gè)組件加載時(shí),綁定事件,如果這個(gè)事件中用到了其他的?state
,那么這個(gè)狀態(tài)發(fā)生變化時(shí)事件中是拿不到最新的?state
你會想到,我把?state
?放到依賴項(xiàng)中:
const [count, setCount] = useState(0); useEffect(() => { const library = new Library(); // click 事件會重復(fù)綁定 library.on("click", () => { console.log(count); }); }, [count]);
這樣做又會有新問題:click
?事件會重復(fù)綁定
這時(shí)候你說那我先卸載?click
?事件,在綁定事件:
const [count, setCount] = useState(0); useEffect(() => { const library = new Library(); library.on("click", handleClick); return () => { // 卸載不掉事件,還是會重復(fù)綁定 handleClick && library.un("click", handleClick); }; }, [count]); const handleClick = () => { console.log(count); };
你驚奇的發(fā)現(xiàn),居然卸載不掉之前的事件,還是會重復(fù)綁定事件。
如何解決這個(gè)問題呢?
使用 addEventListener 代替第三方庫的事件
這里使用?addEventListener
?代替第三方庫的事件,初始代碼
const Test = (props) => { const ref = useRef(); const [count, setCount] = useState(0); useEffect(() => { const handleClick = (event) => { console.log("clicked"); console.log("count", count); }; const element = ref.current; element.addEventListener("click", handleClick); return () => { element.removeEventListener("click", handleClick); }; }, []); const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>點(diǎn)擊 +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
方法一:state 變化,卸載/綁定事件
將?state
?放在依賴項(xiàng)中,就要解決?state
?變化時(shí),事件重復(fù)綁定的問題
解決事件重復(fù)綁定問題,首先想到的是事件卸載
你很容易就會想到這樣寫
useEffect(() => { handleClick && ref.current.removeEventListener("click", handleClick); ref.current.addEventListener("click", handleClick); }, [count]); const handleClick = () => { console.log(count); };
這在?React Hooks
?中是一個(gè)坑,state
?變化后會?handleClick
?事件函數(shù)會重新聲明,新的?handleClick
?和之前的?handleClick
?不是一個(gè)事件函數(shù),導(dǎo)致?removeEventListener
?移除的事件函數(shù)不是之前的事件函數(shù)
那你又會想到,我給?handleClick
?加個(gè)?useCallback
useEffect(() => { handleClick && ref.current.removeEventListener("click", handleClick); ref.current.addEventListener("click", handleClick); }, [count]); const handleClick = useCallback(() => { console.log(count); }, []);
這樣寫的話還是會有同一個(gè)問題:依賴項(xiàng)為空數(shù)組,就拿不到最新的?state
;依賴項(xiàng)中放入?state
,state
?變化后就不是同一個(gè)事件函數(shù)了,無法移除事件
如何解決這個(gè)問題呢?
把事件函數(shù)保存為狀態(tài):
- 當(dāng)?
count
?變化時(shí),掛載事件,同時(shí)將事件函數(shù)保存為?state
- 當(dāng)?
eventFn.fn
?變化時(shí),在?useEffect return
?中卸載之前的事件函數(shù)(這里利用的是閉包)
具體的代碼:
const Test = () => { const ref = useRef(); const [count, setCount] = useState(0); const [eventFn, setEventFn] = useState({ fn: null }); useEffect(() => { mountEvent(); }, [count]); const mountEvent = () => { if (!ref.current) return; // eventFn.fn && ref.current.removeEventListener("click", eventFn.fn); // 下面看不懂的話,也可以這樣寫 ref.current.addEventListener("click", handleClick); setEventFn({ fn: handleClick }); }; useEffect(() => { return () => { eventFn.fn && ref.current.removeEventListener("click", eventFn.fn); // 這里用的是閉包,和上面注釋部分任選其一 }; }, [eventFn.fn]); const handleClick = () => { console.log(count); }; const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>點(diǎn)擊 +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
方法二:使用閉包的方式卸載事件
利用閉包,可以將方法一簡化
const Test = () => { const ref = useRef(); const [count, setCount] = useState(0); useEffect(() => { const element = ref.current; element.addEventListener("click", handleClick); return () => { element.removeEventListener("click", handleClick); }; }, [count]); const handleClick = () => { console.log(count); }; const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>點(diǎn)擊 +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
useEffect return
?中的變量用的是閉包,這點(diǎn)剛開始學(xué)的時(shí)候不好理解
方法三:使用 ref 保存狀態(tài)
ref
?保存的數(shù)據(jù)雖然不能用于頁面渲染,但可以作為?state
?備份,在?state
?變化時(shí)更新?ref
在事件函數(shù)中就能拿到最新的?stateRef
const Test = () => { const ref = useRef(); const [count, setCount] = useState(0); const countRef = useRef(count); useEffect(() => { countRef.current = count; }, [count]); useEffect(() => { const element = ref.current; element.addEventListener("click", handleClick); }, []); const handleClick = () => { console.log(countRef.current); }; const onClickIncrement = () => { setCount(count + 1); }; return ( <> <h2>Test</h2> <button onClick={onClickIncrement}>點(diǎn)擊 +1</button> <div>count: {count}</div> <button ref={ref}>Click Test Button</button> </> ); };
優(yōu)化 state 手動(dòng)維護(hù)
上面三種方法,都有個(gè)問題,state
?需要手動(dòng)維護(hù)
這一步如何優(yōu)化呢?
方法一和方法二,優(yōu)化的方式都一樣:將依賴項(xiàng)是?count
?改為?state
const [state, setState] = useState({ count: 0 }); useEffect(() => { // ... }, [state]);
方法三的優(yōu)化是,用?stateRef
?保存?ref
?對象,當(dāng)?state
?變化時(shí),遍歷?state
?給?stateRef
?賦值
事件函數(shù)中使用?stateRef
const [state, setState] = useState({ count: 0 }); const stateRef = useRef({}); useEffect(() => { Object.keys(state).forEach((key) => { stateRef.current[key] = state[key]; }); }, [state]);
原文鏈接:https://segmentfault.com/a/1190000043365896
相關(guān)推薦
- 2022-05-08 Python集合的增刪改查操作_python
- 2022-05-10 一起來學(xué)習(xí)C++中remove與erase的理解_C 語言
- 2023-10-14 C/C++ 批量梯度下降法實(shí)現(xiàn)一元線性回歸
- 2022-08-23 python使用redis模塊來跟redis實(shí)現(xiàn)交互_python
- 2022-09-12 python?通過dict(zip)和{}的方式構(gòu)造字典的方法_python
- 2022-11-05 pytest官方文檔解讀之安裝和使用插件的方法_python
- 2022-10-16 QT實(shí)現(xiàn)簡單TCP通信_C 語言
- 2022-06-24 C#利用itext實(shí)現(xiàn)PDF頁面處理與切分_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支