<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          [譯]React函數(shù)組件和類組件的差異

          共 11678字,需瀏覽 24分鐘

           ·

          2021-03-14 02:21

          一、前言

          本文是一篇譯文,原文地址:

          https://overreacted.io/how-are-function-components-different-from-classes

          為什么選擇發(fā)這篇文章呢,因?yàn)檫@篇文章,讓我有眼前一亮,所以分享下。

          二、函數(shù)組件與類有什么不同?

          有一段時(shí)間,規(guī)范的答案是: 類可以訪問更多功能(如狀態(tài))。有了Hooks,就不再是這樣了。

          也許你聽說其中一個(gè)在性能上會更好。那么是哪一個(gè)? 許多這樣的benchmarks是有缺陷的,所以我會小心地從中得出結(jié)論。性能主要取決于代碼在做什么,而不是你選擇的是函數(shù)還是類。在我們的觀察中,雖然優(yōu)化策略有點(diǎn)不同,但性能差異可以忽略不計(jì)。

          在任何一種情況下,我們都不建議重寫現(xiàn)有組件,除非你有其他原因,并且你也不介意成為早期實(shí)踐者。Hooks仍然是新的(就像2014年的React一樣),并且一些“最佳實(shí)踐”尚未進(jìn)入教程。

          這給我們帶來了什么? React函數(shù)和類之間有什么根本的區(qū)別嗎?當(dāng)然,在心智模型中存在。在這篇文章中,我將看看它們之間的最大區(qū)別。 自從2015年引入函數(shù)組件以來,它就一直存在,但它經(jīng)常被忽視:

          三、函數(shù)組件捕獲渲染的值


          讓我們來解釋下這意味著什么。


          注意: 這篇文章不是對類或函數(shù)的價(jià)值判斷。我只描述了React中這兩種編程模型之間的區(qū)別。有關(guān)更廣泛地采用功能的問題,請參閱Hooks FAQ。


          考慮這個(gè)組件:

          function ProfilePage(props) {
            const showMessage = () => {
              alert('Followed ' + props.user);
            };

            const handleClick = () => {
              setTimeout(showMessage, 3000);
            };

            return (
              <button onClick={handleClick}>Follow</button>
            );
          }

          它顯示一個(gè)按鈕,使用setTimeout模擬網(wǎng)絡(luò)請求,然后顯示彈出框。例如,如果props.user'Dan',它將在三秒后顯示'Followed Dan'。很簡單。

          (請注意,在上面的示例中我是否使用箭頭或函數(shù)聲明并不重要。function handleClick()將以完全相同的方式工作。)

          我們把它寫成一個(gè)類會怎么樣?一個(gè)簡單的翻譯可能是這樣的:

          class ProfilePage extends React.Component {
            showMessage = () => {
              alert('Followed ' + this.props.user);
            };

            handleClick = () => {
              setTimeout(this.showMessage, 3000);
            };

            render() {
              return <button onClick={this.handleClick}>Follow</button>;
            }
          }

          通常認(rèn)為這兩段代碼是等價(jià)的。人們經(jīng)常在這些模式之間自由地重構(gòu),而不注意它們的含義:

          image

          但是,這兩個(gè)代碼片段略有不同。 好好看看他們。你看到區(qū)別了嗎? 就我個(gè)人而言,我花了一段時(shí)間才明白這一點(diǎn)。

          前面有劇透,如果你想自己弄明白,這里有一個(gè)在線演示。 本文的其余部分解釋了差異及其重要性。


          在我們繼續(xù)之前,我想強(qiáng)調(diào)一點(diǎn),我所描述的差異與React Hooks本身無關(guān)。以上示例甚至沒有使用Hooks!

          這都是React中函數(shù)和類之間的區(qū)別。如果你計(jì)劃在React應(yīng)用程序中更頻繁地使用函數(shù),則可能需要了解它。


          我們將通過React應(yīng)用程序中常見的bug說明其差異。

          打開此示例沙箱并使用選擇的當(dāng)前配置文件和上面的兩個(gè)ProfilePage實(shí)現(xiàn) -- 每個(gè)都渲染一個(gè)Follow按鈕。

          使用兩個(gè)按鈕嘗試此操作序列:

          1. 點(diǎn)擊 其中一個(gè)"follow"按鈕

          2. 在3秒之前 更改 所選的個(gè)人資料(筆:就是那個(gè)下拉框)。

          3. 查看 彈出的文字。

          你會注意到一個(gè)特殊的區(qū)別:

          • 使用上面的ProfilePage 函數(shù) ,單擊Follow Dan的個(gè)人資料,然后導(dǎo)航到Sophie's仍然會彈框'Followed Dan'

          • 使用上面的ProfilePage ,他將會彈出'Followed Sophie'

          image


          在此示例中,第一個(gè)行為是正確的行為。如果我follow一個(gè)人然后導(dǎo)航到另一個(gè)人的個(gè)人資料,我的組件不應(yīng)該對我follow的人感到困惑。 類的實(shí)現(xiàn)顯然是錯(cuò)誤的。

          (你應(yīng)該關(guān)注Sophie。)


          那么為什么我們的類示例會以這種方式運(yùn)行?

          讓我們仔細(xì)看看我們類中的showMessage方法:

          class ProfilePage extends React.Component {
            showMessage = () => {
              alert('Followed ' + this.props.user);
            };

          這個(gè)類的方法從this.props.user讀取。Props在React中是不可變的,因此它們永遠(yuǎn)不會改變。然而,this是,并且一直是可變的。

          實(shí)際上,這就是this在一個(gè)類中的全部的目的。React本身會隨著時(shí)間的推移而改變,以便你可以在render和生命周期方法中讀取新版本。

          因此,如果我們的組件在請求處于運(yùn)行狀態(tài)時(shí)重新呈現(xiàn),則this.props將會更改。showMessage方法從“過新”的props中讀取user

          這就暴露了一個(gè)關(guān)于用戶界面性質(zhì)的有趣觀察。如果我們說UI在概念上是當(dāng)前應(yīng)用程序狀態(tài)的函數(shù), 那么事件處理程序就是呈現(xiàn)結(jié)果的一部分——就像可視化輸出一樣。 我們的事件處理程序“屬于”具有特定props和狀態(tài)的特定渲染。

          但是,調(diào)用其回調(diào)讀取this.props的超時(shí)會中斷該關(guān)聯(lián)。我們的showMessage回調(diào)沒有“綁定”到任何特定的渲染,因此它“失去”正確的props。從this讀取切斷了這種聯(lián)系。


          假設(shè)函數(shù)組件不存在。 我們?nèi)绾谓鉀Q這個(gè)問題?

          我們希望以某種方式“修復(fù)”具有正確props的render和讀取它們的showMessage回調(diào)之間的連接。沿途的某個(gè)地方props丟失了。

          一種方法是在事件早期讀取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 <button onClick={this.handleClick}>Follow</button>;
            }
          }

          這個(gè)可以運(yùn)行。但是,這種方法會使代碼隨著時(shí)間的推移變得更加冗長和容易出錯(cuò)。如果我們需要不止一個(gè)prop怎么辦?如果我們還需要訪問該狀態(tài)怎么辦?如果showMessage調(diào)用另一個(gè)方法,并且該方法讀取this.props.somethingthis.state.something,我們將再次遇到完全相同的問題。 所以我們必須通過從showMessage調(diào)用的每個(gè)方法將this.propsthis.state作為參數(shù)傳遞。

          這樣做會破壞通常由類提供的人體工程學(xué)。這也很難記住或執(zhí)行, 這就是人們經(jīng)常解決問題的原因。

          同樣,在handleClick中嵌入alert代碼并不能解決更大的問題。我們希望以一種允許將代碼拆分為更多方法的方式來構(gòu)造代碼,同時(shí)還可以讀取與這個(gè)調(diào)用相關(guān)的呈現(xiàn)所對應(yīng)的props和狀態(tài)。這個(gè)問題甚至不是React獨(dú)有的 - 你可以在任何UI庫中重現(xiàn)它,你只需要將數(shù)據(jù)放入可變的對象,比如this

          也許,我們可以在構(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 <button onClick={this.handleClick}>Follow</button>;
            }
          }

          不,這不能解決任何問題。請記住,問題是我們從this.props讀取的太晚了 - 不是我們正在使用的語法!但是,如果我們完全依賴JavaScript閉包,問題就會消失。

          通常會避免閉包,因?yàn)楹茈y想象隨著時(shí)間的推移可能會發(fā)生改變的值。但在React中,props和狀態(tài)是不可改變的!(或者至少,這是一個(gè)強(qiáng)烈的推薦。)這就消除了閉包的主要障礙。

          這意味著,如果結(jié)束某個(gè)特定渲染中的props或狀態(tài),則始終可以指望它們保持完全相同:

          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 <button onClick={handleClick}>Follow</button>;
            }
          }

          你在渲染時(shí)“捕獲”了props:

          image

          這樣,它內(nèi)部的任何代碼(包括showMessage)都可以保證看到這個(gè)特定渲染的props。React不再“移動我們的奶酪”了。

          然后我們可以在里面添加任意數(shù)量的輔助函數(shù),它們都會使用捕獲的props和狀態(tài)。 閉包來救場了!


          [上面的例子](example above)是正確的,但看起來很奇怪。如果在render中定義函數(shù)而不是使用類方法,那么擁有一個(gè)類有什么意義呢?

          實(shí)際上,我們可以通過刪除它周圍的類“外殼”來簡化代碼:

          function ProfilePage(props) {

            const showMessage = () => {
              alert('Followed ' + props.user);
            };

            const handleClick = () => {
              setTimeout(showMessage, 3000);
            };

            return (
              <button onClick={handleClick}>Follow</button>
            );
          }

          就像上面一樣,props仍然被捕獲 - React將它們作為參數(shù)傳遞。this不同,props對象本身永遠(yuǎn)不會被React改變。

          如果你在函數(shù)定義中對props進(jìn)行解構(gòu),效果會更明顯:

          function ProfilePage({ user }) {  const showMessage = () => {
              alert('Followed ' + user);  };

            const handleClick = () => {
              setTimeout(showMessage, 3000);
            };

            return (
              <button onClick={handleClick}>Follow</button>
            );
          }

          當(dāng)父組件使用不同的props呈現(xiàn)ProfilePage時(shí),React將再次調(diào)用ProfilePage函數(shù)。但我們單擊的事件處理程序“屬于”上一個(gè)呈現(xiàn),它有自己的user值和讀取它的showMessage回調(diào)。它們都完好無損。

          這就是為什么,在這個(gè)演示的功能版本中,單擊關(guān)注Sophie的個(gè)人資料,然后將選擇更改為Sunil會彈出'Followed Sophie'

          image

          這個(gè)行為是正確的。(雖然你可能也想關(guān)注Sunil!)


          現(xiàn)在我們了解React中函數(shù)和類之間的巨大差異:

          函數(shù)組件捕獲呈現(xiàn)的值。

          使用Hooks,同樣的原則也適用于狀態(tài)。 考慮這個(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 (
              <>
                <input value={message} onChange={handleMessageChange} />
                <button onClick={handleSendClick}>Send</button>
              </>
            );
          }

          (這里是在線演示。)

          雖然這不是一個(gè)非常好的message應(yīng)用的UI,但它說明了同樣的觀點(diǎn):如果我發(fā)送特定消息,組件不應(yīng)該對實(shí)際發(fā)送的消息感到困惑。這個(gè)函數(shù)組件的message捕獲了“屬于”呈現(xiàn)的狀態(tài),呈現(xiàn)返回了瀏覽器調(diào)用的單擊處理程序。因此,當(dāng)我單擊"send"時(shí),消息將設(shè)置為輸入中的內(nèi)容。


          因此,默認(rèn)情況下,我們知道React中的函數(shù)捕獲props和狀態(tài)。但是,如果我們想要閱讀不屬于這個(gè)特定渲染的最新props或狀態(tài),該怎么辦? 如果我們想“從未來讀取它們”怎么辦?

          在類中,你可以通過閱讀this.propsthis.state來實(shí)現(xiàn)它,因?yàn)?code>this本身是可變的。React改變了它。在函數(shù)組件中,還可以有一個(gè)可變值,該值由所有組件呈現(xiàn)共享。它被稱為“ref”:

          function MyComponent() {
            const ref = useRef(null);
            // You can read or write `ref.current`.
            // ...
          }

          但是,你必須自己管理它。

          ref與實(shí)例字段扮演相同的角色。這是進(jìn)入可變命令式世界的出口。你可能熟悉“DOM refs”,但概念更為通用。它只是一個(gè)盒子,你可以把東西放進(jìn)去。

          即使在視覺上,this.something看起來像是something.current的鏡子。它們代表了相同的概念。

          默認(rèn)情況下,React不會為函數(shù)組件中的最新props或狀態(tài)創(chuàng)建ref。在許多情況下,你不需要它們,分配它們將是浪費(fèi)工作。但是,如果你愿意,可以手動跟蹤值:

          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中讀取message,我們會在按下“發(fā)送”按鈕時(shí)看到消息。但是當(dāng)我們讀取latestMessage.current時(shí),我們得到最新的值 - 即使我們在按下發(fā)送按鈕后繼續(xù)輸入。

          你可以比較兩個(gè) 演示,看看差異。ref是一種“選擇退出”渲染一致性的方法,在某些情況下可以很方便。

          通常,你應(yīng)該避免在渲染 期間 讀取或設(shè)置refs,因?yàn)樗鼈兪强勺兊摹N覀兿M3咒秩镜目深A(yù)測性。但是,如果我們想獲得特定props或狀態(tài)的最新值,那么手動更新ref會很煩人。 我們可以通過使用effect來自動處理它:

          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);
            };
            // ...
          }

          (這里是一個(gè)demo。)

          我們在effect 進(jìn)行賦值,以便ref值僅在DOM更新后更改。這確保了我們的突變不會破壞依賴于可中斷呈現(xiàn)的Time Slicing and Suspense等特性。

          使用像這樣的ref并不是經(jīng)常需要的。捕獲props或狀態(tài)通常是更好的默認(rèn)配置。 但是,在處理間隔和訂閱等命令式API時(shí),它可以很方便。請記住,你可以跟蹤 任何 這樣的值 - 一個(gè)prop,一個(gè)狀態(tài)變量,整個(gè)props對象,甚至是函數(shù)。

          這種模式對于優(yōu)化也很方便,例如當(dāng)useCallback標(biāo)識更改太頻繁時(shí)。但是,using a reducer 通常是一個(gè) 更好的解決方案。(后續(xù)的博客文章的主題!)


          在這篇文章中,我們研究了類中常見的破壞模式,以及閉包如何幫助我們修復(fù)它。然而,你可能已經(jīng)注意到,當(dāng)你試圖通過指定依賴項(xiàng)數(shù)組來優(yōu)化Hooks時(shí),可能會遇到使用過時(shí)閉包的bug。是否意味著閉包是問題?我不這么認(rèn)為。

          正如我們上面所看到的,閉包實(shí)際上幫助我們解決了很難注意到的細(xì)微問題。類似地,它們使在并發(fā)模式下編寫正確工作的代碼更加容易。這是可能的,因?yàn)榻M件內(nèi)部的邏輯結(jié)束了正確的props和狀態(tài)。

          到目前為止,我所看到的所有情況下,“過時(shí)的閉包”問題都是由于錯(cuò)誤地假設(shè)“函數(shù)不會更改”或“props總是相同”而發(fā)生的。 事實(shí)并非如此,因?yàn)槲蚁M@篇文章有助于澄清這個(gè)問題。

          函數(shù)與它們的props和狀態(tài)密切相關(guān),因此它們的身份也同樣重要。這不是bug,而是函數(shù)組件的一個(gè)特性。例如,函數(shù)不應(yīng)該從useeffectusecallback的“依賴項(xiàng)數(shù)組”中排除。(正確的修復(fù)通常是useReducer或上面的useRef解決方案 - 我們很快就會記錄如何在它們之間進(jìn)行選擇。)

          當(dāng)我們編寫大多數(shù)帶有函數(shù)的React代碼時(shí),我們需要調(diào)整優(yōu)化代碼,以及哪些值會隨時(shí)間變化。

          正如 Fredrik所說 :

          到目前為止,我在hook中發(fā)現(xiàn)的最好的規(guī)則是“編寫代碼時(shí),就好像任何值都可以隨時(shí)更改”。

          函數(shù)也不例外。這將需要一段時(shí)間才能成為react學(xué)習(xí)材料中的常識。這需要從階級觀念上做一些調(diào)整。但我希望這篇文章可以幫助你以新的眼光看待它。

          React函數(shù)總是捕獲它們的值 - 現(xiàn)在我們知道原因了。

          他們是一個(gè)完全不同的神奇寶貝。


          關(guān)注我,一起攜手進(jìn)階

          瀏覽 80
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩精品人妻无码 | 操高中生到高潮在线观看免费 | 色偷偷亚洲| 欧美在线三级视频 | 日韩欧美高清dvd碟片 |