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

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')
);
1个回答

12

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"
);

此外,Reason Playground 上也有一个可运行的示例,只需对其进行一些修改即可解决不必要单独 JavaScript 文件的问题。

绑定

withStrong 只是一个函数。它恰好是一个接收和返回 React 组件的函数,这有点神秘,但它们像任何其他值一样。我们可以像普通函数一样绑定它。

即使是这样简单的操作也可以工作。

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

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

ReasonReact源代码表示组件具有类型component('props),因此这就是我们将使用的内容。

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

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

至于绑定本身,就是这些了。现在我们可以像这样使用它:

let strongMessage = withStrong(HelloMessage.make);

不幸的是,这不支持JSX。要像原样呈现strongMessage,我们必须编写类似于以下内容的代码:

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

不太好,让我们修复它。

JSX

<StrongMessage name="Joe" />

transforms to

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

因此,我们需要一个StrongMessage模块,其中包含两个函数:makemakeProps,它们符合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"
);

你好 @glennsl,如果你的HOC返回一个带有组件的东西,例如authstate,它看起来是什么样子?或者换句话说,我如何访问随组件返回的props?谢谢。 - armand
1
@idkjs 我不确定你的意思。你能给我一个 JavaScript 的例子吗? - glennsl
@glennsl 我们如何处理 hoc 返回一个可以接受额外 prop 的组件的情况呢?例如:一个 styled hoc 接受一个组件并返回一个将接受名为 css 的新 prop 的组件。 - a-c-sreedhar-reddy
1
据我所知,目前还没有办法使用额外的props扩展props类型。 - glennsl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接