[譯] 深刻理解 Props 和 State

原文連接: learnreact.design/2018/01/15/…react

喜歡理由: 文筆生動 通俗易懂react-native

特別鳴謝: 原做者 Linton Ye 的傾情校對ionic

系列博客: 用通俗的語言和塗鴉來解釋 React 術語post

上篇文章中,咱們介紹了組件、props 和 state 。學習

props 和 state 的區別至關明顯,肯定什麼時候使用 props 和 state 彷佛也很簡單。舉個例子,屋頂的顏色天然就是 prop ,由於顏色是屋頂的固有屬性。另外一方面,門的開關狀態很顯然是 state ,由於門在建立後還能夠打開或關上。然而在本文中,咱們未來挑戰這一思惟方式!動畫

沒開玩笑?!?沒錯,你所看到的東西既能夠是 prop,又能夠是 state 。並無絕對的界限。我將介紹一種更有用、偏實戰的方式來思考 prop 和 state 。spa

學習目標

當你讀完本文後但願你能從新回到這裏,並可以輕鬆回答如下問題:翻譯

  • props 和 state 的主要用途是什麼?
  • 「state 提高」的含義是什麼?在什麼場景下須要提高 state ?

新成員

你注意到房子周圍的新成員了嗎?試試點擊房門!設計

查看由 focuser (@focuser) 在 CodePen 編寫的 Demo : 有貓的 React 小屋code

這是一隻嗜睡的貓,門一關她就睡,只有當門再開啓的時候纔會起來。若是把門關上,她當即又睡過去了。

實現貓

如今我來問你,若是實現貓的行爲?先來試試吧!

先從下面的「代碼」入手,花點時間先讀一遍。(再次重申,這並不是真正的 JavaScript 代碼,它只是以一種簡化的形式來幫助你理解概念,同時不會被 JS 中的細節所幹擾。)

House:
  <div> <Roof /> <Wall /> <Window /> <Door /> <Cat /> </div>

Door:
  State: status <!-- "open""closed" -->
  <div>{state.status} door</div>
  當點擊門時
    若是 door.state.status 爲 "open"
      將 door.state.status 修改爲 "closed"
    不然
      將 door.state.status 修改爲 "open"
複製代碼

House 組件中又新增了 Cat 標籤。那麼 Cat 組件又是怎麼樣的呢?咱們來定義它。

貓要麼睡、要麼醒。這彷佛跟門的開關狀態很相似。或許咱們一樣可使用 state 來表示貓的狀態:

Cat:
  State: status <!-- "sleeping""awake" -->
  <div>{state.status} cat</div>
複製代碼

Cat 組件定義好後,還須要實現的就只剩下將貓和門的狀態進行同步。門的狀態爲 「open」 時,咱們想要貓的狀態爲 「awake」,反之爲 「sleeping」 。

就這麼簡單?看看再說吧…

第一次嘗試

既然咱們已經有了根據當前狀態切換門狀態的代碼,莫不如咱們就在此處切換貓的狀態:

Door:
  State: status <!-- "open""closed" -->
  <div>{state.status} door</div>
  當點擊門時
    若是 state.status 爲 "open"
      將 state.status 修改爲 "closed"
      將 cat.state.status 修改爲 "sleeping"     <!-- 錯誤的 -->
    不然
      將 state.status 修改爲 "open"
      將 cat.state.status 修改爲 "awake"        <!-- 錯誤的 -->
複製代碼

不幸的是,這不起做用!還記得組件的 state 是私有數據嗎?只有在組件的內部才能訪問。其餘組件,不管是父組件仍是兄弟組件,都沒法訪問本組件的 state 。

很遺憾,咱們在 Door 組件內嘗試修改貓的狀態以失敗了結。(轉換成真正的 JavaScript 代碼也不例外)

第二次嘗試

那麼在 Cat 組件內來修改貓的狀態如何?此次應該能夠的,是吧?

Cat:
  State: status <!-- "sleeping""awake" -->
  <div>{state.status} cat</div>
  當點擊門時                                    <!-- 黑人門號臉???怎麼個點擊法? -->
    若是 door.state.status 爲 "open"            <!-- 錯誤的 -->
      將 cat.state.status 修改爲 "sleeping"
    不然
      將 cat.state.status 修改爲 "awake"
複製代碼

毫無疑問,在 Cat 組件內修改貓的狀態是沒問題的。但咱們須要讀取門的狀態來決定貓的狀態是什麼。門的狀態是 Door 組件的 state ,所以沒法在 Cat 組件裏訪問!

