React Hook

react 16.8 以后加上了 react hook童本,它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性捏题。16.8之前卸伞,react組件可以分為類組件和函數(shù)組件撬腾。

  • 函數(shù)組件一定是無狀態(tài)組件,展示型組件(渲染組件)一般是無狀態(tài)組件;
  • 類組件既可以是有狀態(tài)組件脾拆,又可以是無狀態(tài)組件夜惭。類組件也可以叫容器組件,一般有交互邏輯和業(yè)務(wù)邏輯啸胧,而容器型組件一般是有狀態(tài)組件赶站。

我們?yōu)槭裁匆獡肀eact hook幔虏?由于類組件存在以下幾點(diǎn)問題:

  • 組件變得復(fù)雜和難以維護(hù),業(yè)務(wù)變得復(fù)雜之后贝椿,組件之間共享狀態(tài)變得頻繁想括,此時組件將變得非常難以理解和維護(hù),復(fù)用狀態(tài)邏輯更是難上加難烙博。
  • 滿天class導(dǎo)致的熱重載和性能問題瑟蜈,class自生具有的復(fù)雜度和組件嵌套過深props層級傳遞。
  • 函數(shù)式組件沒有狀態(tài)

下面逐一介紹官方提供的hook API渣窜。
1.useState()
作用:返回一個狀態(tài)以及能修改這個狀態(tài)的setter铺根,在其他語言稱為元組(tuple),一旦mount之后只能通過這個setter修改這個狀態(tài)乔宿。


useState函數(shù)申明

2.useEffect(callback, arr)
作用:處理函數(shù)組件中的副作用位迂,如異步操作、延遲操作等详瑞。useEffect有兩個參數(shù)掂林,callback和數(shù)組依賴項,無arr時相當(dāng)于componentDidMount生命周期坝橡,有arr時相當(dāng)componentDidMount和componentDidUpdata生命周期泻帮。如果callback中有return,則相當(dāng)于componentWillUnmount驳庭。

3.useContext
作用:跨組件共享數(shù)據(jù)鉤子刑顺,使用可分為三步:

  • 首先使用React.createContext API創(chuàng)建Context,由于支持在組件外部調(diào)用饲常,因此可以實現(xiàn)狀態(tài)共享
export const MyContext = React.createContext(null);
  • 使用Context.Provider API在上層組件掛載狀態(tài)
<MyContext.Provider value={value}>
  <childComponent />
</MyContext.Provider>
  • 獲取上層組件中距離當(dāng)前組件最近的<MyContext.Provider> 的 value
const value = useContext(MyContext)   // MyContext 為 context 對象(React.createContext 的返回值) 

useContext和傳統(tǒng)的props傳參分別適用于那些場景
useContext的應(yīng)用場景:
1.全局狀態(tài)的定義蹲堂,即可以被不同層級的組件所需要。
2.多個組件之間傳參(他們之間可能是跨多層級即祖孫關(guān)系傳參)時贝淤。
傳統(tǒng)props的應(yīng)用場景:
如果普通的父子組件之間傳參柒竞,即父子組只有單純的一層時,用props傳參更省事

4.useReducer

語法:const [state, dispatch] = useReducer(reducer, initialArg, init);

作用:用于管理復(fù)雜的數(shù)據(jù)結(jié)構(gòu)(useState一般用于管理扁平結(jié)構(gòu)的狀態(tài))播聪,基本實現(xiàn)了redux的核心功能朽基。useState的替代方案。它接收一個形如 (state, action) => newState 的 reducer离陶,并返回當(dāng)前的 state 以及與其配套的 dispatch 方法稼虎。

const initialState = {count: 0};
const reducer = (state, action)=> {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      return state;
  }
}
const [state, dispatch] = useReducer(reducer, initialState);
return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );

5.useMemo、useCallback
這倆個Api與性能優(yōu)化有關(guān)招刨。react中霎俩,性能的優(yōu)化點(diǎn)在于:

  • 調(diào)用setState,就會觸發(fā)組件的重新渲染,無論前后的state是否不同
  • 父組件更新打却,子組件也會自動的更新

基于上面的兩點(diǎn)杉适,我們通常的解決方案是:使用immutable進(jìn)行比較,在不相等的時候調(diào)用setState柳击;在shouldComponentUpdate中判斷前后的props和state猿推,如果沒有變化,則返回false來阻止更新捌肴。
在hooks出來之后蹬叭,我們能夠使用function的形式來創(chuàng)建包含內(nèi)部state的組件。但是哭靖,使用function的形式具垫,失去了上面的shouldComponentUpdate,我們無法通過判斷前后狀態(tài)來決定是否更新试幽。而且筝蚕,在函數(shù)組件中,react不再區(qū)分mount和update兩個狀態(tài)铺坞,這意味著函數(shù)組件的每一次調(diào)用都會執(zhí)行其內(nèi)部的所有邏輯起宽,那么會帶來較大的性能損耗。因此useMemo 和useCallback就是解決性能問題的殺手锏济榨。

