摘要:函數(shù)組件與類(lèi)有何不同有一段時(shí)間,規(guī)范的答案是類(lèi)可以訪問(wèn)更多功能如狀態(tài)。那么功能性函數(shù)和類(lèi)是否又根本的區(qū)別函數(shù)組件捕獲的值。假設(shè)功能組件不存在。在我到目前為止看到的所有情況中,由于錯(cuò)誤地假設(shè)功能不會(huì)改變或總是相同,所以會(huì)出現(xiàn)陳舊的封閉問(wèn)題。
React函數(shù)組件與React類(lèi)有何不同?
有一段時(shí)間,規(guī)范的答案是類(lèi)可以訪問(wèn)更多功能(如狀態(tài))。但是自從有了Hook后,這個(gè)答案變得不唯一了。
也許你聽(tīng)說(shuō)其中一個(gè)表現(xiàn)更好。哪一個(gè)?許多此類(lèi)基準(zhǔn)都存在缺陷,因此我會(huì)小心地從中得出結(jié)論。性能主要取決于代碼的作用,而不是您選擇的是函數(shù)還是類(lèi)。在我們的觀察中,雖然優(yōu)化策略有點(diǎn)不同,但性能差異可以忽略不計(jì)。
在任何一種情況下,除非您有其他原因并且不介意成為早期采用者,否則我們不建議您重寫(xiě)現(xiàn)有組件。Hooks仍然是新的。
那么功能性函數(shù)和類(lèi)是否又根本的區(qū)別?
函數(shù)組件捕獲rendered的值。讓我們看看這句話是什么意思?
注意:這篇文章不是對(duì)類(lèi)或函數(shù)的值判斷。我只描述了React中這兩種編程模型之間的區(qū)別。有關(guān)更廣泛地采用功能的問(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è)按鈕,使用setTimeout模擬網(wǎng)絡(luò)請(qǐng)求,然后顯示確認(rèn)警報(bào)。例如,如果props.user是"Dan",它將在三秒后顯示"Followed Dan"。很簡(jiǎn)單。(注意在上面的例子中我是否使用箭頭或函數(shù)聲明并不重要。函數(shù)handleClick()將以完全相同的方式工作。)
我們?nèi)绾伟阉鼘?xiě)成一個(gè)類(lèi)?感覺(jué)應(yīng)該是如下所示:
class ProfilePage extends React.Component { showMessage = () => { alert("Followed " + this.props.user); }; handleClick = () => { setTimeout(this.showMessage, 3000); }; render() { return ; } }
通常認(rèn)為這兩個(gè)代碼片段是等效的。人們經(jīng)常在這些模式之間自由地重構(gòu),而不會(huì)注意到它們的含義:
但是,這兩個(gè)代碼片段略有不同。好好看看他們。你看到了差異嗎?就個(gè)人而言,我花了一段時(shí)間才看到這一點(diǎn)。
如果你想自己搞清楚,這里是一個(gè)現(xiàn)場(chǎng)演示。本文的其余部分解釋了差異及其重要性。
在我們繼續(xù)之前,我想強(qiáng)調(diào)一點(diǎn),我所描述的差異與React Hooks本身無(wú)關(guān)。以上示例甚至不使用Hooks!這就是React中函數(shù)和類(lèi)之間的區(qū)別。如果您計(jì)劃在React應(yīng)用程序中更頻繁地使用函數(shù),則可能需要了解它。
我們將通過(guò)React應(yīng)用程序中常見(jiàn)的錯(cuò)誤說(shuō)明其差異。
打開(kāi)此示例
使用兩個(gè)按鈕嘗試以下操作序列:
單擊其中一個(gè)“關(guān)注”按鈕。
在3秒之前更改所選的配置文件。
閱讀警報(bào)文字。
你會(huì)注意到一個(gè)特殊的區(qū)別:
使用上面的ProfilePage函數(shù),單擊Follow on Dan的個(gè)人資料,然后導(dǎo)航到Sophie"s仍然會(huì)提醒"Followed Dan"。
使用上面的ProfilePage類(lèi),它會(huì)警告"Followed Sophie":
在此示例中,第一個(gè)行為是正確的行為。如果我跟隨一個(gè)人然后導(dǎo)航到另一個(gè)人的個(gè)人資料,我的組件不應(yīng)該讓使用的人感到困惑。這個(gè)類(lèi)實(shí)現(xiàn)顯然是錯(cuò)誤的。
那么為什么我們的類(lèi)示例會(huì)以這種方式運(yùn)行?
讓我們仔細(xì)看看我們類(lèi)中的showMessage方法:
class ProfilePage extends React.Component { showMessage = () => { alert("Followed " + this.props.user); };
此類(lèi)方法從this.props.user讀取。Props在React中是不可變的,因此它們永遠(yuǎn)不會(huì)改變。然而,這一直是,并且一直是可變的。
React本身會(huì)隨著時(shí)間的推移而變異,以便您可以在渲染和生命周期方法中閱讀新版本。
因此,如果我們的組件在請(qǐng)求處于運(yùn)行狀態(tài)時(shí)render,則this.props將會(huì)更改。showMessage方法從“too new”的props中讀取用戶(hù)。
這暴露了一個(gè)關(guān)于用戶(hù)界面性質(zhì)的有趣觀察。如果我們說(shuō)UI在概念上是當(dāng)前應(yīng)用程序狀態(tài)的函數(shù),則事件處理程序是渲染結(jié)果的一部分 - 就像視覺(jué)輸出一樣。我們的事件處理程序“屬于”具有特定props和state的特定渲染。
假設(shè)功能組件不存在。我們?nèi)绾谓鉀Q這個(gè)問(wèn)題?
一種方法是在事件期間盡早讀取this.props,然后將它們顯式傳遞到超時(shí)完成處理程序:
class ProfilePage extends React.Component { showMessage = (user) => { alert("Followed " + user); }; handleClick = () => { const {user} = this.props; setTimeout(() => this.showMessage(user), 3000); }; render() { return ; } }
這有效。但是,這種方法會(huì)使代碼隨著時(shí)間的推移變得更加冗長(zhǎng)和容易出錯(cuò)。如果我們需要不止一個(gè)道具怎么辦?如果我們還需要訪問(wèn)該州怎么辦?如果showMessage調(diào)用另一個(gè)方法,并且該方法讀取this.props.something或this.state.something,我們將再次遇到完全相同的問(wèn)題。所以我們必須通過(guò)showMessage調(diào)用的每個(gè)方法將this.props和this.state作為參數(shù)傳遞。
同樣,在handleClick中內(nèi)聯(lián)警報(bào)代碼并不能解決更大的問(wèn)題。我們希望以允許將其拆分為更多方法的方式構(gòu)造代碼,同時(shí)還要讀取與該調(diào)用相關(guān)的渲染所對(duì)應(yīng)的props和state。這個(gè)問(wèn)題甚至不是React獨(dú)有的 - 您可以在任何將數(shù)據(jù)放入像這樣的可變對(duì)象的UI庫(kù)中重現(xiàn)它。
也許,我們可以綁定構(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)題。請(qǐng)記住,問(wèn)題是我們從這里讀取。支持太晚了 - 不是我們正在使用的語(yǔ)法!但是,如果我們完全依賴(lài)JavaScript閉包,問(wèn)題就會(huì)消失。
通常會(huì)避免閉包,因?yàn)楹茈y想象隨著時(shí)間的推移可能會(huì)發(fā)生變異的價(jià)值。但在React中,props和state是不可改變的!(或者至少,這是一個(gè)強(qiáng)烈的推薦。)這消除了一個(gè)主要的封閉區(qū)域。
這意味著如果你從特定渲染中關(guān)閉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 ; } }
你在渲染時(shí)“捕獲”了props:
這樣,它內(nèi)部的任何代碼(包括showMessage)都可以保證看到這個(gè)特定渲染的道具。React不再“move our cheese”了。
然后我們可以在里面添加任意數(shù)量的輔助函數(shù),它們都會(huì)使用捕獲的props和state。
上面的例子是正確的,但看起來(lái)很奇怪。如果在render中定義函數(shù)而不是使用類(lèi)方法,那么有一個(gè)類(lèi)是什么意思?
實(shí)際上,我們可以通過(guò)刪除它周?chē)念?lèi)“shell”來(lái)簡(jiǎn)化代碼:
function ProfilePage(props) { const showMessage = () => { alert("Followed " + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( ); }
就像上面一樣,props仍然被捕獲 - React將它們作為參數(shù)傳遞。與此不同,props對(duì)象本身永遠(yuǎn)不會(huì)被React變異。如果你在函數(shù)定義中構(gòu)造props,那就更明顯了:
function ProfilePage({ user }) { const showMessage = () => { alert("Followed " + user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return ( ); }
當(dāng)父組件使用不同的props呈現(xiàn)ProfilePage時(shí),React將再次調(diào)用ProfilePage函數(shù)。但是我們已經(jīng)點(diǎn)擊的事件處理程序“屬于”具有自己的用戶(hù)值的前一個(gè)渲染和讀取它的showMessage回調(diào)。他們都完好無(wú)損。
現(xiàn)在我們了解React中函數(shù)和類(lèi)之間的巨大差異:
函數(shù)組件捕獲呈現(xiàn)的值。使用Hooks,同樣的原則也適用于州。考慮這個(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 ( <> > ); }
雖然這不是一個(gè)非常好的消息應(yīng)用UI,但它說(shuō)明了同樣的觀點(diǎn):如果我發(fā)送特定消息,組件不應(yīng)該對(duì)實(shí)際發(fā)送的消息感到困惑。此函數(shù)組件的消息捕獲“屬于”渲染器的狀態(tài),該渲染器返回瀏覽器調(diào)用的單擊處理程序。因此,當(dāng)我單擊“發(fā)送”時(shí),消息將設(shè)置為輸入中的內(nèi)容。
因此,默認(rèn)情況下,我們知道React捕獲道具和狀態(tài)中的函數(shù)。但是,如果我們想要閱讀不屬于這個(gè)特定渲染的最新道具或州,該怎么辦?如果我們想 “read them from the future”怎么辦?
在類(lèi)中,你可以通過(guò)閱讀this.props或this.state來(lái)實(shí)現(xiàn)它,因?yàn)樗旧硎强勺兊摹eact改變了它。在函數(shù)組件中,您還可以具有由所有組件呈現(xiàn)共享的可變值。它被稱(chēng)為“ref”:
function MyComponent() { const ref = useRef(null); // You can read or write `ref.current`. // ... }
但是,您必須自己管理它。
ref與實(shí)例字段扮演相同的角色。它是進(jìn)入可變命令世界的逃脫艙。您可能熟悉“DOM refs”,但概念更為通用。它只是一個(gè)盒子,你可以把東西放進(jìn)去。即使在視覺(jué)上,這個(gè)東西看起來(lái)像是某種東西的鏡子。它們代表了相同的概念。默認(rèn)情況下,React不會(huì)為函數(shù)組件中的最新props或狀態(tài)創(chuàng)建引用。在許多情況下,您不需要它們,分配它們將是浪費(fèi)的工作。但是,如果您愿意,可以手動(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; };
如果我們?cè)趕howMessage中讀取消息,我們會(huì)在按下“發(fā)送”按鈕時(shí)看到消息。但是當(dāng)我們讀取latestMessage.current時(shí),我們得到最新的值 - 即使我們?cè)诎聪掳l(fā)送按鈕后繼續(xù)輸入。你可以比較兩個(gè)演示(https://codesandbox.io/s/93m5... https://codesandbox.io/s/ox200vw8k9),看看差異。ref是一種“選擇退出”渲染一致性的方法,在某些情況下可以很方便。通常,您應(yīng)該避免在渲染期間讀取或設(shè)置引用,因?yàn)樗鼈兪强勺兊摹N覀兿M3咒秩镜目深A(yù)測(cè)性。但是,如果我們想獲得特定道具或狀態(tài)的最新值,那么手動(dòng)更新ref會(huì)很煩人。我們可以通過(guò)使用效果自動(dòng)化它:
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
結(jié)論在這篇文章中,我們研究了類(lèi)中常見(jiàn)的破碎模式,以及閉包如何幫助我們修復(fù)它。但是,您可能已經(jīng)注意到,當(dāng)您嘗試通過(guò)指定依賴(lài)關(guān)系數(shù)組來(lái)優(yōu)化Hook時(shí),您可能會(huì)遇到帶有過(guò)時(shí)閉包的錯(cuò)誤。是否意味著閉包是問(wèn)題?我不這么認(rèn)為。
正如我們上面所看到的,閉包實(shí)際上幫助我們解決了很難注意到的細(xì)微問(wèn)題。同樣,它們使編寫(xiě)在并發(fā)模式下正常工作的代碼變得更加容易。這是可以的,因?yàn)榻M件內(nèi)部的邏輯關(guān)閉了正確的props和渲染state。
在我到目前為止看到的所有情況中,由于錯(cuò)誤地假設(shè)“功能不會(huì)改變”或“props總是相同”,所以會(huì)出現(xiàn)“陳舊的封閉”問(wèn)題。事實(shí)并非如此,因?yàn)槲蚁M@篇文章有助于澄清。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/53797.html
摘要:函數(shù)組件與類(lèi)有何不同有一段時(shí)間,規(guī)范的答案是類(lèi)可以訪問(wèn)更多功能如狀態(tài)。那么功能性函數(shù)和類(lèi)是否又根本的區(qū)別函數(shù)組件捕獲的值。假設(shè)功能組件不存在。在我到目前為止看到的所有情況中,由于錯(cuò)誤地假設(shè)功能不會(huì)改變或總是相同,所以會(huì)出現(xiàn)陳舊的封閉問(wèn)題。 React函數(shù)組件與React類(lèi)有何不同? 有一段時(shí)間,規(guī)范的答案是類(lèi)可以訪問(wèn)更多功能(如狀態(tài))。但是自從有了Hook后,這個(gè)答案變得不唯一了。 也...
摘要:函數(shù)組件與類(lèi)有何不同有一段時(shí)間,規(guī)范的答案是類(lèi)可以訪問(wèn)更多功能如狀態(tài)。那么功能性函數(shù)和類(lèi)是否又根本的區(qū)別函數(shù)組件捕獲的值。假設(shè)功能組件不存在。在我到目前為止看到的所有情況中,由于錯(cuò)誤地假設(shè)功能不會(huì)改變或總是相同,所以會(huì)出現(xiàn)陳舊的封閉問(wèn)題。 React函數(shù)組件與React類(lèi)有何不同? 有一段時(shí)間,規(guī)范的答案是類(lèi)可以訪問(wèn)更多功能(如狀態(tài))。但是自從有了Hook后,這個(gè)答案變得不唯一了。 也...
摘要:需要注意的是,同樣的行為也適用于。這意味著我們必須重新綁定每個(gè)事件。組件的由調(diào)用它的父組件提供,這意味著所有事件都應(yīng)該與父組件相關(guān)聯(lián)。 原文鏈接:Vue.js — Considerations and Tricks showImg(https://segmentfault.com/img/bVbqHOd?w=1600&h=1599); Vue.js 是一個(gè)很棒的框架。然而,當(dāng)你開(kāi)始構(gòu)建...
摘要:相反的,提供了全局作用域和局部作用域。組成界面的分子的樣式可以通過(guò)元素選擇器定位。元素選擇器的優(yōu)先級(jí)很低,因此他們不會(huì)覆蓋任何基于類(lèi)選擇器的樣式。使用元素選擇器有以下優(yōu)點(diǎn)避免了的冗長(zhǎng)沒(méi)有冗余的類(lèi)。 最近學(xué)習(xí)到CSS的繼承屬性,正好看到這篇文章,便將它翻譯出來(lái)。作者的思想,在平時(shí)的項(xiàng)目中或多或少都有用過(guò),但是從來(lái)沒(méi)有仔細(xì)去思考如何利用這些特性讓代碼更加優(yōu)雅。 我熱愛(ài)模塊化設(shè)計(jì)。長(zhǎng)期以來(lái),...
摘要:經(jīng)常處于隨機(jī)級(jí)聯(lián)樣式表項(xiàng)目維護(hù)者的心血來(lái)潮之中,并且試圖讓項(xiàng)目編譯過(guò)程中涉及的其他庫(kù),框架或預(yù)處理器經(jīng)常成為一場(chǎng)噩夢(mèng)。上下文中的預(yù)處理器基本上是構(gòu)建過(guò)程中的一個(gè)步驟。 showImg(https://segmentfault.com/img/remote/1460000018822671?w=1920&h=960); 來(lái)源 | 愿碼(ChainDesk.CN)內(nèi)容編輯 愿碼Sloga...
閱讀 2971·2021-11-23 10:12
閱讀 2695·2021-11-23 09:51
閱讀 2045·2021-11-15 11:37
閱讀 1378·2019-08-30 15:55
閱讀 1970·2019-08-29 15:40
閱讀 1170·2019-08-28 18:30
閱讀 1653·2019-08-28 18:02
閱讀 2648·2019-08-26 12:00