解決辦法

呃!太蹩腳了。要保持門和貓的狀態同步,咱們必需要在某處能同時訪問二者。但看上去數據是經過設計而對外隱藏的。若是來解決此難題呢?

解決辦法就是須要咱們靈活地理解 state 和 props 的用法。

提高門的 state

House 組件:

House:
  <div> ... <Door /> <Cat /> </div>
複製代碼

DoorCat 是並排放置的。或許這就是能夠輕鬆同步它們的地方?

可是,咱們如今是在 House 組件內。與以前的嘗試同理,在這裏是沒辦法讀取 Door 的 state 或者改變 Cat 的 state 。

但若是咱們使用 props 來替代 state 呢?

House:
  <div>
    ...
    <Door status="open" />
    <Cat status="awake" />
  </div>
複製代碼

當門關上時:

House:
  <div>
    ...
    <Door status="closed" />
    <Cat status="sleeping" />
  </div>
複製代碼

固然,門的狀態不會是固定的值,它會隨時間而改變。咱們用 doorStatus 來表示門的狀態。

House:
  <div>
    ...
    <Door status={doorStatus} />
    <Cat status={若是 doorStatus 爲 'open' 值爲 'awake' 不然爲 'sleeping'} />
  </div>
複製代碼

這不就解決同步的問題了嘛。順便問下,這個會變化的值 doorStatus 是什麼?在組件中什麼是能夠改變的?沒錯,正是 state 。

House:
  State: doorStatus <!-- 'open''closed' -->
  <div>
    ...
    <Door status={state.doorStatus} />
    <Cat status={若是 state.doorStatus 爲 'open' 值爲 'awake' 不然爲 'sleeping'} />
  </div>
複製代碼

太棒了!House 組件如今定義的很好,門和貓的狀態也能完美同步。

咱們還須要修改 DoorCat 組件,使用 props 來代替 state :

Door:
  <div>{props.status} door</div>
Cat:
  <div>{props.status} cat</div> 
複製代碼

正如你所見,由於咱們想要使用來父組件的 state,在這種狀況下,爲了設置貓的狀態,門的狀態實際上是來自於 House 的,咱們能夠將相同的數據表示爲父組件的 state,並將數據做爲 props 傳遞給子組件。一般,這被稱之爲 state 提高。咱們將 state 移至組件的更高層級處。

更改房子的 state

如今門和貓的狀態經過房子的 state 進行鏈接。若是想開門或喚醒貓的話,咱們須要更改 House 組件的 state 。

問題來了,哪裏是惟一能夠更新 House 的 state 的地方?就在 House 組件內,沒錯吧?

可是,咱們想要在 Door 裏來觸發此次更改。也就是說,咱們想要的效果是隻有當點擊門時纔開門,而不是點擊整個房子或窗戶等。

因此 Door 組件須要作些改動:

Door:
  <div>{props.status} door</div>
  當點擊門時 
    作某件事來修改 `House` 的 state
複製代碼

但等等,以前不是說不能在 Door 組件內修改 House 的 state 嗎?

沒錯。咱們沒辦法直接修改 House 的 state 。但並不等於說不能間接地修改。看下面…

House 組件內,咱們來寫代碼以實際修改它的 state :

House:
  State: doorStatus <!-- 'open''closed' -->
  toggleDoorStatus:
    若是 state.doorStatus 爲 'open'
      將 state.doorStatus 修改爲 'closed'
    不然
      將 state.doorStatus 修改爲 'open'
  ...
複製代碼

此刻,咱們還未指定什麼時候運行這段代碼。咱們只是給了它一個名字 (toggleDoorStatus),以便稍後經過名稱來找到它運行。

而後將 toggleDoorStatus 做爲 prop 傳遞給 Door 組件:

House:
  ...
  <div>
    ...
    <Door ... onClickAction={toggleDoorStatus} />
    ...
  </div>
複製代碼

Door 組件中,咱們只需執行這個點擊操做便可:

Door:
  <div>{props.status} door</div>
  當點擊門時 
    執行 props.onClickAction <!-- 實際運行的是名爲 "toggleDoorStatus" 的代碼-->
複製代碼

這就像把電視遙控器傳遞給其餘人同樣。某人在 Door 組件內按下了遙控器按鍵。House 組件裏的電視機就會換臺或加大音量。

將會發生什麼取決於傳給 Door 的遙控器是什麼。它可能控制的是房間裏的電視、空調或高保真音響系統。在 Door 組件內,某人須要作的只是按下遙控器的按鍵。

