有没有一种方法可以在Elm中使用React组件?

12

我知道在React中使用Elm组件(“模块”?还在学习术语)是一种已经成熟的模式,类似于react-elm-components的库,但是否有一种方法来实现相反的效果?

例如,像这样的东西是可能的:

Top-level React App
 -> React layout/component
    -> Elm components

但是这个呢:

Top-level Elm App
  -> Elm layout/components
     -> React components

如果可能的话,“底部的榆树”模式是否更优?试图推理为什么似乎没有相关文献,这可能是一个解释。
3个回答

11
不要这样做。Elm 的整个设计理念就是六边形架构。Elm 旨在作为一个封闭的系统运行,即 JS 的问题无法渗透其中。这就是为什么将 Elm 嵌入到 JS 中是一流的,并且你很难找到相反的原因。将 React 嵌入到 Elm 中的想法是交出 Elm 确定性和保证的价值。那你还不如不使用 Elm。
如果你真的只想在浏览器中进行函数式编程,并使用带有 Elm 架构的 React 组件,请查看 PureScript PUX。PUX 是为这种情况设计的,并且会比将 Elm 弄成用于防止 Elm 特别设计的事物的东西更好。
如果我的评论对你来说行不通,我可以提供以下建议,以便以不太邪恶的方式实现此目的。
首先,你可以通过端口完成这个任务。在你想要呈现 react 组件时,在 Elm 应用程序上写事件以向 JavaScript 发送数据。然后使用标准方式用 JavaScript 在 Elm Dom 空间内呈现 react 组件。
其次(这是最接近正确方法的做法),你可以使用 Elm Native 模块编写代码。这将要求你学习 Elm 的未经记录的运行时,并编写与之交互的代码。你需要在 Elm 的低级系统中编写自定义的 Html 元素,用于在其内部呈现 React 并管理重新渲染。
祝你好运!

1
我不确定这个答案是否仍然有效,因为NoRedInk的人正在强调Elm与Web组件的强大。React可能不是一个Web标准,但在Elm页面中有状态组件的小口袋的想法已不再是禁忌了。 - Simon H
这是对React的回应。Web组件是一个独立的主题。 - Fresheyeball

4
如上所述,由于 onload 的原因,这个方法本身是无法正常工作的,但可能会对寻找类似内容的其他人有所帮助。
你可以在 Elm 节点内部始终呈现 React 组件(在重写之前)。关键是如何挂载它。粗略地说:这可能会有点 hacky(并且使用 Json.Encode.encodeJSON.parse 会有点昂贵,因为你必须将数据从 Elm 中取出并转换成可消费的 JS 格式以供组件使用),但假设你已经在 window 对象上拥有 ThatComponentReactReactDOM,你可以尝试以下操作:
import Html exposing (Html, div)
import Html.Attributes as Attr exposing (attribute)
import Json.Encode as Encode exposing (Value)


reactComponent : String -> (a -> Value) -> a -> Html msg
reactComponent componentName encoder data =
    let
        jsonProps : String
        jsonProps =
            Encode.encode 0 <| encoder data

        -- a script to load the React component on `this` which is
        -- the `div` element in this case.
        onLoad : String
        onLoad =
            String.join ""
                [ "javascript:"
                , "window.ReactDOM.render("
                , "window.React.createElement(window[\""
                , componentName
                , "\"], JSON.parse("
                , jsonProps
                , ")), this)"
                ]
    in
        div [ attribute "onload" onLoad ] []

你将会遇到一个问题,那就是它无法维护自身的状态——这显然不好,也违背了Elm架构(但你可能已经知道了并且想要重用一些预先构建好的东西)。你可以使用端口将数据发送回来,但你需要在Html.Lazy.lazy3中包裹reactComponent,这样它就不会重新渲染自己了(这也是为什么你需要分别发送编码器和数据)。

1
感谢@toastal的分析和其他答案的建议,听起来这可能是最乐观的痴心妄想,尽管利用巨大的React生态系统是一个非常诱人的回报。也许我会在完成Elm新手教程后向您寻求帮助;但更有可能的是,我会吸取经验并重写一些组件——这可能是一个很好的学习经历。 - Brandon
1
我不相信这会起作用,因为显然div不支持onload。https://dev59.com/yX3aa4cB1Zd3GeqPZi1x - Christopher Armstrong

2

我一直在尝试做同样的事情。我使用了一个流行的React日期选择器,并通过端口进行了交互。这仍然是一个正在进行中的工作,但确实有效。

import React from 'react';
import ReactDOM from 'react-dom';

import * as Datetime from 'react-datetime'

function datepicker(d, cb) {
    return requestAnimationFrame( () => {
        ReactDOM.render(Welcome(d, (res) => {
            cb(res);
        }), document.getElementById('reactDate'));            
    });
}

function Welcome(initialdate, cb) {
  return (
      <div>
         <h1>{initialdate}</h1>
         <Datetime onChange={cb} value={initialdate} />
     </div>
  );
}

export default datepicker;

然后我的端口代码看起来像这样:
import datepicker from './reactDPPort.jsx';
elm.ports.trigger.subscribe( (s) => {
    datepicker(s, res => app.ports.toElm.send(res.toString()));
});

你完成了正在进行中的工作吗?你的结论是什么? - choonkeat
不需要了,现在浏览器的日期选择器普遍更好,我会直接使用它。 - Simon H

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