useCallback和useMemo的參數(shù)跟useEffect一致坯沪。useMemo和useCallback都會在組件第一次渲染的時候執(zhí)行,之后會在其依賴的變量發(fā)生改變時再次執(zhí)行擒滑;并且這兩個hooks都返回緩存的值腐晾,useMemo返回緩存的變量,useCallback返回緩存的函數(shù)丐一。

語法:
useMemo:const A = useCallback(fnB, [a]) 調(diào)用fnB函數(shù)并返回其結(jié)果
useCallback:const fnA = useCallback(fnB, [a]) 返回fnB函數(shù)

通過一個例子來看useMemo的作用:

const [count, setCount] = useState(1);
const [val, setValue] = useState('');
const expensive = () => {
        console.log('compute');
        let sum = 0;
        for (let i = 0; i < count * 100; i++) {
            sum += i;
        }
        return sum;
}
return <>
        <h4>{count}-{val}-{expensive()}</h4>
        <div>
            <button onClick={() => setCount(count + 1)}>+c1</button>
            <input value={val} onChange={event => setValue(event.target.value)}/>
        </div>
</>;

不使用useMemo藻糖,無論count還是val改變都會執(zhí)行expensive()

const [count, setCount] = useState(1);
const [val, setValue] = useState('');
const expensive = useMemo(() => {
        console.log('compute');
        let sum = 0;
        for (let i = 0; i < count * 100; i++) {
            sum += i;
        }
        return sum;
}, [count])
return <>
        <h4>{count}-{val}-{expensive()}</h4>
        <div>
            <button onClick={() => setCount(count + 1)}>+c1</button>
            <input value={val} onChange={event => setValue(event.target.value)}/>
        </div>
</>;

使用useMemo,只有在count發(fā)生改變使才會會執(zhí)行expensive()

useCallback跟useMemo比較類似库车,但是使用場景不同:比如有一個父組件巨柒,其中包含子組件,子組件接收一個函數(shù)作為props柠衍;通常而言洋满,如果父組件更新了,子組件也會執(zhí)行更新珍坊;但是大多數(shù)場景下牺勾,更新是沒有必要的,我們可以借助useCallback來返回函數(shù)阵漏,然后把這個函數(shù)作為props傳遞給子組件禽最;這樣腺怯,子組件就能避免不必要的更新袱饭。

const [count, setCount] = useState(1);
const [val, setVal] = useState('');

const callback = useCallback(() => {
    console.log('callback執(zhí)行');
    return count;
}, [count]);

return <>
    <h4>{count}</h4>
    <Child callback={callback} />
    <div>
        <button onClick={() => setCount(count + 1)}>+</button>
        <input value={val} onChange={event => setVal(event.target.value)} />
    </div>
</>

const Child = (props: any) => {
    const [count, setCount] = useState(() => props.callback());

    useEffect(() => {
        setCount(props.callback());
    }, [props.callback]);

    return <div>{count}</div>;
};

6.useRef

語法:const refContainer = useRef(initialValue);

useRef是一個方法川无,返回一個可變的 ref 對象;其 .current 屬性被初始化為傳入的參數(shù)(initialValue)虑乖;可以保存任何類型的值:dom懦趋、對象等任何可變值;返回的 ref 對象在組件的整個生命周期內(nèi)保持不變疹味;修改 ref 的值是不會引發(fā)組件的重新 render 仅叫。

useRef非常常用的一個操作,訪問DOM節(jié)點(diǎn)糙捺,對DOM進(jìn)行操作诫咱,監(jiān)聽事件等等,如下:

const inputEl = useRef(null);
const onButtonClick = () => {
    // `current` 指向已掛載到 DOM 上的文本輸入元素
    inputEl.current.focus();
};
return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
);

除了傳統(tǒng)的用法之外,它還可以“跨渲染周期”保存數(shù)據(jù)洪灯。

const likeRef = useRef(1);
const [like, setLike] = useState(1);
const onButtonClick = () => {
    setTimeout(() => {
        console.log(likeRef.current); //11
        console.log(like); // 1
    }, 2000);
};
return (
  <>
    <button
        onClick={() => {
           likeRef.current++;
           setLike(like + 1);
    }}>
    {like}
    </button>
    <button onClick={onButtonClick}>打印like</button>
  </>
)

在上面的例子中坎缭,點(diǎn)擊了打印like按鈕后,連續(xù)點(diǎn)擊數(shù)字按鈕签钩,會發(fā)現(xiàn)2s后likeRef.current打印出11掏呼,而like打印出1。

因為铅檩,在任意一次渲染中憎夷,props和 state 是始終保持不變的,如果props和state在任意不同渲染中是相互獨(dú)立的話昧旨,那么使用到他們的任何值也是獨(dú)立的拾给。所以onButtonClick時拿到的時未點(diǎn)擊數(shù)字按鈕時的like值。
而ref 在所有 render 都保持著唯一的引用兔沃,因此所有的對 ref 的賦值或者取值拿到的都是一個最終的狀態(tài)蒋得,而不會存在隔離。

