如果我們想在hooks里面獲同步取最新的值,那么則可以使用useRef, 關鍵源碼如下:
function mountRef<T>(initialValue: T): {|current: T|} {const hook = mountWorkInProgressHook();const ref = {current: initialValue};hook.memoizedState = ref;return ref;
}function updateRef<T>(initialValue: T): {|current: T|} {const hook = updateWorkInProgressHook();return hook.memoizedState;
}
可以看到,代碼實現非常簡單,創建一個ref對象,然后掛載到hook.memoizedState, 我們在修改的時候,就是直接修改ref.current。useRef其實就是提供一個穩定的變量,在組件的整個生命周期都是持續存在且是同一個引用。
注意:修改useRef返回的狀態不會引起UI的重渲染。
為什么在setTimeout的回調函數里面,拿不到useState的最新值?
主要有以下三點原因:
?? react 中的state,遵循著狀態不可變的原則,在setState之后,不會修改原來老的state的值,而是把新值重新賦值給hook.memoizedState。
???對于react函數組件,其本身就是個render函數,每次重渲染之后,都會重新執行此函數,而每次執行的時候就會產生一個新的函數作用域。
???setTimeout的回調函數對hook.memoizedState形成了閉包引用,而在setState之后,都會重新執行組件函數,setTimeout 的回調函數會捕獲在 setTimeout 被創建時的狀態的快照,而不是最新的狀態。
但是為什么又能獲取useRef的最新值呢?
useRef本身并不能解決閉包引用的問題,但是它通過創建一個穩定的引用:
function mountRef<T>(initialValue: T): {|current: T|} {const hook = mountWorkInProgressHook();const ref = {current: initialValue};hook.memoizedState = ref;return ref;
}
mount在整個組件生命周期,只會觸發一次,因此只會創建一次。然后這也是為什么要創建一個對象,而對象上近僅創建一個current屬性來存儲數據,正是為了讓開發者在整個生命周期,拿到的始終是同一個引用,可以把它想象成當前組件域下的一個全局變量了,而修改數據僅僅在這個引用上的current屬性修改。