摘要:未來(lái)可能成為官方之一。討論地址是精讀組件如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。
1. 引言
為什么要了解 Function 寫法的組件呢?因?yàn)樗谧兊迷絹?lái)越重要。
那么 React 中 Function Component 與 Class Component 有何不同?
how-are-function-components-different-from-classes 這篇文章帶來(lái)了一個(gè)獨(dú)特的視角。
順帶一提,以后會(huì)用 Function Component 代替 Stateless Component 的說(shuō)法,原因是:自從 Hooks 出現(xiàn),函數(shù)式組件功能在不斷豐富,函數(shù)式組件不再需要強(qiáng)調(diào)其無(wú)狀態(tài)特性,因此叫 Function Component 更為恰當(dāng)。2. 概述
原文事先申明:并沒有對(duì) Function 與 Classes 進(jìn)行優(yōu)劣對(duì)比,而僅僅進(jìn)行特性對(duì)比,所以不接受任何吐槽。
這兩種寫法沒有好壞之分,性能差距也幾乎可以忽略,而且 React 會(huì)長(zhǎng)期支持這兩種寫法。Capture props
對(duì)比下面兩段代碼。
Class Component:
class ProfilePage extends React.Component { showMessage = () => { alert("Followed " + this.props.user); }; handleClick = () => { setTimeout(this.showMessage, 3000); }; render() { return ; } }
Function Component:
function ProfilePage(props) { const showMessage = () => { alert("Followed " + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ; }
(在線 Demo>))
這兩個(gè)組件都描述了同一個(gè)邏輯:點(diǎn)擊按鈕 3 秒后 alert 父級(jí)傳入的用戶名。
如下父級(jí)組件的調(diào)用方式:
那么當(dāng)點(diǎn)擊按鈕后的 3 秒內(nèi),父級(jí)修改了 this.state.user,彈出的用戶名是修改前的還是修改后的呢?
Class Component 展示的是修改后的值:
> ); }
(在線 Demo)
在點(diǎn)擊 Send 按鈕后,再次修改輸入框的值,3 秒后的輸出依然是 點(diǎn)擊前輸入框的值。這說(shuō)明 Hooks 同樣具有 capture value 的特性。
利用 useRef 可以規(guī)避 capture value 特性:
function MessageThread() { const latestMessage = useRef(""); const showMessage = () => { alert("You said: " + latestMessage.current); }; const handleSendClick = () => { setTimeout(showMessage, 3000); }; const handleMessageChange = e => { latestMessage.current = e.target.value; }; }
只要將賦值與取值的對(duì)象變成 useRef,而不是 useState,就可以躲過 capture value 特性,在 3 秒后得到最新的值。
這說(shuō)明了利用 Function Component + Hooks 可以實(shí)現(xiàn) Class Component 做不到的 capture props、capture value,而且 React 官方也推薦 新的代碼使用 Hooks 編寫。
3. 精讀原文 how-are-function-components-different-from-classes 從一個(gè)側(cè)面講述了 Function Component 與 Class Component 的不同點(diǎn),之所以將 Function Component 與 Class Component 相提并論,幾乎都要?dú)w功于 Hooks API 的出現(xiàn),有了 Hooks,F(xiàn)unction Component 的能力才得以向 Class Component 看齊。
關(guān)于 React Hooks,之前的兩篇精讀分別有過介紹:
精讀《React Hooks》
精讀《怎么用 React Hooks 造輪子》
但是,雖然 Hook 已經(jīng)發(fā)布了穩(wěn)定版本,但周邊生態(tài)跟進(jìn)還需要時(shí)間(比如 useRouter)、最佳實(shí)踐整理還需要時(shí)間,因此不建議重構(gòu)老代碼。
為了更好的使用 Function Component,建議時(shí)常與 Class Component 的功能做對(duì)比,方便理解和記憶。
下面整理一些常見的 Function Component 問題:
非常建議完整閱讀 React Hooks FAQ。怎么替代 shouldComponentUpdate
說(shuō)實(shí)話,F(xiàn)unction Component 替代 shouldComponentUpdate 的方案并沒有 Class Component 優(yōu)雅,代碼是這樣的:
const Button = React.memo(props => { // your component });
或者在父級(jí)就直接生成一個(gè)自帶 memo 的子元素:
function Parent({ a, b }) { // Only re-rendered if `a` changes: const child1 = useMemo(() =>, [a]); // Only re-rendered if `b` changes: const child2 = useMemo(() => , [b]); return ( <> {child1} {child2} > ); }
相比之下,Class Component 的寫法通常是:
class Button extends React.PureComponent {}
這樣就自帶了 shallowEqual 的 shouldComponentUpdate。
怎么替代 componentDidUpdate由于 useEffect 每次 Render 都會(huì)執(zhí)行,因此需要模擬一個(gè) useUpdate 函數(shù):
const mounting = useRef(true); useEffect(() => { if (mounting.current) { mounting.current = false; } else { fn(); } });
更多可以查看 精讀《怎么用 React Hooks 造輪子》
怎么替代 forceUpdateReact 官方文檔提供了一種方案:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0); function handleClick() { forceUpdate(); }
每次執(zhí)行 dispatch 時(shí),只要 state 變化就會(huì)觸發(fā)組件更新。當(dāng)然 useState 也同樣可以模擬:
const useUpdate = () => useState(0)[1];
我們知道 useState 下標(biāo)為 1 的項(xiàng)是用來(lái)更新數(shù)據(jù)的,而且就算數(shù)據(jù)沒有變化,調(diào)用了也會(huì)刷新組件,所以我們可以把返回一個(gè)沒有修改數(shù)值的 setValue,這樣它的功能就僅剩下刷新組件了。
更多可以查看 精讀《怎么用 React Hooks 造輪子》
state 拆分過多useState 目前的一種實(shí)踐,是將變量名打平,而非像 Class Component 一樣寫在一個(gè) State 對(duì)象里:
class ClassComponent extends React.PureComponent { state = { left: 0, top: 0, width: 100, height: 100 }; } // VS function FunctionComponent { const [left,setLeft] = useState(0) const [top,setTop] = useState(0) const [width,setWidth] = useState(100) const [height,setHeight] = useState(100) }
實(shí)際上在 Function Component 中也可以聚合管理 State:
function FunctionComponent() { const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 }); }
只是更新的時(shí)候,不再會(huì)自動(dòng) merge,而需要使用 ...state 語(yǔ)法:
setState(state => ({ ...state, left: e.pageX, top: e.pageY }));
可以看到,更少的黑魔法,更可預(yù)期的結(jié)果。
獲取上一個(gè) props雖然不怎么常用,但是畢竟 Class Component 可以通過 componentWillReceiveProps 拿到 previousProps 與 nextProps,對(duì)于 Function Component,最好通過自定義 Hooks 方式拿到上一個(gè)狀態(tài):
function Counter() { const [count, setCount] = useState(0); const prevCount = usePrevious(count); return (Now: {count}, before: {prevCount}
); } function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; }
通過 useEffect 在組件渲染完畢后再執(zhí)行的特性,再利用 useRef 的可變特性,讓 usePrevious 的返回值是 “上一次” Render 時(shí)的。
可見,合理運(yùn)用 useEffect useRef,可以做許多事情,而且封裝成 CustomHook 后使用起來(lái)仍然很方便。
未來(lái) usePrevious 可能成為官方 Hooks 之一。性能注意事項(xiàng)
useState 函數(shù)的參數(shù)雖然是初始值,但由于整個(gè)函數(shù)都是 Render,因此每次初始化都會(huì)被調(diào)用,如果初始值計(jì)算非常消耗時(shí)間,建議使用函數(shù)傳入,這樣只會(huì)執(zhí)行一次:
function FunctionComponent(props) { const [rows, setRows] = useState(() => createRows(props.count)); }
useRef 不支持這種特性,需要寫一些冗余的函判定是否進(jìn)行過初始化。
掌握了這些,F(xiàn)unction Component 使用起來(lái)與 Class Component 就幾乎沒有差別了!
4. 總結(jié)Function Component 功能已經(jīng)可以與 Class Component 媲美了,但目前最佳實(shí)踐比較零散,官方文檔推薦的一些解決思路甚至不比社區(qū)第三方庫(kù)的更好,可以預(yù)料到,Class Component 的功能會(huì)被五花八門的實(shí)現(xiàn)出來(lái),那些沒有被收納進(jìn)官方的 Hooks 乍看上去可能會(huì)眼花繚亂。
總之選擇了 Function Component 就同時(shí)選擇了函數(shù)式的好與壞。好處是功能強(qiáng)大,幾乎可以模擬出任何想要的功能,壞處是由于可以靈活組合,如果自定義 Hooks 命名和實(shí)現(xiàn)不夠標(biāo)準(zhǔn),函數(shù)與函數(shù)之間對(duì)接的溝通成本會(huì)更大。
討論地址是:精讀《Stateless VS Class 組件》 · Issue #137 · dt-fe/weekly
如果你想?yún)⑴c討論,請(qǐng) 點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀 - 幫你篩選靠譜的內(nèi)容。
關(guān)注 前端精讀微信公眾號(hào)
special Sponsors
DevOps 全流程平臺(tái)
版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(創(chuàng)意共享 3.0 許可證)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/109204.html
摘要:引言工具型文章要跳讀,而文學(xué)經(jīng)典就要反復(fù)研讀。原文非常長(zhǎng),所以概述是筆者精簡(jiǎn)后的。這是理解以及的關(guān)鍵,后面還會(huì)詳細(xì)介紹。從幾個(gè)疑問開始假設(shè)讀者有比較豐富的前端開發(fā)經(jīng)驗(yàn),并且寫過一些。 1. 引言 工具型文章要跳讀,而文學(xué)經(jīng)典就要反復(fù)研讀。如果說(shuō) React 0.14 版本帶來(lái)的各種生命周期可以類比到工具型文章,那么 16.7 帶來(lái)的 Hooks 就要像文學(xué)經(jīng)典一樣反復(fù)研讀。 Hooks...
摘要:拿到的都是而不是原始值,且這個(gè)值會(huì)動(dòng)態(tài)變化。精讀對(duì)于的與,筆者做一些對(duì)比。因此采取了作為優(yōu)化方案只有當(dāng)?shù)诙€(gè)依賴參數(shù)變化時(shí)才返回新引用。不需要使用等進(jìn)行性能優(yōu)化,所有性能優(yōu)化都是自動(dòng)的。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 Vue 3.0 的發(fā)布引起了軒然大波,讓我們解讀下它的 function api RFC 詳細(xì)了解一下 Vue 團(tuán)隊(duì)是怎么想的吧! 首先官方回答了幾個(gè)最受關(guān)注的...
摘要:在讀了一些文章后,大致是找到自己總是掉坑的原因了沒理解中的特性。通過這個(gè)示例,相信會(huì)比較容易地理解特性,并如何使用來(lái)暫時(shí)繞過它。在知道并理解這個(gè)特性后,有助于進(jìn)一步熟悉了的運(yùn)行機(jī)制,減少掉坑的次數(shù)。 由于剛使用 React hooks 不久,對(duì)它的脾氣還拿捏不準(zhǔn),掉了很多次坑;這里的 坑 的意思并不是說(shuō) React hooks 的設(shè)計(jì)有問題,而是我在使用的時(shí)候,因?yàn)檫€沒有跟上它的理念導(dǎo)...
摘要:精讀源碼一共行,我們分析一下其精妙的方式。更多討論討論地址是精讀新用法如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。 1 引言 很高興這一期的話題是由 epitath 的作者 grsabreu 提供的。 前端發(fā)展了 20 多年,隨著發(fā)展中國(guó)家越來(lái)越多的互聯(lián)網(wǎng)從業(yè)者涌入,現(xiàn)在前端知識(shí)玲瑯滿足,概念、庫(kù)也越來(lái)越多。雖然內(nèi)容越來(lái)越多,但作為個(gè)體的...
閱讀 2011·2021-11-15 17:58
閱讀 2203·2021-10-19 11:45
閱讀 3638·2021-09-02 15:40
閱讀 2662·2021-07-25 10:50
閱讀 3817·2019-08-30 15:56
閱讀 3217·2019-08-30 12:44
閱讀 1083·2019-08-26 13:38
閱讀 1930·2019-08-23 18:29