在 Reasonml 中使用 React ref 时如何定义组件/绑定?

How to defined component /binding when using React ref in Reasonml?

我在我的应用程序中集成 react-system-notification 模块时遇到问题,阅读了有关 Reason React Ref 的文档后,我不确定为什么不将引用传递到堆栈中;一个提示将不胜感激。

我一直收到以下错误,我过去曾在 React 中使用过此组件,但在 ReasonML/React 中使用时似乎存在一些问题。我怀疑传递了一个空引用,这破坏了组件。

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of Notifications.

绑定:

module NotificationSystem = {    
    [@bs.module "react-notification-system"] external reactClass : ReasonReact.reactClass = "default";

    let make = ( children ) => 
    ReasonReact.wrapJsForReason(
        ~reactClass, 
        ~props=Js.Obj.empty(),
        children
    )
};

组件

type action =
  | AddNotification(string);

type state = {
    _notificationSystem: ref(option(ReasonReact.reactRef)),
};

let setNotificationSystemRef = (notificationRef, {ReasonReact.state: state}) => 
  state._notificationSystem := Js.toOption(notificationRef) ;

let component = ReasonReact.reducerComponent("Notifications");

let addNotification = (message, state) => {   
    switch state._notificationSystem^ {
    | None => ()
    | Some(r) => ReasonReact.refToJsObj(r)##addNotification({"message": message, "level": "success"});      
    }
};

let make = (_children) => {
    ...component,
    initialState: () => {_notificationSystem: ref(None) },
    reducer: (action, state) =>
        switch action {
            | AddNotification(message) =>  ReasonReact.SideEffects(((_) => addNotification(message, state)))
        },
    render: ({handle, reduce}) => (
        <div>
            <NotificationSystem ref=(handle(setNotificationSystemRef)) />
            <button onClick=(reduce( (_) => AddNotification("Test Notification Test"))) > (ReasonReact.stringToElement("Click")) </button> 
        </div>
    )
};

我的猜测是 react-notification-system 没有作为 es6 组件分发,因此不会导出 default。尝试从外部删除 default

[@bs.module "react-notification-system"] external reactClass : ReasonReact.reactClass = "";

您应该始终首先尝试最简单的实现,然后从那里逐步构建,以最大程度地减少可能的错误原因。尤其是在处理像js边界这样容易出错的东西的时候。在这种情况下,没有复杂的 ref 处理。由于上述原因,您可能会发现它仍然无法正常工作,并且您一直在寻找错误的地方,因为您咬得太多了。

经过进一步调查,感谢 glensl 提示和在 Discord 上交换的一些消息,我发布了完整的答案。

问题与 bsb 在 javascript 输出中生成 "require" 语句的方式有关:

[@bs.module "react-notification-system"] external reactClass : ReasonReact.reactClass = "default";

被发射为:

var ReactNotificationSystem = require("react-notification-system");

而不是

var NotificationSystem = require("react-notification-system");

可能看起来有点老套,但是我让 bsb 发出正确的 javascript 使用以下语句:

[@bs.module ] external reactClass : ReasonReact.reactClass = "react-notification-system/dist/NotificationSystem";

然后通过对包装器组件进行一些小的调整,我能够使用以下代码让它工作:

模块 ReactNotificationSystem = { [@bs.module]外部反应类:ReasonReact.reactClass="react-notification-system/dist/NotificationSystem";

let make = ( children ) => 
ReasonReact.wrapJsForReason(
    ~reactClass, 
    ~props=Js.Obj.empty(),
    children
)
};

type action =
  | AddNotification(string);

type state = {
    _notificationSystem: ref(option(ReasonReact.reactRef)),
};

let setNotificationSystemRef = (notificationRef, {ReasonReact.state}) => 
  state._notificationSystem := Js.Nullable.to_opt(notificationRef) ;

let component = ReasonReact.reducerComponent("Notifications");

let addNotification = (message, state) => {   
    switch state._notificationSystem^ {
    | None => ()
    | Some(r) => ReasonReact.refToJsObj(r)##addNotification({"message": message, "level": "success"});      
    }
};

let make = (_children) => {
    ...component,
    initialState: () => {_notificationSystem: ref(None) },
    reducer: (action, state) =>
        switch action {
            | AddNotification(message) =>  ReasonReact.SideEffects(((_) => addNotification(message, state)))
        },
    render: ({handle, reduce}) => (
    <div>             
        <ReactNotificationSystem ref=(handle(setNotificationSystemRef)) />
        <button onClick=(reduce( (_) => AddNotification("Hello"))) > (ReasonReact.stringToElement("Click")) </button> 
    </div>
  )
};

可以在 Github here:

上找到完整的示例工作项目