2016-12-06 71 views
0

我想了解@trepidacious'sscalajs-reactwrapper對於this HOC反應組分。使用scalajs反應包裝React HOC組件的說明

1a)爲什麼包裝組件的類型爲hereReactComponentC[P,_,_,_]

1b)爲什麼組件ReactComponentU_的返回類型?

def wrap[P](wrappedComponent: ReactComponentC[P,_,_,_]): Props => P => ReactComponentU_ = { 

2)爲什麼工廠函數傳遞給SortableElementhere

val componentFactoryFunction = js.Dynamic.global.SortableElement(wrappedComponent.factory) ? 

是不是SortableElement需要一個組件?

3)爲什麼包裝的道具像this那樣通過?

"v" -> wrappedProps.asInstanceOf[js.Any] 

這條線的原因是什麼?

那個神奇的v來自哪裏?是從scalajs-react還是從react-sortable-hoc

4)這個包裝的原因是什麼?如果我想爲另一個HOC組件編寫一個包裝,那麼應該怎麼說呢?

回答

3

這裏有相當多的部分,但我已經彙集了一些從最低層到最高層的鏈接,同時涵蓋了問題。

第一個也是最重要的定義是React組件和React元素。 This page有一個深入的解釋 - 我建議完全跳過「管理實例」一節,因爲它通過描述傳統的UI模型來混淆水域,同時使用與React中使用的方式不同的術語。總之,我的理解是:

React Component是一個通用的概念,可以用多種方式實現。然而,它是實現的,它實質上是一個從道具(可選狀態)到頁面內容(渲染器)的函數。

React元素是頁面內容的描述,表示組件的特定呈現。

React components and props docs描述了定義React組件的兩種方式,第一種是從道具到反應元素的函數,這是我們感興趣的。然後React.createFactory文檔確認我們可以傳遞這樣的函數創建工廠。據我所知,這是爲了適應從定義React組件的多種方式(通過React.Component或React.PureComponent的子類,通過使用React.createClass,或通過從Props到ReactElement的函數)到一種方式將道具渲染爲ReactElement。我們可以通過查看這個gist在React 0.12中引入React.createFactory來看到這件事 - 本質上他們想要在用於定義React Component的類和從props到渲染時使用的React Elements的最終函數之間引入一些抽象,而不是讓班級直接提供道具。

接下來我們有一個小小的皺紋 - React.createFactory被標記爲遺留在文檔中。幸運的是,這不是一個大問題,就我所知React.createFactory(type)只是產生一個函數f(props),它與React.createElement(type, props)相同 - 我們只是在React.createElement中修復了type參數。我的反應可排序,特別的包裝進行了測試,我們可以使用的createElement代替createFactory的:

val componentFunction = js.Dynamic.global.SortableContainer(wrappedComponent.factory) 
(props) => (wrappedProps) => { 
    val p = props.toJS 
    p.updateDynamic("v")(wrappedProps.asInstanceOf[js.Any]) 
    React.asInstanceOf[js.Dynamic].createElement(componentFunction, p).asInstanceOf[ReactComponentU_] 
} 

我們現在幾乎在問題2)。如果我們看the source for SortableElement,我們可以看到sortableElement函數接受一個WrappedComponent參數 - 它用於通過「子類React.Component」方法創建另一個React組件。在這個類的render函數中,我們可以看到WrappedComponent被用作React組件,所以我們知道它確實是一個組件,即使沒有靜態類型:)這意味着WrappedComponent需要被React.createElement接受,因爲這是一個什麼樣的JSX component use desugars to

因此我們知道我們需要傳遞給sortableElement函數,這個函數在javascript React.createElement函數中可用作React Component。看看scalajs-react types doc,我們可以看到ReactComponentC看起來像是一個不錯的選擇 - 它構建了組件,大概是從道具中提取的。展望at the source for this我們可以看到,我們有兩個看起來很有希望的值 - reactClassfactory。此時,我意識到代碼可能使用了錯誤的代碼 - 我試圖用.reactClass代替.factory,這仍然有效,但更有意義,因爲我們有評論告訴我們它提供Output of [[React.createClass()]],它是有效React組件的選項。我懷疑factory也基本上將提供的組件包裝在createFactory中兩次,因爲createFactory的輸出也可用作其輸入...我認爲給出了這個更正,我們已經回答了問題2 :)這也回答了很多問題1a) - ReactComponentC是得到我們.reactClass val的scala特徵,我們需要一個scala定義的React組件。我們只關心它使用的道具類型(因爲我們必須提供它們),因此P。由於scala是鍵入的,我們知道這是我們通過正常方式構建一個scala React組件(至少對於我嘗試過的組件)得到的結果。