這就是咱們所須要的!下面是完整「代碼」:

House:
  State: doorStatus <!-- 'open' or 'closed' -->
  toggleDoorStatus:
    若是 state.doorStatus 爲 'open'
      將 state.doorStatus 修改爲 'closed'
    不然
      將 state.doorStatus 修改爲 'open'
  <div>
    ...
    <Door status={state.doorStatus} onClickAction={toggleDoorStatus} />
    <Cat status={若是 state.doorStatus 爲 'open' 值爲 'awake' 不然爲 'sleeping'} /> </div>
Door:
  <div>{props.status} door</div>
  當點擊門時
    執行 props.onClickAction
Cat:
  <div>{props.status} cat</div>  
複製代碼

再次審視 Props 和 State

如今讓咱們重溫幾個問題,props 和 state 的區別是什麼?什麼時候應該使用 state ?什麼時候應該使用 props ?

什麼時候使用 state ? 什麼時候使用 props ?

若是你還記得的話,我曾說過 props 是組件的固有屬性,它是不會改變的,而 state 是組件建立後纔有的,它是能夠改變的。當最初學習這兩個概念時,它是有幫助的。

可是,咱們剛剛建立的示例讓這一觀點變得使人困惑。不管是門的開的,仍是貓是睡着的,這理所應當應該是 state ,但咱們卻使用 props 來表示它們。這是爲何?

事實證實,在 state 和 props 的選擇問題上,仍是有很大的靈活性的。這取決於你看它的視角,你能夠採用不一樣的方式來爲組件建模。例如,當門敞開之時,你能夠說它是門的狀態,你能夠說它是房子的狀態。

一種更有用的理解方式

感到困惑了?這是一種更有用的思考問題的方式:

  • State: 若是 UI 須要更改就表示某處確定會有 state
  • Props: 用來傳遞數據、傳遞控制

當應用運行時,若是 UI 須要變化,那它必定是 state 。當點擊門時,門或開或關,那麼它必定是某處的 state 。

可是,state 並不必定是更新組件的 state 。它可能位於某個上游組件中。這徹底都取決於咱們須要在何處以及如何使用這些信息。舉個例子,咱們決定將門的狀態從 Door 組件提高到 House 組件中,是由於咱們須要在 House 組件中使用它。

另外一方面,props 只是用來向下傳遞數據的東西。就像咱們以前將門的狀態從 House 組件中向下傳遞給 Door 組件。

props 還能夠用來向下傳遞控制。例如,咱們將事件處理方法從 House 傳給 Door

本示例中的 props 是否會改變值?

並無,props 永遠不會改變值。我懂你的意思,門開開關關,貓睡了又醒。由於咱們如今使用 props 來表示它們,很容易讓人認爲 props 的行爲就比如 state,它們的值改變了,是這樣嗎?

這只是一個錯覺,我發現它與翻頁書的動畫至關類似。

每次房子的 state 發生改變,舊的那隻貓就會消失,而後一隻嶄新狀態的貓將從新建立。可是這一過程發生的很是之快,這就形成了咱們的視覺殘留,認爲只有一隻貓在那睡了又醒。

翻書中任何頁面上的草圖都不會移動。相似的,每隻貓在其(短暫的)生命中始終保持清醒/沉睡。

總結

好了,咱們經過一個更復雜的示例再次學習了 props 和 state 。在這個示例中,當點擊門時,門須要切換開關狀態,同時咱們還須要將門和貓的狀態保持同步,

由於 state 是私有的,因此咱們須要將門的狀態從 Door 組件提高到 House 組件中。這樣咱們即可以在 House 組件中使用此數據來設置門和貓的狀態。咱們將此數據做爲 props 傳給 DoorCat,以便它們根據門的狀態來顯示正確的圖片。

另外一個需求是點擊門時觸發狀態的變動。由於如今門的狀態是 House 的 state,它是私有數據,只能在 Door 組件中間接來更改它。咱們在 House 組件中編寫了實際修改 state 的代碼,而後將其做爲 props 傳遞給 Door 。這相似於把電視的遙控器傳給別人。

本文中的示例或許會讓你感到一絲困惑。下面是一種用來思考 prop 和 state 的更實用的方式:

  • State: 若是 UI 須要更改就表示某處確定會有 state
  • Props: 用來傳遞數據、傳遞控制

到目前爲止感受如何?若是你有任何問題或意見,請給我留言!

相關文章
相關標籤/搜索