Question: Is there a way to use React Components within Elm?


Is there a way to use React Components within Elm?

Answers 2
Added at 2016-12-28 19:12

I know that using Elm components ("modules"? still learning parlance) within React is an established pattern with libraries like react-elm-components, but is there a way to do the inverse?

e.g., something like this is possible:

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

But what about this:

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

If it is possible, is the 'elm-at-the-bottom' pattern superior? Trying to reason why there doesn't seem to be literature on it & that's a possible explanation.

Answers to

Is there a way to use React Components within Elm?

nr: #1 dodano: 2016-12-28 21:12

You can always render a React component--'til you get around to rewriting it ;)--inside an Elm node. The trick is going to be mounting it. Shooting from the hip: it'd be a little hacky (and a bit expensive with a Json.Encode.encode and JSON.parse since you have to get the data out of Elm and into a consumable JS format for the component), but assuming you have ThatComponent, React, and ReactDOM on the window object you could try something along the lines of

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 =
        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)"
        div [ attribute "onload" onLoad ] []

What you will have though is a problem with it maintaining its own state--which is obviously bad and runs against the Elm architecture (but you probably know that and want to reuse something pre-built). You can use ports to send in data back in, but you'd want to wrap that reactComponent in Html.Lazy.lazy3 so it doesn't rerender itself (which is why you'd send in both the encoder and data separately).

Feel free to comment or find me on the Elm Slack if you'd be interested in tackling this fun problem.

nr: #2 dodano: 2016-12-28 21:12

Don't do that. The whole point of Elm is hexagonal design. Elm is intended to function as a closed system, one in which the evils of JS cannot penetrate. This is why embedding Elm in JS is first class, and you are having such trouble finding the inverse. The idea of embedding React into Elm is to hand over the value of Elm's determinism and assurances. You might as well not use Elm at all.

If you really just want to do functional programming in the browser and use React components with the Elm architecture, check out PureScript PUX. PUX is designed for that use-case and is going to give you better results than hacking Elm to do things Elm was specifically designed to prevent.

If my comments are a non-starter for you I can offer the following advice for being evil in this way, without being toooo evil.

First, you can accomplish this with ports. Write your Elm application to fire events on a port to JavaScript when you want to render your react component. Then render the react component inside of the Elm Dom space, in the standard way with JavaScript.

Second (this is the closest to doing it properly you are going to get), you can write this using Elm Native modules. This will require you to learn Elm's undocumented runtime, and write code that interacts with it. What you will do is write a custom Html element in Elm's low level system, that rendered React inside itself and manages re-rendering.

Good luck!

Source Show
◀ Wstecz