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

时间:2017-12-28 08:40:19

标签: ffi reason bucklescript reason-react

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

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

  

元素类型无效:需要一个字符串(用于内置组件)   或类/函数(对于复合组件)但得到:undefined。您   可能忘记从您定义的文件中导出组件,   或者您可能混淆了默认导入和命名导入。

     

检查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>
    )
};

2 个答案:

答案 0 :(得分:1)

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

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

您应该首先尝试最简单的实现,然后从那里逐步构建,以尽量减少可能的错误原因。特别是在处理像js边界一样容易出错的事情时。在这种情况下,没有复杂的ref处理。你可能会发现它仍然不起作用,因为上述情况,并且你一直在寻找错误的地方因为你咬得比你能咀嚼的多。

答案 1 :(得分:1)

经过一些进一步的调查,感谢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");

Mightseem有点hacky然而我得到bsb发出正确的javascript使用以下声明:

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

然后对包装器组件稍作调整,我能够使用以下代码:

模块ReactNotificationSystem = {     [@ bs.module] external reactClass: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