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

gle*_*nsl 4 interop reason higher-order-components bucklescript reason-react

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

export const withStrong =
  Component => props =>
    <strong> <Component ...props/> </strong>
Run Code Online (Sandbox Code Playgroud)

假设我有一个名为的组件HelloMessage,那么这段JavaScript是等效的:

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

const HelloMessage = ...

const StrongMessage = withStrong(HelloMessage);

ReactDOM.render(
  <StrongMessage name="Joe" />,
  document.getElementById('react-app')
);
Run Code Online (Sandbox Code Playgroud)

gle*_*nsl 6

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"
);
Run Code Online (Sandbox Code Playgroud)

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

解释如下:

捆绑

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

甚至像这样简单的事情都会起作用

[@bs.module ./hoc.js]
external withStrong : 'a => 'a = "withStrong";
Run Code Online (Sandbox Code Playgroud)

假设您始终确保传递组件。但这并不是特别安全,因为您还可以通过其他任何方式来传递它,因此让我们尝试使用应使用的类型系统,将其限制为仅接受反应组分。

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

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

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

绑定本身就是所有这些。我们现在可以像这样使用它:

let strongMessage = withStrong(HelloMessage.make);
Run Code Online (Sandbox Code Playgroud)

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

React.createElementVariadic(strongMessage, { "name": "Joe" }, [||]);
Run Code Online (Sandbox Code Playgroud)

不是很好。因此,让我们对其进行修复。

JSX

<StrongMessage name="Joe" />
Run Code Online (Sandbox Code Playgroud)

转换

React.createElementVariadic(
  StrongMessage.make,
  StrongMessage.makeProps(~name="Joe", ()),
  [||]
);
Run Code Online (Sandbox Code Playgroud)

因此,我们需要一个StrongMessage具有两个功能的模块,make并且makeProps要符合的期望React.createElementVariadicmake只是组件本身,所以很简单。makeProps是一个函数,该函数将props作为带标签的参数终止unit(因为props可能是可选的),并返回js对象。这也恰好是这样[@bs.obj]做的,绝不是巧合。

然后将其放在一起,我们得到:

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

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

就是这样!好极了!

附录:快捷方式

好的,因此该makeProps功能有点令人讨厌。幸运的是,在本例中,被包装的组件的道具与原始道具相同,这也是不必要的,因为StrongMessage.makeProps它与相同HelloMessage.makeProps。那我们就偷吧!现在我们有了

module StrongMessage = {
  let make = withStrong(HelloMessage.make);
  let makeProps = HelloMessage.makeProps;
}
Run Code Online (Sandbox Code Playgroud)

但是我们可以做得更好!通过使用,include HelloMessage我们可以makeProps完全删除(感谢@bloodyowl,通过@idkjs进行此操作)。

module StrongMessage = {
  include HelloMessage;
  let make = withStrong(make);
}
Run Code Online (Sandbox Code Playgroud)

很好,不是吗?之所以可行,是因为include HelloMessage它将包括从中导出的所有定义,HelloMessage例如makePropsmake以及其他所有内容。以这种方式包装组件时,可能正是您想要的,但是请注意,如果不是您想要的,它会从所包含的模块中导入并重新导出所有内容

用法

最后,一旦有了绑定和JSX,就可以像这样使用它

ReactDOMRe.renderToElementWithId(
  <StrongMessage name="Joe" />,
  "react-app"
);
Run Code Online (Sandbox Code Playgroud)