使用 React Hooks 的 5 條建議
Hooks 越來越成為體驗React之美的新潮方式。本文為您提供了一些有關如何正確使用 Hooks 的建議,以及詳細的解釋和見解。
建議 #1:適當使用 Hook
當 Hooks 剛出來的時候,React 社區(qū)的人氣一直很高。鉤子很酷!掛鉤很流行!Hooks 是 React 的未來!
我的一些同事甚至試圖將他們的項目代碼庫遷移到 Hooks 的幻想世界。不過要小心!如果您不了解,請不要使用 HooksReact 團隊發(fā)明 Hooks 的動機. 請至少花 30 分鐘閱讀并深入思考。
在這里總結一下,我可以給你 2 個 Hooks 存在的理由:
從組件中提取狀態(tài)邏輯,以便可以獨立測試并輕松重用
提供 class的替代方案,因為這 充滿了困惑和驚喜。
我承認Hooks盡到了自己的職責,值得表揚。我真的很喜歡胡克斯!但我的建議是,如果你對上面提到的這些原因沒有任何特別的問題,并且如果你的代碼已經簡潔有效,那么就沒有必要用 React Hooks 重寫你所有的組件,除非你真的想嘗試炒作。
建議 #2:React Hooks 不會取代 Redux
鉤子很棒,但沒有。
Eric Elliott 寫了一篇很棒的文章這里解釋為什么 Hooks 不是Redux的絕對替代品。
Hooks 是一組 API(或函數(shù)),可幫助您使用狀態(tài)和其他 React 功能。同時,Redux 是一種架構,您可以使用它來組織代碼并以可預測的方式控制流程。
在談論 Redux 時,我們不僅僅提到狀態(tài)管理庫,我們還提到了一個巨大的 Redux 生態(tài)系統(tǒng),其中有大量的開源庫和強大的工具來幫助你處理副作用、不可變性、持久性……
此外,您可以將 Redux 與 Hooks 一起使用。因此,與其重新發(fā)明輪子,不如利用兩者來實現(xiàn)更大的利益。
建議 #3:避免狀態(tài)突變
類似于 class組件中的this.setState ,不要直接修改state:
const [ user , setUser ] = useState ({name: "Jane"}) //錯誤的用戶.name = "John" // 不會重新渲染// 因為user的引用仍然是同一個setUser ( user )
相反,總是創(chuàng)建一個新狀態(tài):
const [ user , setUser ] = useState ({name: "Jane"}) // 將觸發(fā)重新渲染setUser ({... user , name: "Jane"})
建議 4:了解依賴關系數(shù)組的工作原理
許多 React 開發(fā)人員習慣于組件生命周期,尤其是componentDidMount 和 componentDidUpdate。所以在學習 Hooks 時,他們的第一個問題通常是:“如何將這段代碼運行一次?”。
換句話說,如何讓 useEffect在渲染 后只運行一次 (比如 componentDidMount)。答案是 dependencies array。
這是您作為第二個參數(shù)傳遞給useEffect的數(shù)組:
useEffect(() => { }), depArr )
Dependencies 數(shù)組是用于淺比較以確定何時重新運行效果的值列表。我們可以想象這樣一個偽代碼:
// 這旨在說明這個想法。// 不是 useEffect 的確切實現(xiàn)。// React 使用的實際比較算法是 Object.is lastDeps = null useEffect = (func, deps) => {
如果(!部門)
返回函數(shù)()
如果(!lastDeps){
lastDeps = [...deps] // 淺拷貝
返回函數(shù)()
}
for (i = 0; i < deps.length; ++i)
if (lastDeps[i] !== deps[i]) // 淺比較
返回函數(shù)()}
看看這個useEffect的偽實現(xiàn),你可以更好地理解為什么以及什么時候 useEffect 會在下面的代碼中重新運行:
// 每次渲染后運行useEffect (() => {
console.log('運行效果')}) // 在第一次渲染后只運行一次useEffect (() => {
console.log('運行效果')}, [])
在下面的代碼中, useEffect 執(zhí)行引用相等性檢查,因此它無法確定 用戶的任何屬性 是否已更改。
const [ user , setUser ] = useState ({name: "Jane"}) // 在第一次渲染后運行,并在用戶更改時重新運行useEffect (() => {
console.log('run effect') // 在這里執(zhí)行效果}, [ user ]) ... // 在某處按下按鈕onPress = () => {
setUser({名稱:“簡”})}
盡管 名稱 保持相同的值“Jane”,但這將觸發(fā)重新渲染并重新運行效果函數(shù)。
建議 #5:了解 useCallback 和 useMemo
如果將函數(shù)作為回調傳遞給子組件,則應使用useCallback記住該函數(shù)。
如果您的數(shù)據計算成本很高,您應該使用useMemo將其記憶。
示例:生成并顯示一個隨機數(shù)。
沒有 useCallback :
每當您單擊 RandomButton時 ,它都會調用 setNumber ,這會觸發(fā)Container 上的重新渲染 (請參閱日志)
Container
重新渲染時 ,它會啟動一個新函數(shù),然后將此函數(shù)傳遞給 as prop。randomize
RandomButton
RandomButton
收到一個新道具并決定重新渲染(見日志)。
常量容器= () => {
const [數(shù)字, setNumber ] = useState (0)
const randomize = () => setNumber (Math.random()) console.log("渲染容器")
返回 (
<>
{數(shù)字}
< RandomButton randomize ={ randomize }/> ) } const RandomButton = ({ randomize }) => { console.log("render RandomButton") returnrandomize}/> }
現(xiàn)在,使用 useCallback 和 React.Memo:
Container
重新渲染時 ,檢查其依賴項數(shù)組以確定是返回最后一個記憶函數(shù)還是創(chuàng)建一個新函數(shù)。useCallback
但在這種情況下,我們提供了一個空數(shù)組,這意味著 useCallback 將始終返回相同的 隨機化 函數(shù)。
當將 randomize 函數(shù)作為 prop 傳遞給 RandomButton時,在React.Memo的幫助下 ,它檢查并發(fā)現(xiàn) randomize prop 是相同的,因此無需重新渲染(見日志)。
常量容器= () => {
const [數(shù)字, setNumber ] = useState (0)
const randomize = useCallback (() => setNumber (Math.random()), [] )
console.log("渲染容器")
返回 (
<>
{數(shù)字}
< RandomButton randomize ={ randomize }/> ) } const RandomButton = React.memo (({ randomize }) => { console.log("render RandomButton") returnrandomize}/> })
在這種特殊情況下,我們將一個空數(shù)組傳遞給 useCallback ,因為它的記憶函數(shù) () => setNumber(Math.random()) 不需要任何額外信息即可執(zhí)行。
但是如果我們想在隨機化函數(shù)中使用 數(shù)字 狀態(tài)怎么辦 ,就像這樣:
() => setNumber(數(shù)字+ Math.random())
在這種情況下, 隨機化 取決于 數(shù)字 狀態(tài)才能正確執(zhí)行。所以我們 在useCallback中將number指定 為依賴項 :
const隨機化= useCallback (
() => setNumber ( number + Math.random()), [number] )
但是,這仍然可能不是一個完美的解決方案,因為當 randomize 調用 setNumber時,它會用 一個新值 更新number ,然后useCallback 檢查它的依賴項是否已更改并創(chuàng)建一個新的 randomize函數(shù),導致 再次 不必要地重新渲染 RandomButton .
為了處理這個困境,我們可以使用功能更新. 這在使用先前狀態(tài)計算新狀態(tài)時特別有用。
一個完美的解決方案如下:
const隨機化= useCallback (
() => setNumber ( number + Math.random()), [number] )