網站首頁 編程語言 正文
前言
此篇文章整理了在 React 項目開發中常用的一些 Hooks
React Hooks
Hooks 只能用于函數組件當中
useState
import { useState } from 'react'; const Component = () => { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}>click</button> ) }
此方法會返回兩個值:當期狀態和更新狀態的函數。效果同 this.state
與 this.setState
,區別是 useState
傳入的值并不一定要對象,并且在更新的時候不會把當前的 state 與舊的 state 合并。
useReducer
useReducer
接收兩個參數,第一個是 reducer 函數,通過該函數可以更新 state,第二個參數為 state 的初始值,是 useReducer
返回的數組的第一個值,也是在 reducer 函數第一次被調用時傳入的一個參數。
基礎用法
import { useState } from 'react'; const Component = () => { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}>click</button> ) }
在基礎用法中,返回一個 dispatch 通過 dispatch 觸發不同的 action 來加減 state。這里既然能傳string
action
那么肯定也能傳遞更復雜的參數來面對更復雜的場景。
進階用法
import { useReducer } from 'react'; const Component = () => { const [userInfo, dispatch] = useReducer( (state, { type, payload }) => { switch (type) { case 'setName': return { ...state, name: payload }; case 'setAge': return { ...state, age: payload }; } }, { name: 'Jace', age: 18 } ); return ( <button onClick={() => dispatch({ type: 'setName', payload: 'John' })}> click </button> ); };
useContext
在上述案例 useReducer
中,我們將函數的參數改為一個對象,分別有type
和 payload
兩個參數,type
用來決定更新什么數據,payload
則是更新的數據。寫過 react-redux 的同學可能發這個 reducer 與 react-redux 中的 reducer 很像,我們借助 react-redux 的思想可以實現一個對象部分更改的 reducer ,那么我們便可以使用 React Hooks 的 useContext
來實現一個狀態管理。
import { useMemo, createContext, useContext, useReducer } from 'react'; const store = createContext([]); const App = () => { const reducerValue = useReducer( (state, { type, payload }) => { switch (type) { case 'setName': return { ...state, name: payload }; case 'setAge': return { ...state, age: payload }; } }, { name: 'Jace', age: 18 } ); const [state, dispatch] = reducerValue; const storeValue = useMemo(() => reducerValue, reducerValue); return ( <store.Provider value={storeValue}> <Child /> </store.Provider> ); }; const Child = () => { const [state, dispatch] = useContext(store); // 在子組件中使用 console.log(state); return ( <button onClick={() => dispatch({ type: 'setName', payload: 'John' })}> click </button> ); }
useEffect
import { useState, useEffect } from 'react'; let timer = null; const Component = () => { const [count, setCount] = useState(0); // 類似于 class 組件的 componentDidMount 和 componentDidUpdate: useEffect(() => { document.title = `You clicked ${count} times`; timer = setInterval(() => { // events ... }, 1000) return () => { // 類似 componentWillUnmount // unmount events ... clearInterval(timer); // 組件卸載、useEffect 更新 移除計時器 }; }, [count]); // ... }
如果 useEffect
第二個參數數組內的值發生了變化,那么useEffect
第一個參數的回調將會被再執行一遍,這里要注意的useEffect 的返回值函數并不只是再組件卸載的時候執行,而是在這個 useEffect 被更新的時候也會調用,例如上述 count 發生變化后,useEffect 返回的方法也會被執行,具體原因見Using the Effect Hook – React (reactjs.org)
useLayoutEffect
useLayoutEffect
與 useEffect
的API相同,區別:useEffect
在瀏覽器渲染后執行,useLayoutEffect
在瀏覽器渲染之前執行,由于JS是單線程,所以 useLayoutEffect 還會阻塞瀏覽器的渲染。區別就是這,那么應用場景肯定是從區別中得到的,useLayoutEffect
在渲染前執行,也就是說我們如果有狀態變了需要依據該狀態來操作DOM
,為了避免狀態變化導致組件渲染,然后更新 DOM
后又渲染,給用戶肉眼能看到的閃爍,我們可以在這種情況下使用 useLayoutEffect
。
當然這個不只是狀態的改變,在任何導致組件重新渲染,而且又要改變
DOM
的情況下都是useLayoutEffect
的使用場景。當然這種場景不多,useLayoutEffect
也不能多用,且使用時同步操作時長不能過長,不然會給用戶帶來明顯的卡頓。
useRef
細心的同學有可能發現我在上面寫 useEffect
中有一個 timer
變量,我將其定義在了函數組件外面,這樣寫簡單使用是沒問題的,但是如果該組件在同一頁面有多個實例,那么組件外部的這個變量將會成共用的,會帶來一個沖突,所以我們需要一個能在函數組件聲明周期內部的變量,可以使用 useState
中的 state 但是 state 發生變化組件也會隨之刷新,在有些情況是不需要刷新的,只是想單純的存一個值,例如計時器的 timer
以及子組件的 Ref 實例等等。
import React, { useRef, useState, useEffect } from 'react'; const Compnent = () => { const timer = useRef(null); const [count, setCount] = useState(0); useEffect(() => { clearInterval(timer.current); timer.current = setTimeout(() => { setCount(count + 1); }, 1000); }, [count]); return <div>UseRef count: {count}</div>; }
useRef
只接受一個參數,就是 初始值,之后可以通過賦值 ref.current
來更改,我們可以將一些不影響組件聲明周期的參數放在 ref 中,還可以將 ref 直接傳遞給子組件 子元素。
const ref = useRef(); <div ref={ref}>Hello</div> // or <Child ref={ref} />
或許有同學這時候會想到,當子組件為 Class 組件時,ref 獲取的是 Class 組件的實例,上面包含 Class 的所有方法屬性等。但當子組件為 Function 組件時,ref 能拿到什么,總不可能是 function 內定義的方法、變量。
useImperativeHandle
import React, { useRef, useState, useImperativeHandle } from 'react'; const App = () => { const ref = useRef(); return ( <Child ref={ref} /> ); }; const Child = React.forwardRef((props, ref) => { const inputRef = useRef(); const [value, setValue] = useState(1); useImperativeHandle(ref, () => ({ value, // 內部變量 setValue, // 方法 input: inputRef.current // Ref })); return ( <input value={value} inputRef={inputRef} /> ); })
使用 useImperativeHandle
鉤子可以自定義將子組件中任何的變量,掛載到 ref 上。React.forwardRef
方法可以讓組件能接收到 ref ,然后再使用或者透傳到更下層。
useCallback
import React, { useCallback } from 'react'; const Component = () => { const setUserInfo = payload => {}; // request api const updateUserInfo = useCallback(payload => { setUserInfo(Object.assign({}, userInfo, payload)); }, [userInfo]); return ( <UserCard updateUserInfo={updateUserInfo}/> ) }
useCallback 會在二個參數的依賴項發生改變后才重新更新,如果將此函數傳遞到子組件時,每次父組件渲染此函數更新,就會導致子組件也重新渲染,可以通過傳遞第二個參數以避免一些非必要性的渲染。
useMemo
import React, { useMemo } from 'react'; const Component = () => { const [count, setCount] = useState(0); const sum = useMemo(() => { // 求和邏輯 return sum; }, [count]); return <div>{sum}</div> }
useMemo 的用法跟 useCallback 一樣,區別就是一個返回的是緩存的方法,一個返回的是緩存的值。上述如果依賴值 count 不發生變化,計算 sum 的邏輯也就只會執行一次,從而性能。
React Redux Hooks useSelector
import { shallowEqual, useSelector } from 'react-redux'; const Component = () => { const userInfo = useSelector(state => state.userInfo, shallowEqual); // ... }
useSelector 的第二個參數是一個比較函數,useSelector 中默認使用的是 ===
來判斷兩次計算的結果是否相同,如果我們返回的是一個對象,那么在 useSelector 中每次調用都會返回一個新對象,所以所以為了減少一些沒必要的 re-render
,我們可以使用一些比較函數,如 react-redux 自帶的 shallowEqual
,或者是 Lodash 的 _.isEqual()
、Immutable 的比較功能。
useDispatch
import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; const Compnent = () => { const dispatch = useDispatch(); const clearUserInfo = useCallback( () => dispatch({ type: 'clearUserInfo' }), [dispatch] ); return ( <button onClick={clearUserInfo}>click</buttn> ) }
使用 dispatch
來調度操作,加上useCallback
來減少不必要的渲染。
React Router Hooks
useHistory
import { useHistory } from 'react-router'; const Compnent = () => { const history = useHistory(); return ( <button onClick={() => history.push('/home')}>go home</buttn> ) }
useLocation
import React, { useEffect } from 'react'; import { useLocation } from 'react-router'; const Compnent = () => { const location = useLocation(); useEffect(() => { // ... }, [location]) }
URL一發生變化,將返回新的 location
,一般可以用來監聽 location.search
useParams
import { useParams, useEffect } from 'react-router'; const Component = () => { const params = useParams(); const getUserInfo = id => { // request api // some event }; useEffect(() => { // parms 的 uid 發生變化就會重新請求用戶信息 getUserInfo(params.uid); }, [params.uid]); // ... }
useParams 返回 react-router 的參數鍵值對
useRouteMatch
import { useRouteMatch } from 'react-router'; const Component = () => { const match = useRouteMatch('/login'); // ... }
useRouteMatch 可以傳入一個參數 path
,不傳參數則返回當前路由的參數信息,如果傳了參數則用來判斷當前路由是否能匹配上傳遞的 path
,適用于判斷一些全局性組件在不同路由下差異化的展示。
參考
React Hooks
React Redux Hooks
React Router Hooks
結語
使用 Hooks
能為開發提升不少效率,但并不代表就要拋棄 Class Component
,依舊還有很多場景我們還得用到它,比如需要封裝一個公共的可繼承的組件,當然通過自定義 hooks 也能將一些共用的邏輯進行封裝,以便再多個組件內共用。
下期更新
在React 中自定義 Hooks 的應用場景
,主要講一些 Hooks 的高階應用
原文鏈接:https://blog.csdn.net/qq_37215621/article/details/127235699
相關推薦
- 2022-10-25 基于Pytorch使用GPU運行模型方法及可能出現的問題解決方法
- 2021-11-22 Linux下Select多路復用實現簡易聊天室示例_C 語言
- 2023-10-25 更簡單的方法實現el-calendar日歷組件中點擊上個月、今天、下個月按鈕時的點擊事件
- 2022-09-06 C語言單鏈表遍歷與求和示例解讀_C 語言
- 2022-07-20 C語言詳細講解while語句的用法_C 語言
- 2022-10-26 C#實現接收QQ郵件的示例代碼_C#教程
- 2022-12-05 關于adfuller函數返回值的參數說明與記錄_python
- 2022-12-22 C++?Boost?Foreach超詳細分析講解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支