0

我试图了解@trepidacious 的 这个HOC反应组件的scalajs-react 包装器。

1a) 为什么这里 ReactComponentC[P,_,_,_]是包装组件的类型?

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

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

2)为什么将工厂函数传递给SortableElement 这里

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

SortableElement带 Component 吗?

3)为什么包裹的道具是这样传递的?

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

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

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

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

4

2 回答 2

3

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

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

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

3)我认为,看看scalajs-react源代码,是的,“v”是一个神奇的键名。源代码中还有(曾经?)一些注释表明这并不理想;)

计划在 新版本scalajs-react中简化's 类型。

于 2016-12-07T15:03:20.313 回答
3

这里有很多部分,但是我在涵盖问题的同时将一些从最低级别到最高级别的链接放在一起。

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

React 组件是一个通用概念,可以通过多种方式实现。不管它是如何实现的,它本质上是一个从道具(和可选的状态)到页面内容的函数——一个渲染器。

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

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

接下来我们有一个小问题 -React.createFactory在文档中被标记为遗留问题。幸运的是,这不是一个主要问题,据我所知,React.createFactory(type)它只是产生了一个f(props)React.createElement(type, props)- 我们只是typeReact.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)。如果我们查看SortableElement 的源代码,我们可以看到该sortableElement函数接受一个WrappedComponent参数 - 这用于通过“子类 React.Component”方法创建另一个 React 组件。在render这个类的函数中,我们可以看到它WrappedComponent被用作一个 React 组件,所以我们知道它确实是一个组件,即使没有静态类型:) 这意味着 WrappedComponent 需要被接受React.createElement,因为这是什么一个JSX 组件使用 desugar 来.

因此我们知道我们需要向sortableElement函数传递一些可用作 javascriptReact.createElement函数中的 React 组件的东西。查看scalajs-react types 文档,我们可以看到 ReactComponentC 看起来是一个不错的选择——它构造组件,大概是从 props 构建的。查看源代码,我们可以看到我们有两个看起来很有希望的值 -reactClassfactory. 在这一点上,我意识到代码可能使用了错误的代码 - 我尝试替换.factory.reactClass,这仍然有效,但更有意义,因为我们有评论告诉我们它给出了Output of [[React.createClass()]],这是选项之一一个有效的 React 组件。我怀疑factory也可以通过在 createFactory 中两次包装提供的组件来工作,因为 createFactory 的输出也可用作其输入...我认为鉴于此更正,我们已经回答了问题 2 :) 这也几乎回答了问题 1a) -ReactComponentC是scala trait 为.reactClass我们提供了 scala 定义的 React 组件所需的 val。我们只关心它使用的道具类型(因为我们必须提供它们),因此P. 由于 scala 是类型化的,我们知道这是我们以正常方式构建 scala React 组件所得到的(至少对于我尝试过的组件)。

在问题 1b) 中,我ReactComponentU_从代码中找到了类型,例如scalajs-react AddonsReactCssTransitionGroup中的外观和interop 上的 scalajs-react-components 注释,其中显示了非 HOC 组件的包装。查看类型本身,我们可以看到它 extends ,这是有道理的——这是渲染 React 组件的预期结果。在我们在 the和facades 的函数中,我们(最终)生成了另一个从 props 到 ReactElement 的函数,它只是通过 HOC 方法跳过了几圈到达那里。我不确定为什么使用而不是 just ,我认为这与通过类型跟踪组件的状态有关,但是如果我返回,代码仍然可以编译ReactElementwrapSortableElementSortableContainerReactComponentU_ReactElementReactElement,这很奇怪。

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

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

this.props被传递到被包装的组件。由于被包装的 scala 组件需要一个包含 scala props 对象的“v”字段,因此我们需要将其添加到包装组件的 props 中。幸运的是,该字段将通过原样通过,稍后由 scala 组件解释。你不会看到“v”字段,因为 scalajs-react 会为你解开它。

这确实会在包装其他一些 HOC 时引发问题 - 例如ReactGridLayout 的 WidthProvider测量包装组件的宽度并将其通过 props as 传递{"width": width},但不幸的是我们无法从 scala 中看到这一点。很可能有一些解决方法。

这涵盖了 HOC 包装部分的详细信息和参考,但实际上该过程非常简单(前提是您不想访问“注入”到包装组件中的道具):

  1. 为外观创建一个 scala 对象来组织代码。
  2. 弄清楚包装器组件需要哪些道具。例如,SortableElement这是index,collectiondisabled。在外观对象中使用这些字段创建一个 Props 案例类。
  3. 在外观对象中编写一个“包装”函数。这将执行以下操作:
  4. 接受 awrappedComponent: ReactComponentC[P,_,_,_]并将其传递给 javascript HOC 函数(例如SortableElement)以生成新的 React 组件。
  5. 使用包装组件的 props 和带有包装组件的 props 的魔术“v”字段构建一个 javascript props 对象。
  6. 使用 javascript React.createElement 函数生成一个呈现包装组件的 ReactElement,并将其强制转换为 ReactComponentU_。

请注意,在第 5 阶段,我们需要将 Scala Props 案例类(包装器组件的 props)转换为 HOC 可以理解的普通 javascript 对象。包装组件的 props 直接进入“v”字段,无需转换,只需转换为js.Any.

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

我已经用上面的改进更新了SortableElement 外观,现在这几乎是一个 HOC 外观的最小示例。我想其他 HOC 看起来会非常相似。您可能可以出于 DRY 的目的抽象一些代码,但实际上实际上并没有很多。

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

于 2016-12-08T14:44:57.677 回答