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