在?React?項目中全量使用?Hooks的方法
前言
此篇文章整理了在 React 項目開發(fā)中常用的一些 Hooks
React Hooks
Hooks 只能用于函數(shù)組件當(dāng)中
useState
import { useState } from 'react';
const Component = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>click</button>
)
}
此方法會返回兩個值:當(dāng)期狀態(tài)和更新狀態(tài)的函數(shù)。效果同 this.state 與 this.setState,區(qū)別是 useState 傳入的值并不一定要對象,并且在更新的時候不會把當(dāng)前的 state 與舊的 state 合并。
useReducer
useReducer 接收兩個參數(shù),第一個是 reducer 函數(shù),通過該函數(shù)可以更新 state,第二個參數(shù)為 state 的初始值,是 useReducer 返回的數(shù)組的第一個值,也是在 reducer 函數(shù)第一次被調(diào)用時傳入的一個參數(shù)。
基礎(chǔ)用法
import { useState } from 'react';
const Component = () => {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>click</button>
)
}
在基礎(chǔ)用法中,返回一個 dispatch 通過 dispatch 觸發(fā)不同的 action 來加減 state。這里既然能傳string action 那么肯定也能傳遞更復(fù)雜的參數(shù)來面對更復(fù)雜的場景。
進(jìn)階用法
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 中,我們將函數(shù)的參數(shù)改為一個對象,分別有type和 payload 兩個參數(shù),type 用來決定更新什么數(shù)據(jù),payload 則是更新的數(shù)據(jù)。寫過 react-redux 的同學(xué)可能發(fā)這個 reducer 與 react-redux 中的 reducer 很像,我們借助 react-redux 的思想可以實現(xiàn)一個對象部分更改的 reducer ,那么我們便可以使用 React Hooks 的 useContext 來實現(xiàn)一個狀態(tài)管理。
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 第二個參數(shù)數(shù)組內(nèi)的值發(fā)生了變化,那么useEffect第一個參數(shù)的回調(diào)將會被再執(zhí)行一遍,這里要注意的useEffect 的返回值函數(shù)并不只是再組件卸載的時候執(zhí)行,而是在這個 useEffect 被更新的時候也會調(diào)用,例如上述 count 發(fā)生變化后,useEffect 返回的方法也會被執(zhí)行,具體原因見Using the Effect Hook – React (reactjs.org)
useLayoutEffect
useLayoutEffect 與 useEffect 的API相同,區(qū)別:useEffect 在瀏覽器渲染后執(zhí)行,useLayoutEffect 在瀏覽器渲染之前執(zhí)行,由于JS是單線程,所以 useLayoutEffect 還會阻塞瀏覽器的渲染。區(qū)別就是這,那么應(yīng)用場景肯定是從區(qū)別中得到的,useLayoutEffect 在渲染前執(zhí)行,也就是說我們?nèi)绻袪顟B(tài)變了需要依據(jù)該狀態(tài)來操作DOM,為了避免狀態(tài)變化導(dǎo)致組件渲染,然后更新 DOM 后又渲染,給用戶肉眼能看到的閃爍,我們可以在這種情況下使用 useLayoutEffect。
當(dāng)然這個不只是狀態(tài)的改變,在任何導(dǎo)致組件重新渲染,而且又要改變
DOM的情況下都是useLayoutEffect的使用場景。當(dāng)然這種場景不多,useLayoutEffect也不能多用,且使用時同步操作時長不能過長,不然會給用戶帶來明顯的卡頓。
useRef
細(xì)心的同學(xué)有可能發(fā)現(xiàn)我在上面寫 useEffect 中有一個 timer 變量,我將其定義在了函數(shù)組件外面,這樣寫簡單使用是沒問題的,但是如果該組件在同一頁面有多個實例,那么組件外部的這個變量將會成共用的,會帶來一個沖突,所以我們需要一個能在函數(shù)組件聲明周期內(nèi)部的變量,可以使用 useState 中的 state 但是 state 發(fā)生變化組件也會隨之刷新,在有些情況是不需要刷新的,只是想單純的存一個值,例如計時器的 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 只接受一個參數(shù),就是 初始值,之后可以通過賦值 ref.current 來更改,我們可以將一些不影響組件聲明周期的參數(shù)放在 ref 中,還可以將 ref 直接傳遞給子組件 子元素。
const ref = useRef();
<div ref={ref}>Hello</div>
// or
<Child ref={ref} />
或許有同學(xué)這時候會想到,當(dāng)子組件為 Class 組件時,ref 獲取的是 Class 組件的實例,上面包含 Class 的所有方法屬性等。但當(dāng)子組件為 Function 組件時,ref 能拿到什么,總不可能是 function 內(nèi)定義的方法、變量。
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, // 內(nèi)部變量
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 會在二個參數(shù)的依賴項發(fā)生改變后才重新更新,如果將此函數(shù)傳遞到子組件時,每次父組件渲染此函數(shù)更新,就會導(dǎo)致子組件也重新渲染,可以通過傳遞第二個參數(shù)以避免一些非必要性的渲染。
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 一樣,區(qū)別就是一個返回的是緩存的方法,一個返回的是緩存的值。上述如果依賴值 count 不發(fā)生變化,計算 sum 的邏輯也就只會執(zhí)行一次,從而性能。
React Redux Hooks useSelector
import { shallowEqual, useSelector } from 'react-redux';
const Component = () => {
const userInfo = useSelector(state => state.userInfo, shallowEqual);
// ...
}
useSelector 的第二個參數(shù)是一個比較函數(shù),useSelector 中默認(rèn)使用的是 === 來判斷兩次計算的結(jié)果是否相同,如果我們返回的是一個對象,那么在 useSelector 中每次調(diào)用都會返回一個新對象,所以所以為了減少一些沒必要的 re-render,我們可以使用一些比較函數(shù),如 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 來調(diào)度操作,加上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一發(fā)生變化,將返回新的 location ,一般可以用來監(jiān)聽 location.search
useParams
import { useParams, useEffect } from 'react-router';
const Component = () => {
const params = useParams();
const getUserInfo = id => { // request api
// some event
};
useEffect(() => {
// parms 的 uid 發(fā)生變化就會重新請求用戶信息
getUserInfo(params.uid);
}, [params.uid]);
// ...
}
useParams 返回 react-router 的參數(shù)鍵值對
useRouteMatch
import { useRouteMatch } from 'react-router';
const Component = () => {
const match = useRouteMatch('/login');
// ...
}
useRouteMatch 可以傳入一個參數(shù) path,不傳參數(shù)則返回當(dāng)前路由的參數(shù)信息,如果傳了參數(shù)則用來判斷當(dāng)前路由是否能匹配上傳遞的 path,適用于判斷一些全局性組件在不同路由下差異化的展示。
參考
結(jié)語
使用 Hooks 能為開發(fā)提升不少效率,但并不代表就要拋棄 Class Component,依舊還有很多場景我們還得用到它,比如需要封裝一個公共的可繼承的組件,當(dāng)然通過自定義 hooks 也能將一些共用的邏輯進(jìn)行封裝,以便再多個組件內(nèi)共用。
下期更新
在React 中自定義 Hooks 的應(yīng)用場景,主要講一些 Hooks 的高階應(yīng)用
到此這篇關(guān)于在 React 項目中全量使用 Hooks的文章就介紹到這了,更多相關(guān)React使用 Hooks內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-native-video實現(xiàn)視頻全屏播放的方法
這篇文章主要介紹了react-native-video實現(xiàn)視頻全屏播放的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
create-react-app構(gòu)建項目慢的解決方法
這篇文章主要介紹了create-react-app構(gòu)建項目慢的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
使用react render props實現(xiàn)倒計時的示例代碼
這篇文章主要介紹了使用react render props實現(xiàn)倒計時的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
淺談React的React.FC與React.Component的使用
本文主要介紹了React的React.FC與React.Component的使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
React 使用recharts實現(xiàn)散點地圖的示例代碼
這篇文章主要介紹了React 使用recharts實現(xiàn)散點地圖的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12

