如何向 ReasonReact 组件添加自定义方法?

How to add custom method to ReasonReact component?

我是 ReasonML 的新手。我能够使用 ReasonReact 成功创建无状态组件,但我还没有想出如何向组件添加自定义方法(例如 Next.js' static getInitialProps)。

尝试在组件上定义 getInitialProps 方法时,收到 The field getInitialProps does not belong to type ReasonReact.componentSpec 错误。

我应该如何在 React 组件上添加这个自定义方法?

组件

let str = ReasonReact.stringToElement;

let component = ReasonReact.statelessComponent("Index");

let make = (~items: Listings.items, _children) => {
  ...component,
  getInitialProps: () =>
    Js.Promise.(
      Endpoints.fetchListings()
      |> then_(Fetch.Response.json)
      |> then_((json) => Js.Json.decodeArray(json) |> resolve)
    ),
  render: (_self) =>
    <div>
      (List.length(items) > 0 ? <Listings items /> : <Loading />)
    </div>
};

let default =
  ReasonReact.wrapReasonForJs(
    ~component,
    (jsProps) => make(~items=jsProps##items, [||])
  );

错误

We've found a bug for you!
/Users/davidcalhoun/Sites/web/evergreen-roots/pages/Index.re 7:3-17

5 │ let make = (~items: Listings.items, _children) => {
6 │   ...component,
7 │   getInitialProps: () =>
8 │     Js.Promise.(
9 │       Endpoints.fetchListings()

This record expression is expected to have type
  ReasonReact.componentSpec ('a,  'b,  'c,  'd,  'e)
The field getInitialProps does not belong to type ReasonReact.componentSpec

在 Discord 中回答,在此处重新发布:

你不能像那样在组件上定义任意方法。看到 ...component 了吗?那是用静态字段传播记录并像您一样更新一些字段,例如render.

此外,我认为您不能直接将 ReasonReact 组件传递给 next 方法。它可能要求一个反应组件 class。请参阅 interop section 了解如何为 js 端公开底层 js class。

与此同时,您可以将该组件包装在定义 getInitialProps

的 js 组件的薄层中

因此,您将拥有一个具有 getInitialProps 的 reactjs 组件,它呈现一个通过互操作公开底层 class 的 ReasonReact 组件。 如果我对你的理解是正确的,那么如果你使用的是来自 Reason 的包装器,你也可以将 ReasonReact 绑定写入该 js 包装器。这使得这个过程有点人为,但你在这里遇到了一个病态案例。

作为一个方面:理想情况下,Next.js API 会要求一个组件 class 和一个 getInitialProps 在旁边,一个完全不可知的功能,不是附加到组件 class 上。这将大大简化 Reason 方面的绑定过程。

我想在我的 reason-react Next.js 页面中获取上下文并找到了解决方案。这是一个访问上下文以检测我们是否在服务器上呈现并将布尔值传递到您的 ReasonReact 组件的示例:

let make = (~onServer, _children) => {
/* component code goes here */ 
};

let default =
  ReasonReact.wrapReasonForJs(~component, jsProps => make(~onServer=jsProps##onServer, [||]));

let getInitialProps = context =>
  Js.Promise.make((~resolve, ~reject as _) => {
    let onServer =
      switch (Js.Nullable.toOption(context##req)) {
      | None => false
      | Some(_) => true
      };
    resolve(. {"onServer": onServer});
  });

let inject = [%bs.raw {| (cls, fn) => cls.getInitialProps = fn |}];

inject(default, getInitialProps);

~https://github.com/zeit/next.js/issues/4202#issuecomment-439175214 所有的功劳都归功于@tmepple 和@-rase,他们在那里发布了解决方案!

另一种解决方案是将你的“_app.js”留在纯 js 中,并将 getInitialProps 中的内容传递给 <Component someParam=manualStuff {...pageProps} />,它可以作为你的 reason-react 页面的参数。