使用scalajs-react

时间:2016-12-06 18:32:46

标签: reactjs scala.js scalajs-react

我正在尝试了解trepidacious's wrapper HOC反应组件的this scalajs-react here

1a)为什么包装组件的类型为here ReactComponentC[P,_,_,_]

1b)为什么组件的返回类型为ReactComponentU_

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

2)为什么工厂功能传递给SortableElement this

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

SortableElement是否不接受组件?

3)为什么包裹的道具像enter image description here一样传递?

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

这条线背后的原因是什么?

神奇的v来自哪里?是来自scalajs-react还是来自react-sortable-hoc

4)这个包装背后的原因是什么?如果我想为另一个HOC组件编写一个包装器,那么它应该是什么样的接收器呢?

2 个答案:

答案 0 :(得分:3)

我没有得到所有的答案,但我的理解是scalajs-react的作者使用了许多类型来防止组件构造期间以及组件生成周期中的错误。他使用带有后缀和字母的命名约定来分隔有意义的类型,但最初可能令人生畏。

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

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

3)我认为,查看scalajs-react来源,是的,“v”是一个神奇的密钥名称。源代码中还有一些注意事项,这是不理想的;)

plan to simplify中有new version scalajs-react个类型。

答案 1 :(得分:3)

这里有很多部分,但是我在整理了一些从最低层到高层的链接,同时涵盖了这些问题。

第一个也是最重要的定义是React Components和React Elements。 This page有一个深入的解释 - 我建议完全跳过"管理实例"因为它通过描述传统的UI模型来混淆水域,同时使用与React中使用的方式不同的术语。总之,我的理解是:

React Component是一个可以通过多种方式实现的一般概念。无论它实现了,它本质上是一个从道具(和可选的状态)到页面内容的函数 - 一个渲染器。

React元素是页面内容的描述,表示组件的特定呈现。

React components and props docs描述了定义React组件的两种方法,第一种是从props到react元素的函数,这是我们感兴趣的一种。React.createFactory docs然后确认我们可以将这样的函数传递给createFactory。据我所知,这是为了适应从定义React组件的多种方式(通过使用React.Component或React.PureComponent,通过使用React.createClass,或通过Props到ReactElement的函数)来适应某种方式将道具渲染到ReactElement。通过查看React 0.12中引入React.createFactory的gist,我们可以看到一些相关内容 - 本质上他们想要在用于定义React Component的类和从props到React Elements的最终函数之间引入一些抽象。在渲染时使用,而不是让类直接渲染道具。

接下来我们有一个小皱纹 - React.createFactory被标记为文档中的遗产。幸运的是,这不是一个主要问题,我可以告诉React.createFactory(type)只是生成一个与f(props)相同的函数React.createElement(type, props) - 我们只是在修复type } React.createElement中的参数。我在react-sortable-hoc包装器中对此进行了测试,我们可以使用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&#34创建另一个React组件。 ;做法。在这个类的render函数中,我们可以看到WrappedComponent被用作React组件,所以我们知道它确实是一个组件,即使没有静态类型:)这意味着WrappedComponent需要被React.createElement接受,因为这是JSX component use desugars to

因此我们知道我们需要将sortableElement函数传递给javascript React.createElement函数中可用作React组件的函数。看看scalajs-react types doc,我们可以看到ReactComponentC看起来是一个不错的选择 - 它构建组件,可能来自道具。通过查看at the source for this,我们可以看到我们有两个看起来很有前景的值 - reactClassfactory。在这一点上,我意识到代码可能使用了错误的代码 - 我已经尝试用.factory替换.reactClass并且这仍然有效,但是因为我们有评论告诉它更有意义我们它给出Output of [[React.createClass()]],这是有效React组件的选项之一。我怀疑factory也可以通过两次基本上将createFactory中提供的组件包装起来,因为createFactory的输出也可用作输入...我认为鉴于此更正,我们已经回答了问题2 :)这也几乎回答了问题1a) - ReactComponentC是scala特征,它为我们提供了scala定义的React Component所需的.reactClass val。我们只关心它使用的道具类型(因为我们必须提供它们),因此P。由于scala是键入的,我们知道这是我们以正常方式构建scala React组件所得到的(至少对于我尝试过的组件)。

