如何在 ReasonReact 中绑定和使用高阶组件

How to bind to and use a higher-order component in ReasonReact

假设我有一个高阶组件,类似于以下简单的定义,从 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')
);

TL;DR:

这应该与请求的 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"
);

还有一些 a runnable example on the Reason playground 进行了一些调整以解决没有单独的 JavaScript 文件的问题。

解释如下:

绑定

withStrong 只是一个函数。它恰好是一个接受和 returns 反应组件的函数,这有点神秘,但它们实际上只是像其他任何值一样的值。我们可以像普通函数一样绑定它。

即使像这样简单的东西也行得通

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

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

ReasonReact source code 说组件的类型是 component('props),所以这就是我们要使用的类型。

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

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

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

let strongMessage = withStrong(HelloMessage.make);

很遗憾,这不支持 JSX。要按原样呈现 strongMessage,我们必须编写类似

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

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

JSX

<StrongMessage name="Joe" />

transforms

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

所以我们需要一个 StrongMessage 模块,它有两个功能,makemakeProps,符合 React.createElementVariadic 的预期。 make 只是组件本身,所以这很简单。 makeProps 是一个函数,它接受 props 作为由 unit 终止的标记参数(因为 props 可能是可选的)和 returns 一个 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"
);