最近開始學習React,記錄一下心得。
React hooks是16.8.0推出的,其目的是為了替換class,HOC,render props。那么本文將討論啥是hooks,HOC,render props,以及hooks究竟能不能替換掉HOC,render props。
Hooks替代HOC和render props。
首先是hooks(這里指custom hooks,下同),HOC,render props到底是做什么的?
對於新手,並不是很容易理解它們是做什么的,至少我沒有很容易理解。
我現在理解為它們是為了共享一些維護state的邏輯。
什么意思呢?首先它共享的是邏輯,但是共享邏輯你隨便封裝一個函數不就可以了么?問題就出在它是共享維護state的邏輯。普通的邏輯的和維護state的邏輯的區別就是,維護一個state的邏輯,當state發生變化時,用到這個state的組件是要重新渲染的,顯然用普通的函數做不了這個事情。
那么有哪些維護state的邏輯呢?我大體上把它們分為兩類:
第一類,一個狀態組件維護state的邏輯,第二類,一個非組件維護state的邏輯。
其實一個狀態組件本身就是在維護一個state,當用戶操作它所渲染的UI的時候,state發生響應的變化。組件的邏輯負責維護state的變化。
那么非組件指什么呢?
比如說我維護一個user的登錄狀態,這個登錄狀態需要發送請求給后台去拿。那么這個登錄狀態就會有3個值{fetching,online,offline},而且可能會從fetching到online,或者從fetching到offline,就是會變化的。
比如說我需要記錄用戶鼠標的位置(x,y坐標),這個是隨用戶移動鼠標而變化的。
比如說我做一個定時器,每隔一段時間切換一下我維護的一個state。
等等,我相信還有其他很多。
之所以分為這兩類,是因為我們不能用hooks來共享狀態組件。這是因為我們用hooks的時候是需要用它的返回值的,這個返回值通常是這個hooks所維護的state或者state的計算值,那么這個hooks就不可能返回一個dom元素了,也就是hooks不能包含組件。
然后我們建立一個簡單的模型分析下三者的結構。
我們假設有三個組件A,B,C需要共享一個維護state的邏輯S。那么:
hooks是什么呢?hooks就是把S封裝成useS,也就是一個custom hooks,然后在A,B,或者C里面使用useS
const useS = () => { const [state, setState] = useState('someValue') useEffect(() => { ...some code... setState('anotherValue') }) return state; } const A = () => { const s = useS(); return ( <div> <div>{s}</div> <div>A</div> </div> ) }
Render props是什么呢?它是把S封裝成一個component,同時這個component留了一個props用來接收需要渲染的組件。我認為下面這兩種寫法都是render props,因為他們的結構基本一樣,結果完全一樣。
第一種,就是官方文檔的寫法:
const S = ({render}) => { const [state, setState] = useState('someValue') useEffect(() => { ...some code... setState('anotherValue') }) return render(state) } const A = ({s}) => { return ( <div> <div>{s}</div> <div>A</div> </div> ) } const App = () => { return <S render = {(s) => <A s={s} />} /> }
第二種,也是可用的寫法。
const S = ({render}) => { const [state, setState] = useState('someValue') useEffect(() => { ...some code... setState('anotherValue') }) const Render = render; return <Render s={state} /> } const A = ({s}) => { return ( <div> <div>{s}</div> <div>A</div> </div> ) } const App = () => { return <S render = {A} /> }
可以看到render props相對於hooks有明顯的區別,它在被調用的時候需要知道兩個component。Render props的兩種寫法的區別就是第一種是調用的時候render設置為一個函數,返回值為component實例。第二種是render設置為component本身。
HOC是什么呢?HOC就是把S封裝成一個函數withS,這個函數返回一個新的component。
const withS =(WrappedComponent) => { return () => { const [state, setState] = useState('someValue') useEffect(() => { ...some code... setState('anotherValue') }) return <WrappedComponent s={state} /> } } const A = ({s}) => { return ( <div> <div>{s}</div> <div>A</div> </div> ) } const AWithS = withS(A); const App = () => { return <AWithS />; }
對比一下HOC和render props,你會發現其實HOC返回的component使用的技術和render props很相似哦!只不過render props的例子中需要render的component來自props,而HOC中需要render的component來自HOC的參數。
當然在react的官方文檔中你會發現另一種寫法,HOC的返回值調用了用render props技術實現的S。這種寫法往往是為了提供API給其他開發者,讓喜歡用HOC的去使用HOC,喜歡用render props的去使用render props。
const S = ({render}) => { const [state, setState] = useState('someValue') useEffect(() => { ...some code... setState('anotherValue') }) const Render = render; return <Render s={state} /> } const withS = (WrappedComponent) => { return () => { return <S render = {(s) => <WrappedComponent s={s} />} /> } } const A = ({s}) => { return ( <div> <div>{s}</div> <div>A</div> </div> ) } const AWithS = withS(A); const App = () => { return <AWithS />; } const AnotherApp = () => { return <S render = {(s) => <A s={s} />} /> }
HOC和hooks一樣,在調用的時候只需要知道一個component,上面例子中的AwithS,實際情況是我們命名AwithS為A,而真正的A不被暴露(export)出去,這是很好的行為。
HOC不光是有共享維護state的邏輯的能力,還可以做一切封裝能做的事情,比如說偷偷改下上層component傳過來的props的值。
Hooks能替代HOC,render props么?
hooks是無法實現共享狀態組件邏輯的,當然就無法在這方面代替HOC和render props。而共享非組件邏輯的功能,基本都可以用hooks來替代,而且最好用hooks來實現,因為我們看到了,hooks的邏輯最直接,最容易理解。當然第一原則仍然是根據業務實事求是,就是概念上該是什么就是什么,如果概念上就是HOC,那么就用HOC。實事求是是為了代碼的更好維護。