千呼萬喚始出來,React Hooks終於在React 16.8版本中發佈穩定版了。最近逛github發現了一個頗有意思的庫:react-hanger。html
若是對Hooks還不怎麼了解的同窗,建議去看一下官方文檔:Introducing Hooks.react
咱們都知道,在Hooks以前,開發react組件主要是class組件和function組件。function組件沒有state,因此也叫SFC(stateless functional component),簡單的將props映射成view;class組件有state,可以處理更加複雜的邏輯。可是基於class的組建並非完美的,總結起來就像Dan說的那樣,有三個主要的問題:git
爲了解決上面的這三個問題,react hooks提案登場了,它有如下幾個特色:github
Hooks 容許你在不編寫 class 的狀況下使用狀態(state)和其餘 React 特性。 你還能夠構建本身的 Hooks, 跨組件共享可重用的有狀態邏輯。數組
如今React中內置的Hooks有:緩存
固然了,授之以魚不如授之以漁,React官方也提供了教你如何封裝本身Hook的文檔Building Your Own Hooks,有興趣的小夥伴能夠去閱讀一下。bash
大體的看了下react-hanger的源碼以後發現,這個庫實際上是對React Hooks API的適用性封裝。暴露一些更經常使用的Hooks節省你們造輪子的工做量。babel
React的核心開發者Dan看到這個庫也作了評價:less
一個對Hooks的隱喻。你能夠將你的state「掛起」在你的function component上,等你回來的時候,它就掛在那。函數
本文寫做時,react-hanger的Usage裏提供了6個API,從名字裏就能夠看出這些Hook都是作什麼的(Hooks都以"use"開頭,這是一種約定),
import {
useInput,
useBoolean,
useNumber,
useArray,
useOnMount,
useOnUnmount
} from "react-hanger";
複製代碼
使用起來也很簡單,好比useNumber
const App = () => {
const showCounter = useBoolean(true);
const counter = useNumber(0);
return (
<div>
<button onClick={counter.increase}> increase </button>
{showCounter.value && <span> {counter.value} </span>}
<button onClick={counter.decrease}> decrease </button>
</div>
);
};
複製代碼
初步印象:大體與原始的basic hooks有點不一樣的是,useState返回一個數組,分別是值
與操做
,而react-hanger提供的API貌似是將值
和一些操做
封裝到一個對象中,好比counter
就是一個{value: count, increase: setCount(count + 1), decrease: setCount(count - 1) }
的對象。
還有更多的操做方法能夠看react-hanger的sandbox:https://codesandbox.io/s/44m70xm70
其實翻看了react-hanger的源碼以後會發現,react-hanger一共引用了四個React內置的Hook,
import { useCallback, useEffect, useRef, useState } from "react";
複製代碼
而後返回一些「輪子」hooks,包括useNumber
、useArray
、useBoolean
等等。
這些輪子能夠大體分爲兩類:封裝Hook和拆分Hook。
好比useStateful
、useNumber
、useArray
、useBoolean
都是對內置HookuseState
的封裝。
export const useStateful = initial => {
const [value, setValue] = useState(initial);
return {
value,
setValue
};
};
複製代碼
利用ES6的解構賦值,將useState
返回的數組封裝成一個對象從新返回,方便調用。
export const useNumber = (
initial,
{ upperLimit, lowerLimit, loop, step = 1 } = {}
) => {
const [value, setValue] = useState(initial);
return {
value,
setValue,
increase: useCallback(i => {
setValue(...);
}, []),
decrease: useCallback(d => {
setValue(...);
}, [])
};
};
複製代碼
useNumber
接收一個initial number和一個配置項對象,在內部是經過對initial number進行useState Hook,返回一個對象,除了基本的value
和setValue
,還有兩個方法increase
和decrease
。這兩個方法都是用useCallback
對setValue
進行的進一步封裝。
而
useCallback
是一個比較重要的內置Hook,useCallback
的能夠於緩存了每次渲染時 inline callback 的實例,在第二個參數數組內的值發生更改時纔會更改。這樣能夠配合上子組件的
shouldComponentUpdate
或者useMemo
起到減小沒必要要的渲染的做用。
而第二個參數爲空數組的意思就是告訴React無論參數如何都要記憶。
至於useArray
、useBoolean
、 useInput
這三個hook能夠說和useNumber
大同小異,都是須要一個傳入的initial值,在hook內部經過useState
初始化,再返回一些經常使用的操做方法。
這裏的useInput
是針對於受控組件,因此不須要useRef
。
export const useSetState = initialValue => {
const { value, setValue } = useStateful(initialValue);
return {
setState: useCallback(v => {
return setValue(oldValue => ({
...oldValue,
...(typeof v === "function" ? v(oldValue) : v)
}));
}, []),
state: value
};
};
複製代碼
Unlike the
setState
method found in class components,useState
does not automatically merge update objects.與類組件中的setState方法不一樣,useState不會自動合併更新對象。
熟悉React Hook的同窗看了代碼就知道這個hook是封裝了什麼了,由於useState返回的相似於setCount
的方法不會自動合併更新對象。這個hook幫助你們能夠得到一個能夠merge以前value的Hook型setState
。
上述幾個算是封裝hook,那麼下面的幾個就能夠算是拆分hook,對useEffect
更精細化的處理。
衆所周知,useEffect
是被用來處理一些原先放在class組件中生命週期函數的反作用,好比componentDidMount
、componentDidUpdate
、componentWillUnmount
,集合而成的一個Hook。
理論上,在每次渲染後都會觸發useEffect
的效果,可是若是我只想在didmount裏或者只想在willunmount裏作一下事情,該怎麼辦?
這時就用到了useEffect
的一個特色:第二個參數爲效果依賴的值數組,也就是說只有當數組內的值變化纔會觸發useEffect
,
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
複製代碼
而若是第二個參數爲一個空數組的時候,則至關於告訴React你的效果不依賴於組件中的任何值,所以該效果只能在mount上運行並在unmount上清理,它不會在更新時運行。
export const useOnUnmount = onUnmount =>
useEffect(() => {
return () => onUnmount && onUnmount();
}, []);
export const useOnMount = onMount =>
useEffect(() => {
onMount && onMount();
}, []);
複製代碼
因此useOnMount
的實現就很是簡單,在useEffect
內執行onMount函數且第二個參數是[]
,useOnUnmount
的實現則是返回onUnmount函數且第二個參數是[]
。
export const useLifecycleHooks = ({ onMount, onUnmount }) =>
useEffect(() => {
onMount && onMount();
return () => onUnmount && onUnmount();
}, []);
複製代碼
useLifecycleHooks
則是對useOnUnmount
和useOnMount
的整合,在useEffect
的第二個參數爲[]
的狀況下,執行onMount和返回onUnmount。
export const useLogger = (name, props) => {
useLifecycleHooks({
onMount: () => console.log(`${name} has mounted`),
onUnmount: () => console.log(`${name} has unmounted`)
});
useEffect(() => {
console.log("Props updated", props);
});
};
複製代碼
useLogger
算是一個爲hook commponent封裝的log插件,經過在useLifecycleHooks
內傳入onMount和onUnmount打印日誌的函數,以後再經過原生的默認useEffect
不傳遞第二個參數來實如今更新過程當中打印日誌。
export const usePrevious = value => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
複製代碼
usePrevious則能夠獲取以前的props或者state,來自於React的官方文檔。
其實react-hanger的源代碼都很簡潔,對源碼感興趣的同窗請看:https://github.com/kitze/react-hanger/blob/master/src/index.js,更多的是這個庫的實現大部分都是用了React Hook的思想,但願你們能夠經過本文加深對React Hook的理解和認識。