react hooks初體驗

ZLinhui發表於2019-02-16

什麼是Hooks?
Hooks是react即將推出的功能,它允許您在不編寫類的情況下使用狀態和其他React功能。
我的理解就是可以用寫無狀態元件的方式去編寫擁有狀態的元件。
遺憾的是,正式版16.7.0出了之後並沒有hooks,如果需要體驗還需下載next版本,目前是16.7.0-alpha.2

npm i react@next
這次與大家分享四個Hooks,個人覺得這幾個應該是之後工作中會經常使用到的。

 1. useState
 2. useEffect
 3. useReducer
 4. useMemo

1.useState
個人感覺這個鉤子是重點,使用它即可做到用函式的編寫帶有狀態的元件。

import React,{ useState,useEffect } from `react`
const HookTest = () => {
    const [obj,setValue] = useState({key:`count`,value:0});
    const handleChange = () => {
        const value = obj.value+1;
        //改變狀態
        setValue(Object.assign(obj,{value}));
    }
    return (
        <div>
             {obj.key}:{obj.value}
             <p>
                <button onClick={handleChange}>累加</button>
             </p>
        </div>
    )
}

很明顯,重點在於const [obj,setValue] = useState({key:`count`,value:0})這一句,useState是個函式,接收一個狀預設值,返回一個陣列,第一個元素為狀態,初始值為傳入函式的預設值,第二個元素為方法,可使用此方法改變狀態的值。

2.useEffect
這個鉤子,官方所說是componentDidMount,componentDidUpdate和componentWillUnmount這三個生命週期的結合,因為元件掛載完成時會執行,更新時會執行,解除安裝時會執行,接上面的HookTest元件,往裡新增

useEffect(()=>{
    console.log(`obj->`,obj);
    return ()=>{
        console.log(`解除安裝時..`);
    }
});

這就是一個基本用法,掛載、更新、解除安裝都會列印obj物件,return的函式,作為元件更新或者解除安裝時執行,比如在使用setinterval,可以在return的函式裡寫clearinterval。
如果只想讓它執行一次的話,可以往函式裡新增第二個引數。

useEffect(()=>{
    console.log(`obj->`,obj);
},false);

這樣只在掛載完成時執行一次,第二個引數可以為false、[]、{}、””
如果想讓他有條件的執行,可以往第二個引數傳入具體的引數

useEffect(()=>{
    console.log(`obj->`,obj);
},{obj.value});

如果obj.value值變化時,就執行,沒變化時就不執行,對於效能優化非常友好。

3.useReducer

如果使用過redux的童鞋們不會預設,將需要的狀態儲存到一個物件中,可供所有的元件使用。
先上程式碼

import React, { useReducer,useMemo,useEffect,useState } from `react`;
//建立reducer,reducer可在外部建立然後再引入
function reducer(state = { count: 0 }, action) {
    switch (action.type) {
        case `reset`:
            return { count: 0 };
        case `increment`:
            return { count: state.count + 1 };
        case `decrement`:
            return { count: state.count <= 0 ? 0 : state.count - 1 };
        default:
            return state;
    }
}
//元件
const useReducerDemo = () => {
    const [state, dispatch] = useReducer(reducer, { count: 0 }, { type: `increment` });
    //非同步增加
    const asyncIncrement = () => {
        setTimeout(()=>{
            dispatch({ type: `increment` })
        },2000);
    }
    return (
        <div>
            <p>
                <span>Count: {state.count}</span>
                <button onClick={() => dispatch({ type: `reset` })}>還原</button>
                <button onClick={() => dispatch({ type: `increment` })}>+</button>
                <button onClick={() => dispatch({ type: `decrement` })}>-</button>
                <button onClick={asyncIncrement}>async+</button>
            </p>
        </div>
    )
}

可以看到,和useState很像,也是使用一個陣列解構接受返回的值。
先說返回的值:
1.state
自然為reducer的狀態
2.dispatch
這個是一個函式,有dispatch就意味著我們可以不用像使用redux時還需要自己下中介軟體(如redux-thunk)就可以進行非同步操作,具體看asyncIncrement函式,引數為一個物件,指定需要執行的action
再說useReducer函式的引數:
第一個引數為你引入的reducer,第二個引數為state的預設值,第三個引數為初始觸發的action,就是載入時預設就執行一個action

4.useMemo
useMemo只有當其中一個輸入發生變化時,才會重新計算記憶值。此優化有助於避免在每個渲染上進行昂貴的計算。
此鉤子也是有助於效能優化,接入上面的useReducerDemo元件,往裡新增

const [tips,setTips] = useState(false);
//當為0時提示不能再減了
useEffect(()=>{
    if(!state.count){
        setTips(true);
    }else{
        setTips(false);
    }
});
const memoizedValue = useMemo(() => {
    console.log(`useMemo run`);
    return tips
}, [tips]);

在return元件元素div裡新增

{
    memoizedValue && <p>不能為負數哦</p>
}

以上新增的程式碼時為了實現在reducer裡的count小於等於0或從0變更為其他數字時更新true或false,以此達到p元素的顯示與否,否則一直為上一次計算得到值,我們使用了console.log(`useMemo run`);記錄它更新的次數,當count從0一直+1,只會列印一次`useMemo run`,由此說明,只在0變成1的時候執行了一次,往後memoizedValue的值一直為0變成1時所return的值。
此例子並說明不了什麼,不過當有很龐大計算量的時候就能體現出useMemo的作用了。
useMemo同樣也是一個函式,接受兩個引數,第一個引數為函式,第二個引數為要比對的值,返回一個值。
第二個引數裡可以傳入多個值,如[a,b,c,…],當傳入的這些值有變化時,就會去執行第一個傳入的函式,根據業務需求計算後返回最終結果。
同理,第二個引數傳入的值沒有更新時,不會執行。

結尾
花了一下午的時間體驗hook,其他的鉤子也使用了個遍,感覺這四個在我看來和在我公司業務裡可能會大量的使用到,所以發此文章分享,也為記錄,本人新手前端一枚,第一次寫文章,有說的不對的地方還請請多多指教。
謝謝大家的閱讀。
以上程式碼的github地址為react-hooks初體驗