使用scalajs反应包装React HOC组件的说明

问题描述:

我想了解@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组件编写一个包装,那么应该怎么说呢?

这里有相当多的部分,但我已经汇集了一些从最低层到最高层的链接,同时涵盖了问题。

第一个也是最重要的定义是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上的问题,让我更加确信,现在这种方式正在以正确的方式运行。

我没有所有的答案,但我的理解是scalajs-react的作者使用大量的类型来防止在构建组件的过程中以及构建组件的生命周期期间的错误。他使用带有后缀和字母的命名约定来区分有意义的类型,但起初可能令人望而生畏。

1a)ReactComponentC是组件的构造函数(构造函数中的C)。

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

3)我想,看scalajs-react来源,是的,“v”是一个魔术键名称。有(是?)的源代码,这是不理想的效果也有一些注意事项;)

有一个plan to simplifyscalajs-react的类型在new version