關於問題1b)中,我發現從像在scalajs-react AddonsReactCssTransitionGroup門面和scalajs-react-components notes on interop,其示出了非HOC成分的包裹代碼的類型ReactComponentU_。查看類型本身,我們可以看到它擴展了ReactElement,這是合理的 - 這是渲染React組件的預期結果。在我們的SortableElementSortableContainer外牆的wrap函數中,我們正在生成(最終)另一個從道具到ReactElement的函數,只是一個跳過了幾個圓圈以便使用HOC方法到達那裏。我不知道爲什麼使用ReactComponentU_代替ReactElement,我認爲這是跟蹤通過類型的組件狀態,但代碼仍然編譯,如果我返回ReactElement,這是奇怪的。

問題3)要容易得多 - scalajs-react與可以是Ints,Longs等的Props一起工作,但在Javascript中,這些不是對象。爲了讓每個scalajs組件的道具成爲一個對象,scalajs會將它們包裝在一個對象中,如{"v": props},然後在使用它們時再解開。當我們使用HOC包裝一個Scala React組件時,我們需要以某種方式獲取包裝組件的道具。由HOC函數(「包裝」組件,SortableElement或SortableContainer)生成的組件通過期望其自己的道具已經包含了包裝組件的道具作爲字段來做到這一點,然後它只是讓它們流向包裝組件,例如在SortableElement的渲染中:

<WrappedComponent 
       ref={ref} 
       {...omit(this.props, 'collection', 'disabled', 'index')} 
      /> 

this.props被傳遞給被包裝的組件。由於包裝的scala組件需要一個帶有scala props對象的「v」字段,所以我們需要將它添加到包裝組件的道具中。幸運的是,這個字段將會在未經更改的情況下通過scala組件稍後解釋。你不會看到「v」字段,因爲scalajs-react會爲你解開它。

這會在包裝其他HOC時產生問題 - 例如ReactGridLayout's WidthProvider會測量包裝組件的寬度並將其作爲{"width": width}傳遞給道具,但不幸的是我們無法從scala中看到這一點。這可能有一些解決方法。

這涵蓋了HOC包裹的部分細節和引用,但實際上這個過程是很容易的(提供您不希望訪問「注入」道具到包裹的分量):

  1. 爲外觀製作一個scala對象來組織代碼。
  2. 確定包裝組件需要什麼道具。例如在SortableElement這是index,collectiondisabled。使用這些字段在門面對象中創建一個Props案例類。
  3. 在外觀對象中寫入'wrap'函數。這包含以下內容:
  4. 接受wrappedComponent: ReactComponentC[P,_,_,_]並將其傳遞給javascript HOC函數(例如SortableElement)以生成新的React組件。
  5. 用包裝組件的道具和魔術「v」字段與包裝組件的道具建立一個JavaScript道具對象。
  6. 使用javascript React.createElement函數生成渲染包裝組件的ReactElement,並將其轉換爲ReactComponentU_。

請注意,在階段5我們需要將我們的Scala Props案例類(包裝器組件的道具)轉換爲HOC可以理解的普通JavaScript對象。被包裹的組件的道具只是直接進入「v」字段而不進行轉換,只需投射到js.Any即可。

我爲SortableElement和SortableContainer編寫的代碼將它分開了一點,以便wrap返回一個curried函數,該函數接受包裝器組件的props並生成從包裝的props到最終React元素的另一個函數。這意味着你可以提供一次包裝道具,然後在你的Scala渲染代碼中使用像普通組件一樣的結果函數。

我已經用上面的改進更新了SortableElement facade,這幾乎是HOC門面的一個簡單例子。我想其他HOC看起來會非常相似。你可能可以爲DRY的目的抽象一些代碼,但實際上這裏並沒有很多。

感謝您提出的問題並幫助您解決問題 - 回顧整個過程,尤其是您在.factory上的問題,讓我更加確信,現在這種方式正在以正確的方式運行。

3

我沒有所有的答案,但我的理解是scalajs-react的作者使用大量的類型來防止在構建組件的過程中以及構建組件的生命週期期間的錯誤。他使用帶有後綴和字母的命名約定來區分有意義的類型,但起初可能令人望而生畏。

1a)ReactComponentC是組件的構造函數(構造函數中的C)。

1b)ReactComponentU_表示未安裝的原生(JavaScript)React組件。

3)我想,看scalajs-react來源,是的,「v」是一個魔術鍵名稱。有(是?)的源代碼,這是不理想的效果也有一些注意事項;)

有一個plan to simplifyscalajs-react的類型在new version