7.useImperativeHandle

語法:useImperativeHandle(ref, createHandle, [deps])

當(dāng)userRef用于一個組件時粘拾,useImperativeHandle 可以讓你在使用 ref 時自定義暴露給父組件的實例值窄锅;如果不使用,父組件的ref(chidlRef)訪問不到任何值(childRef.current==null)缰雇;且useImperativeHandle 應(yīng)當(dāng)與 forwardRef 一起使用入偷。

const RefDemo= ()=>{
    const childRef = useRef<any>(null);
    const onButtonClick = () => {
        childRef.current?.say();
    };
    return (
    <div>
        <Child ref={childRef} />
        <button onClick={onButtonClick}>say123</button>
    </div>
    )
};
export default RefDemo;
const Child = React.forwardRef((props, ref) => {
    useImperativeHandle(ref, () => ({
        say: () => {
            console.log('123');
        },
    }));
    return (
        <>
            <h4>子組件</h4>
        </>
    );
});
  • React.forwardRef會創(chuàng)建一個React組件,這個組件能夠?qū)⑵浣邮艿膔ef屬性轉(zhuǎn)發(fā)到其組件樹下的另一個組件中械哟。
  • React.forwardRef接受渲染函數(shù)作為參數(shù)疏之,React將使用prop和ref作為參數(shù)來調(diào)用此函數(shù)。

8.useLayoutEffect
用法與useEffect 相同暇咆,但它會在所有的 DOM 變更之后同步調(diào)用(立即執(zhí)行)锋爪”铮可以使用它來讀取 DOM 布局并同步觸發(fā)重渲染。在瀏覽器執(zhí)行繪制之前其骄,useLayoutEffect 內(nèi)部的更新計劃將被同步刷新亏镰。由于會在瀏覽器進(jìn)行任何繪制之前運(yùn)行完成,阻塞了瀏覽器的繪制拯爽。
使用場景:當(dāng)useEffect里面的操作需要處理DOM,并且會改變頁面的樣式,就需要用這個,否則可能會出現(xiàn)閃屏問題索抓。

9.useDebugValue
useDebugValue 用于在 React 開發(fā)者工具中顯示 自定義 Hook 的標(biāo)簽。
useDebugValue 接受一個格式化函數(shù)作為可選的第二個參數(shù)毯炮。該函數(shù)只有在 Hook 被檢查時才會被調(diào)用逼肯。它接受 debug 值作為參數(shù),并且會返回一個格式化的顯示值桃煎。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篮幢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子为迈,更是在濱河造成了極大的恐慌三椿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曲尸,死亡現(xiàn)場離奇詭異赋续,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)另患,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門纽乱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人昆箕,你說我怎么就攤上這事鸦列。” “怎么了鹏倘?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵薯嗤,是天一觀的道長。 經(jīng)常有香客問我纤泵,道長骆姐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任捏题,我火速辦了婚禮玻褪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘公荧。我一直安慰自己带射,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布循狰。 她就那樣靜靜地躺著窟社,像睡著了一般券勺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灿里,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天关炼,我揣著相機(jī)與錄音,去河邊找鬼钠四。 笑死盗扒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缀去。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼甸祭,長吁一口氣:“原來是場噩夢啊……” “哼缕碎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起池户,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤咏雌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后校焦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赊抖,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年寨典,在試婚紗的時候發(fā)現(xiàn)自己被綠了氛雪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸成,死狀恐怖报亩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情井氢,我是刑警寧澤弦追,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站花竞,受9級特大地震影響劲件,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜约急,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一零远、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烤宙,春花似錦遍烦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽供填。三九已至,卻和暖如春罢猪,著一層夾襖步出監(jiān)牢的瞬間近她,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工膳帕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粘捎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓危彩,卻偏偏與公主長得像攒磨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子汤徽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • React Hook是React函數(shù)式組件娩缰,它不僅僅有函數(shù)組件的特性,還帶有React框架的特性谒府。所以拼坎,官網(wǎng)文檔多...
    娜姐聊前端閱讀 445評論 3 1
  • 一、組件類 React的核心是組件, 在v16.8之前,組件的標(biāo)準(zhǔn)寫法是類(class)完疫。 以下為一個簡單的組件類...
    郭_小青閱讀 711評論 1 5
  • Hook 是 react 16.8 推出的新特性泰鸡,具有如下優(yōu)點(diǎn):Hook 使你在無需修改組件結(jié)構(gòu)的情況下復(fù)用狀態(tài)邏...
    林木木road閱讀 791評論 0 1
  • github的MD[https://github.com/liusanhong/study/blob/master...
    半個木頭人閱讀 276評論 0 0
  • 前言 自react16.8發(fā)布了正式版hook用法以來,我們公司組件的寫法逐漸由class向函數(shù)式組件+hook的...
    大春春閱讀 3,967評論 3 7