摘要:但是,你可能已經(jīng)注意到,當(dāng)你試圖通過(guò)指定依賴(lài)數(shù)組來(lái)優(yōu)化時(shí),可能會(huì)遇到帶有過(guò)時(shí)閉包的錯(cuò)誤。這是否意味著閉包是問(wèn)題所在我不這么認(rèn)為。到目前為止,我所看到的所有情況下,過(guò)時(shí)的閉包問(wèn)題都是由于錯(cuò)誤地假設(shè)函數(shù)不更改或總是相同而發(fā)生的。
原文鏈接:https://overreacted.io/how-ar...
在很長(zhǎng)一段時(shí)間內(nèi),標(biāo)準(zhǔn)答案是class components提供更多的特性(像state)。但隨著Hooks的出現(xiàn),答案就不再是這樣子了。
或許你聽(tīng)說(shuō)過(guò)他們中的一個(gè)性能可能更好,哪一個(gè)?因?yàn)楦鞣N的判斷標(biāo)準(zhǔn)獲取都存在缺陷,所以我們需要小心仔細(xì)的得出結(jié)論。性能的好壞主要取決于什么?它主要取決于你的代碼在做什么,而不是你使用的是function還是class。在我們的觀察中,盡管優(yōu)化的策略可能會(huì)有些許的不同,但性能的差異幾乎可以忽略不及。
無(wú)論是哪種情況,我們都不建議你重寫(xiě)現(xiàn)有的組件,除非你有一些其他的原因或者是想成為Hooks的早期的采用者。Hooks仍然是一個(gè)新特性(就像2014年的React一樣),一些最佳實(shí)踐還沒(méi)有被寫(xiě)入到教程中。
那我們?cè)撛趺崔k?function components和class components之間有什么本質(zhì)的區(qū)別嗎?顯然,在構(gòu)思模型中是不同的。在這篇文章中,我們將看到他們之間最大的區(qū)別,自從2015年推出function componetns以來(lái),它就一直存在著,但是卻經(jīng)常被忽視:
function components捕獲渲染值(capture value)
注意: 本文并不是對(duì)函數(shù)或者類(lèi)的值做判斷。我只描述了React中這兩個(gè)編程模型之間的區(qū)別。有關(guān)更廣泛地采用函數(shù)的問(wèn)題,請(qǐng)參閱hooks常見(jiàn)問(wèn)題解答。
思考下面這樣一個(gè)組件:
function ProfilePage(props) { const showMessage = () => { alert("Followed " + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( ); }
這個(gè)組件通過(guò)setTimeout模擬網(wǎng)絡(luò)請(qǐng)求,在點(diǎn)擊按鈕3秒后彈出props.user的值,如果props.user的值是Dan的話(huà),他將在點(diǎn)擊后3秒彈出“Followed Dan”。(注意,使用箭頭函數(shù)還是函數(shù)聲明的形式并不重要,handleClick函數(shù)的工作方式完全相同)
如果改寫(xiě)成class形式可能長(zhǎng)下面這個(gè)樣子:
class ProfilePage extends React.Component { showMessage = () => { alert("Followed " + this.props.user); }; handleClick = () => { setTimeout(this.showMessage, 3000); }; render() { return ; } }
通常認(rèn)為這兩段代碼是等效的。但是大家經(jīng)常在這兩種形式之間來(lái)回切換代碼而不去關(guān)注他們的含義。
然而其實(shí)這兩段代碼是有細(xì)微的差別的,就我個(gè)人而言,我花費(fèi)了一段時(shí)間才看出來(lái)。
如果你自己想弄清楚的話(huà),這里有一個(gè)在線(xiàn)demo。本文的剩余部分解釋了有什么不同和它的重要性。
再繼續(xù)之前,我要強(qiáng)調(diào),我所描述的差異本身和React Hooks無(wú)關(guān),上面的例子甚至都沒(méi)有使用Hooks。
這全部都是React中,function和class的差別。如果你想要在React應(yīng)用中去頻繁的使用function components,那么你應(yīng)該去了結(jié)它。
我們將用一個(gè)在React應(yīng)用程序中常見(jiàn)的錯(cuò)誤來(lái)說(shuō)明這一區(qū)別。
打開(kāi)這個(gè)示例,有一個(gè)主頁(yè)select和兩個(gè)主頁(yè),且每一個(gè)包含一個(gè)Follow按鈕。
嘗試按照下面的順序操作:
點(diǎn)擊一個(gè)Follow按鈕
改變select選項(xiàng)然后等待3秒
查看alert的文字
你會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題:
在function components中,在Dan的主頁(yè)點(diǎn)擊follow然后切換到Sophie,alert仍然會(huì)展示“Followed Dan”。
在class components中,alert的卻是“Followed Sophie”。
在這個(gè)例子中,第一個(gè)行為是正確的。如果我Follow A,然后導(dǎo)航B的主頁(yè),我的組件不應(yīng)該Follow到B。這個(gè)class顯然有缺陷。
所以為什么我們class的例子展示出這樣的結(jié)果呢?
讓我們仔細(xì)研究一下class中的showMessage方法:
showMessage = () => { alert("Followed " + this.props.user); };
這個(gè)方法從this.props.user取值,在React中,props應(yīng)該是不可變的,但是this卻是可變的。
實(shí)際上,在React內(nèi)部會(huì)隨著時(shí)間的推移改變this,以便可以在render和生命周期中取到最新的版本。
所以如果我們的組件在請(qǐng)求過(guò)程中re-render,this.props將會(huì)改變,showMessage方法將會(huì)從“最新”的props中取到user的值。
這就暴露了一個(gè)關(guān)于UI的有趣問(wèn)題。如果說(shuō)UI是一個(gè)關(guān)于當(dāng)前應(yīng)用state的函數(shù),那么事件處理函數(shù)就是render的一部分,就像是可視化輸出一樣。我們的事件處理函數(shù)“屬于“某一特定state和props的render。
但是在包含超時(shí)操作的回調(diào)函數(shù)內(nèi)讀取this.props會(huì)破壞這個(gè)關(guān)聯(lián)。showMessage沒(méi)有“綁定”到任何一個(gè)特定的render,因此它“丟失”了正確的props。
我們說(shuō)function components不會(huì)存在這個(gè)問(wèn)題。那么我們?cè)撛趺慈ソ鉀Q呢?
我們需要去用某種方式“修復(fù)”正確的props到showMessage之間的關(guān)聯(lián)。在執(zhí)行的某個(gè)地方,props丟失了。
一個(gè)簡(jiǎn)單的方式就是在早期我們就拿到這個(gè)this.props的值,然后顯示的去將它傳遞到超時(shí)處理函數(shù)中:
class ProfilePage extends React.Component { showMessage = (user) => { alert("Followed " + user); }; handleClick = () => { const {user} = this.props; setTimeout(() => this.showMessage(user), 3000); }; render() { return ; } }
這是可行的。然而,這種方法使代碼更加冗長(zhǎng),并且隨著時(shí)間的推移更容易出錯(cuò)。如果我們需要的不僅僅是一個(gè)單一的props怎么辦?如果ShowMessage調(diào)用另一個(gè)方法,而該方法讀取this.props.something或this.state.something,我們將再次遇到完全相同的問(wèn)題。所以我們必須通過(guò)在ShowMessage調(diào)用的每個(gè)方法,將this.props和this.state作為參數(shù)傳遞。
這樣做我們通常會(huì)破壞一個(gè)class,并且會(huì)導(dǎo)致很多bug出現(xiàn)。
同樣,在handleClick中用alert展示也不能暴露出更深的問(wèn)題。如果我們想要去結(jié)構(gòu)化我們的代碼,將代碼拆分出不同的方法,并且在讀取props和state時(shí)也能保持一樣的展示結(jié)果,而且不僅僅在React中,你也可以在任何UI庫(kù)中去調(diào)用它。
也許,我們可以在構(gòu)造函數(shù)中綁定這些方法?
class ProfilePage extends React.Component { constructor(props) { super(props); this.showMessage = this.showMessage.bind(this); this.handleClick = this.handleClick.bind(this); } showMessage() { alert("Followed " + this.props.user); } handleClick() { setTimeout(this.showMessage, 3000); } render() { return ; } }
不,這并不能解決任何問(wèn)題。記住,我們的問(wèn)題是拿到this.props太晚了,而不是我們使用何種語(yǔ)法。但是,如果我們完全依賴(lài)于js的閉包,問(wèn)題就會(huì)得到解決。
閉包通常是被避免的,因?yàn)樗茈y考慮一個(gè)隨時(shí)間變化的值。但是在React中,props和state應(yīng)該是不可變的。
這意味著,如果去掉某個(gè)特定render中的props或state,則始終可以指望它們保持相同:
class ProfilePage extends React.Component { render() { // Capture the props! const props = this.props; // Note: we are *inside render*. // These aren"t class methods. const showMessage = () => { alert("Followed " + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ; } }
已經(jīng)在render時(shí)“捕獲”到了props。
這樣,它里面的任何代碼(包括showMessage)都可以保證取到某個(gè)特定render中的props了。
然后我們可以添加很多的helper函數(shù),他們都可以捕獲到props和state。閉包救了我們。
上面的例子是正確的,但看起來(lái)很奇怪。如果只是在render中定義函數(shù)而不是使用類(lèi)方法,那么我們使用一個(gè)class又有什么意義呢?
實(shí)際上我們可以通過(guò)移除class來(lái)簡(jiǎn)化代碼:
function ProfilePage(props) { const showMessage = () => { alert("Followed " + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( ); }
就像上面所說(shuō)的,props仍然可以被捕獲到,React將它作為一個(gè)參數(shù)傳遞。不同的是,props對(duì)象本身不會(huì)因React而發(fā)生變化了。
在下面中就更明顯了:
function ProfilePage({ user }) { const showMessage = () => { alert("Followed " + user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( ); }
當(dāng)父組件根據(jù)不同的props渲染時(shí),React將會(huì)再次調(diào)用function,但是我們點(diǎn)擊的事件處理函數(shù)是上一個(gè)包含user值的render,并且showMessage函數(shù)已經(jīng)拿到了user這個(gè)值
這就是為什么在這個(gè)版本的function demo中在Sophie主頁(yè)點(diǎn)擊Follow,然后改變select,將會(huì)alert “Followed Sophie”。
現(xiàn)在我們知道了在React中 function 和 class的最大不同。
function components捕獲渲染值(capture value)
對(duì)于鉤子,同樣的原理也適用于state??紤]這個(gè)例子:
function MessageThread() { const [message, setMessage] = useState(""); const showMessage = () => { alert("You said: " + message); }; const handleSendClick = () => { setTimeout(showMessage, 3000); }; const handleMessageChange = (e) => { setMessage(e.target.value); }; return ( <> > ); }
這里是在線(xiàn)demo
這個(gè)例子說(shuō)明了相同點(diǎn):在點(diǎn)擊send按鈕后,再次修改輸入框的值,3秒后的輸出依然是點(diǎn)擊前輸入框的值。這說(shuō)明function Hooks同樣具有capture value的特性。
所以我們知道了在React中function默認(rèn)情況下會(huì)捕獲props和state(capture value)。但是如果我們想要去避免這個(gè)capture value呢?
在class中,我們可以通過(guò)使用this.props和this.state,因?yàn)?b>this本事是可變的,React改變了它,在function components中,還有一個(gè)被所有組件所共享的可變值,被叫做ref:
function MyComponent() { const ref = useRef(null); // You can read or write `ref.current`. // ... }
但是,你必須自己管理它。
ref和實(shí)例字段有著相同的作用,你也許更為熟悉“dom refs”,但是這個(gè)概念更為普遍,它僅僅是一個(gè)“放置一些東西的通用容器”。
盡管看起來(lái)它像是某一些東西的鏡像,但實(shí)際上他們表示著相同的概念。
React默認(rèn)不會(huì)為function components創(chuàng)建保存最新props和state的refs。因?yàn)楹芏嗲闆r下你是不需要他們的,并且分配他們也很浪費(fèi)時(shí)間。但是需要的時(shí)候可以手動(dòng)的去跟蹤值:
function MessageThread() { const [message, setMessage] = useState(""); const latestMessage = useRef(""); const showMessage = () => { alert("You said: " + latestMessage.current); }; const handleSendClick = () => { setTimeout(showMessage, 3000); }; const handleMessageChange = (e) => { setMessage(e.target.value); latestMessage.current = e.target.value; };
這時(shí)我們發(fā)現(xiàn),在點(diǎn)擊send按鈕后繼續(xù)輸入,3秒后alert的是點(diǎn)擊按鈕后輸入的值而不是點(diǎn)擊按鈕錢(qián)輸入的值。
通常,應(yīng)該避免在渲染期間讀取或設(shè)置refs,因?yàn)樗鼈兪强勺兊?。我們希望保持渲染的可預(yù)測(cè)性。但是,如果我們想要獲取特定props或state的最新值,手動(dòng)更新ref可能會(huì)很煩人。我們可以通過(guò)使用一種效果來(lái)實(shí)現(xiàn)自動(dòng)化(useEffect在每次render都會(huì)執(zhí)行):
function MessageThread() { const [message, setMessage] = useState(""); // Keep track of the latest value. const latestMessage = useRef(""); useEffect(() => { latestMessage.current = message; }); const showMessage = () => { alert("You said: " + latestMessage.current); };
這里是demo
我們?cè)?b>effect中進(jìn)行賦值,以便ref的值只在DOM更新后才更改。
像這樣使用ref不是經(jīng)常需要的。通常capture props或state才是默認(rèn)更好的選擇。但是,在處理諸如間隔和訂閱之類(lèi)的命令式API時(shí),它非常方便。記住,您可以跟蹤任何這樣的值:一個(gè)prop、一個(gè)state變量、整個(gè)props對(duì)象,甚至一個(gè)函數(shù)。
在本文中,我們研究了class中常見(jiàn)的中斷模式,以及閉包如何幫助我們修復(fù)它。但是,你可能已經(jīng)注意到,當(dāng)你試圖通過(guò)指定依賴(lài)數(shù)組來(lái)優(yōu)化Hooks時(shí),可能會(huì)遇到帶有過(guò)時(shí)閉包的錯(cuò)誤。這是否意味著閉包是問(wèn)題所在?我不這么認(rèn)為。
正如我們上面所看到的,閉包實(shí)際上幫助我們解決了難以注意到的細(xì)微問(wèn)題。類(lèi)似地,它們使在并發(fā)模式下正確地編寫(xiě)代碼變得更加容易。
到目前為止,我所看到的所有情況下,“過(guò)時(shí)的閉包”問(wèn)題都是由于錯(cuò)誤地假設(shè)“函數(shù)不更改”或“props總是相同”而發(fā)生的。事實(shí)并非如此,我希望這篇文章能夠幫助澄清。
function components沒(méi)有props和state,因此它們的也同樣重要。這不是bug,而是function components的一個(gè)特性。例如,函數(shù)不應(yīng)該從useEffect或useCallback的“依賴(lài)項(xiàng)數(shù)組”中被排除。(正確的解決方案通常是上面的useReducer或useRef解決方案。)
當(dāng)我們用函數(shù)編寫(xiě)大多數(shù)React代碼時(shí),我們需要調(diào)整優(yōu)化代碼的直覺(jué),以及什么值會(huì)隨著時(shí)間而改變。
正如Fredrik所說(shuō):
對(duì)于Hooks,我迄今為止發(fā)現(xiàn)的最好的規(guī)則是“代碼就像任何值在任何時(shí)候都可以改變”。
React的function總是捕捉它們的值(capture value)—— 現(xiàn)在我們知道為什么了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/102990.html
摘要:這是一個(gè)用于構(gòu)建響應(yīng)式應(yīng)用和網(wǎng)站的前端框架。是基于設(shè)計(jì)的一套豐富的組件。這是一個(gè)對(duì)混合式手機(jī)應(yīng)用框架的擴(kuò)展庫(kù)。到目前為止它僅大小,而且不依賴(lài)于任何第三方的插件,它可以很輕量的被用來(lái)創(chuàng)建和應(yīng)用。 _Material design_是Google開(kāi)發(fā)的,目的是為了統(tǒng)一公司的web端和手機(jī)端的產(chǎn)品風(fēng)格。它是基于很多的原則,比如像合適的動(dòng)畫(huà),響應(yīng)式,以及顏色和陰影的使用。完整的指南詳情請(qǐng)看這里...
摘要:這是一個(gè)用于構(gòu)建響應(yīng)式應(yīng)用和網(wǎng)站的前端框架。是基于設(shè)計(jì)的一套豐富的組件。這是一個(gè)對(duì)混合式手機(jī)應(yīng)用框架的擴(kuò)展庫(kù)。到目前為止它僅大小,而且不依賴(lài)于任何第三方的插件,它可以很輕量的被用來(lái)創(chuàng)建和應(yīng)用。 _Material design_是Google開(kāi)發(fā)的,目的是為了統(tǒng)一公司的web端和手機(jī)端的產(chǎn)品風(fēng)格。它是基于很多的原則,比如像合適的動(dòng)畫(huà),響應(yīng)式,以及顏色和陰影的使用。完整的指南詳情請(qǐng)看這里...
摘要:在里面有兩種組件類(lèi)組件和函數(shù)式組件兩者有明顯的區(qū)別比如是屬于的類(lèi)是一個(gè)函數(shù)它返回一個(gè)組件什么是先看一段代碼這是一個(gè)函數(shù)式組件它和類(lèi)組件最關(guān)鍵的區(qū)別就是函數(shù)式組件沒(méi)有和一系列的鉤子函數(shù)這也是函數(shù)式組件經(jīng)常被用作無(wú)狀態(tài)組件的原因 在React里面有兩種組件, Class components(類(lèi)組件) 和 Functional components(函數(shù)式組件).兩者有明顯的區(qū)別,比如 ...
摘要:譯者前端小智原文就像人們對(duì)更新移動(dòng)應(yīng)用程序和操作系統(tǒng)感到興奮一樣,開(kāi)發(fā)人員也應(yīng)該對(duì)更新框架感到興奮。錯(cuò)誤邊界是一種組件。注意將作為值傳遞進(jìn)去并不會(huì)導(dǎo)致使用。如果兩者不同,則返回一個(gè)用于更新?tīng)顟B(tài)的對(duì)象,否則就返回,表示不需要更新?tīng)顟B(tài)。 譯者:前端小智 原文:medium.freecodecamp.org/why-react16… 就像人們對(duì)更新移動(dòng)應(yīng)用程序和操作系統(tǒng)感到興奮一樣,開(kāi)發(fā)人員也應(yīng)...
閱讀 3561·2023-04-26 00:39
閱讀 4790·2021-09-22 10:02
閱讀 2613·2021-08-09 13:46
閱讀 1177·2019-08-29 18:40
閱讀 1500·2019-08-29 18:33
閱讀 829·2019-08-29 17:14
閱讀 1571·2019-08-29 12:40
閱讀 3096·2019-08-28 18:07