在问题1b)上,我从代码中找到了类型ReactComponentU_,如scalajs-react Addons中的ReactCssTransitionGroup外观和scalajs-react-components notes on interop,其中显示了非HOC组件的包装。查看类型本身,我们可以看到它扩展ReactElement,这是有道理的 - 这是渲染React组件的预期结果。在我们wrapSortableElement外观中的SortableContainer函数中,我们正在制作(最终)从道具到ReactElement的另一个函数,只有一个跳过几个箍来实现HOC方法。我不确定为什么使用ReactComponentU_而不仅仅使用ReactElement,我认为这与通过类型跟踪组件的状态有关,但是如果我返回{{}代码仍会编译1}},这很奇怪。

问题3)更容易 - scalajs-react与Props一起使用,可以是Ints,Longs等,但在Javascript中这些不是对象。为了使每个scalajs组件的道具成为一个对象,scalajs-react将它们包裹在ReactElement之类的对象中,然后在使用它们时再次展开。当我们使用HOC包装Scala React组件时,我们需要以某种方式获取包装组件的道具。由HOC函数生成的组件("包装"组件,SortableElement或SortableContainer)通过期望其自己的道具已经包含包裹组件的道具作为字段来实现这一点,然后它只是让这些流向包装的组件,例如在SortableElement的渲染中:

{"v": props}

<WrappedComponent ref={ref} {...omit(this.props, 'collection', 'disabled', 'index')} /> 传递给包装组件。由于包裹的scala组件需要&#34; v&#34;在其中包含scala props对象的字段,我们需要将其添加到包装器组件的道具中。幸运的是,这个字段将不加改变地传递,稍后由scala组件解释。你不会看到&#34; v&#34;因为scalajs-react将为你解开它。

在包装其他HOC时会出现问题 - 例如ReactGridLayout's WidthProvider测量包裹组件的宽度并将其作为this.props传递到道具中,但不幸的是我们可以&#39 ;从scala看到这个。可能有一些解决方法。

这涵盖了HOC包装部分的细节和参考,但实际上这个过程非常简单(假设您不想访问道具&#34;注入&#34;到包装组件中):

  1. 为Facade创建一个scala对象来组织代码。
  2. 确定包装器组件需要哪些道具。例如,在{"width": width}中,这是SortableElementindexcollection。在facade对象中创建一个包含这些字段的Props案例类。
  3. 写一个&#39; wrap&#39;在facade对象中的函数。这样做如下:
  4. 接受disabled并将其传递给javascript HOC函数(例如wrappedComponent: ReactComponentC[P,_,_,_])以生成新的React组件。
  5. 使用包装器组件的道具和魔法&#34; v&#34;构建一个javascript道具对象。带有包裹组件的道具。
  6. 使用javascript React.createElement函数生成呈现包装组件的ReactElement,并将其强制转换为ReactComponentU _。
  7. 请注意,在第5阶段,我们需要将我们的Scala Props案例类(包装器组件的道具)转换为HOC可以理解的普通javascript对象。被包裹的组件的道具直接进入&#34; v&#34;没有转换的字段,只需转换为SortableElement

    我为SortableElement和SortableContainer编写的代码将其拆分一点,以便wrap返回一个curried函数,该函数接受包装器组件的props并从包装的props到最终的React元素生成另一个函数。这意味着您可以提供一次包装器道具,然后像Scala渲染代码中的普通组件一样使用生成的函数。

    我已经通过上述改进更新了SortableElement facade,现在这几乎是HOC门面的一个最小例子。我想其他的HOC看起来非常相似。你可能会为DRY的目的抽象一些代码,但实际上那里并没有那么多。

    感谢您提出问题并帮助解决这个问题 - 回顾整个过程,特别是您在js.Any上提出的问题让我更加确信现在正在以正确的方式工作(所描述的更改)。< / p>