5

假设我有一个从 JavaScript 模块导出的高阶组件,类似于以下简单定义./hoc.js

export const withStrong =
  Component => props =>
    <strong> <Component ...props/> </strong>

假设我有一些名为 的组件HelloMessage,那么这段 JavaScript 的等价物是什么:

import { withStrong } from './hoc.js';

const HelloMessage = ...

const StrongMessage = withStrong(HelloMessage);

ReactDOM.render(
  <StrongMessage name="Joe" />,
  document.getElementById('react-app')
);
4

1 回答 1

10

TL;博士:

这应该与请求的 JavaScript 片段完全相同:

[@bs.module ./hoc.js]
external withStrong
  : React.component('props) => React.component('props)
  = "withStrong";

module HelloMessage = ...

module StrongMessage = {
  include HelloMessage;
  let make = withStrong(make);
};

ReactDOMRe.renderToElementWithId(
  <StrongMessage name="Joe" />,
  "react-app"
);

Reason 游乐场上还有一个可运行的示例,其中进行了一些修改以解决没有单独的 JavaScript 文件的问题。

解释如下:

捆绑

withStrong只是一个功能。它恰好是一个接受并返回一个 react 组件的函数,这有点神秘,但它们实际上就像其他任何值一样只是值。我们可以像绑定普通函数一样绑定它。

即使像这样简单的事情也会起作用

[@bs.module ./hoc.js]
external withStrong : 'a => 'a = "withStrong";

假设您始终确保传入一个组件。但它并不是特别安全,因为您也可以传递任何其他内容,所以让我们尝试使用应该使用的类型系统,将其限制为仅接受反应组件。

ReasonReact源代码说组件有 type component('props),所以这就是我们将使用的。

[@bs.module ./hoc.js]
external withStrong
  : React.component('props) => React.component('props)
  = "withStrong";

在参数和返回类型中使用'props类型变量意味着我们将它们限制为相同。也就是说,返回的组件将具有与传入的完全相同的 props,这正是我们在这种情况下想要的。

这就是绑定本身的全部内容。我们现在可以像这样使用它:

let strongMessage = withStrong(HelloMessage.make);

不幸的是,这不支持 JSX。要按原样渲染strongMessage,我们必须编写类似

React.createElementVariadic(strongMessage, { "name": "Joe" }, [||]);

不是很好。所以让我们解决这个问题。

JSX

<StrongMessage name="Joe" />

转换

React.createElementVariadic(
  StrongMessage.make,
  StrongMessage.makeProps(~name="Joe", ()),
  [||]
);

所以我们需要一个StrongMessage具有两个功能的模块,make并且makeProps符合React.createElementVariadic. make只是组件本身,所以这很简单。makeProps是一个函数,它接受道具作为标记参数终止unit(因为道具可能是可选的)并返回一个 js 对象。这也恰好是什么[@bs.obj],这绝不是巧合。

把这些放在一起,我们得到:

module StrongMessage = {
  let make = withStrong(HelloMessage.make);

  [@bs.obj]
  external makeProps
    : (~name: string, unit) => {. "name" string }
    = "";
}

就是这样!耶!

附录:快捷方式

好的,所以这个makeProps功能有点烦人。幸运的是,在我们的案例中,包装组件的 props 与原始组件相同,因此也没有必要,因为StrongMessage.makeProps将与HelloMessage.makeProps. 那我们就偷那个吧!现在我们有了

module StrongMessage = {
  let make = withStrong(HelloMessage.make);
  let makeProps = HelloMessage.makeProps;
}

但我们可以做得更好!通过使用include HelloMessage我们可以makeProps完全放弃(感谢@bloodyowl,通过@idkjs,为此)。

module StrongMessage = {
  include HelloMessage;
  let make = withStrong(make);
}

这很不错,不是吗?这是有效的,因为include HelloMessage将包括所有导出的定义,HelloMessage例如makeProps,make以及其他任何东西。当您以这种方式包装组件时,这可能是您想要的,但请注意它会从包含的模块导入和重新导出所有内容,以防这不是您想要的。

用法

最后,一旦我们同时拥有绑定和 JSX,我们就可以像这样使用它

ReactDOMRe.renderToElementWithId(
  <StrongMessage name="Joe" />,
  "react-app"
);
于 2019-08-24T20:17:42.723 回答