GithubHelp home page GithubHelp logo

cultureamp / react-elm-components Goto Github PK

View Code? Open in Web Editor NEW
780.0 24.0 54.0 442 KB

Write React components in Elm

Home Page: https://www.npmjs.com/package/react-elm-components

License: BSD 3-Clause "New" or "Revised" License

JavaScript 38.61% HTML 5.06% Elm 43.63% CSS 12.70%

react-elm-components's Introduction

Write React components in Elm

This package makes it easy to turn Elm code into React components.

Companies that use Elm in production usually start with a single component. So if you want to use Elm at work, start with a small experiment. Do people think it is nice? Do more! Do people think it sucks? Do less!

Read more about how to use Elm at work here.

Example

Usage

After you have compiled an Elm program to JavaScript, you can embed it in React like this:

import Elm from 'react-elm-components'
import { Todo } from '../dist/elm/todomvc.js'

function render() {
	return <Elm src={Todo} />
}

Flags

Sometimes you want to give your Elm program some flags on start up. For example, maybe your Todo module needs to get an array of todos. You would write something like this:

import Elm from 'react-elm-components'
import { Todo } from '../dist/elm/todomvc.js'

function render() {
	var flags = { todos: ["Get Milk", "Do Laundry"] };
	return <Elm src={Todo} flags={flags} />
}

These flags will be given to the Elm program, allowing you to do some setup work in JS first.

JavaScript/Elm Interop

As your Elm program gets fancier, you will probably need to interact with JavaScript. We do this with ports. Think of these as holes in the side of an Elm program that let you pass messages back-and-forth.

So maybe we extend our Todo app to allow outsiders to register new tasks through the todos port. And maybe we also expose numActiveTodos so that the outsider can know how much work you have left. You would set it up like this:

import Elm from 'react-elm-components'
import { Todo } from '../dist/elm/todomvc.js'

function render() {
	return <Elm src={Todo} ports={setupPorts} />
}

function setupPorts(ports) {
	ports.numActiveTodos.subscribe(function(n) {
		console.log(n);
	});

	ports.todos.send("Invent the Universe");
	ports.todos.send("Bake an Apple Pie");
}

In the setupPorts function, we first subscribe to the numActiveTodos port. Whenever the number of active todos changes, we will run that function and log the number on the console. After that, we send two values through the todos port. This will add both of these into the model and trigger the numActiveTodos callback twice.

Advanced Usage

Once the Elm component is initialized, changing the flags and ports properties will do nothing. So here are some tricks that may help you out:

  1. If you want to reinitialize your Elm component, add a different key to the old and new components. This way old one is destroyed and replaced by the new one.
  2. If you want to mess with ports, you can save the ports object into your state and access it later.
  3. This package is super simple. Fewer than 20 lines. Check out the implementation and do it different if you want!

Angular, Ember, etc.

If you want to embed Elm in Angular or Ember or whatever else, you are in luck!

The implementation is under 20 lines, mostly React-related. The important lines are basically running the following program at the correct time:

var Elm = require('../dist/elm/todomvc.js');
var app = Elm.Todo.embed(node, flags);
setupPorts(app.ports)

So if you are interested in embedding Elm in something else, do the same trick! You can get more complete docs on embedding Elm in HTML here and JavaScript interop here. Let the community know if you make something!


react-elm-components is maintained by the Front End Capability Team at Culture Amp.

react-elm-components's People

Contributors

akselw avatar argshook avatar backstage-culture-amp[bot] avatar ckychris avatar evancz avatar jasononeil avatar moritztk avatar ryanmtaylor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-elm-components's Issues

Warning from React: React.createClass is deprecated and will be removed in version 16

Hey folks,
Thanks for making this extremely useful package.
I'm using it to introduce Elm to a React codebase and I keep getting this warning that seems to be coming from this package:

warning.js:36 Warning: A Component: React.createClass is deprecated and will be removed in version 16. Use plain JavaScript classes instead. If you're not yet ready to migrate, create-react-class is available on npm as a drop-in replacement.

Trying to run a simple example and it didn't compile

./src/components/elm/Test.elm
Error: Compiler process exited with error Compilation failed
-- NO elm.json FILE ------------------------------------------------------------

It looks like you are starting a new Elm project. Very exciting! Try running:

    elm init

It will help you get set up. It is really simple!

I don't have this elm init command. The weird thing is that I have configured my webpack and it seems that my folders structure is okay. Can anyone help me here?

Here's my webpack.js file.

image

Update to work with elm 0.19

Unfortunately, this module currently does not work with Elm 0.19.

In 0.19 we no longer have the embed function ( https://github.com/akselw/react-elm-components/blob/master/index.js#L7 ) and I noticed the compiled JavaScript is now expecting a scope context and Elm property, which is undefined when imported.

(function(scope){
'use strict';

...

function _Platform_export(exports)
{
	scope['Elm']
		? _Platform_mergeExportsDebug('Elm', scope['Elm'], exports)
		: scope['Elm'] = exports;
}

...

_Platform_export({
  'Main':{
    'init':author$project$Main$main(elm$json$Json$Decode$succeed(_Utils_Tuple0))
    ({"versions":{"elm":"0.19.0"},"types":{"message":"msg","aliases":{},"unions":{}}})}});

}(this));

Uncaught TypeError: Cannot read property 'embed' of undefined

The elm component fails to render. The error in the console is the title of this bug report.

To reproduce:
1.

git clone https://github.com/richardkall/react-starter.git

mkdir elm && touch elm/Counter.elm

Add the following into Counter.elm:

{- This file re-implements the Elm Counter example (1 counter) with elm-mdl
   buttons. Use this as a starting point for using elm-mdl components in your own
   app.
-}


module Counter exposing (..)

import Html exposing (..)
import Html.Attributes exposing (href, class, style)
import Material
import Material.Scheme
import Material.Button as Button
import Material.Options as Options exposing (css)


-- MODEL


type alias Model =
    { count : Int
    , mdl :
        Material.Model
        -- Boilerplate: model store for any and all Mdl components you use.
    }


model : Model
model =
    { count = 0
    , mdl =
        Material.model
        -- Boilerplate: Always use this initial Mdl model store.
    }



-- ACTION, UPDATE


type Msg
    = Increase
    | Reset
    | Mdl (Material.Msg Msg)



-- Boilerplate: Msg clause for internal Mdl messages.


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Increase ->
            ( { model | count = model.count + 1 }
            , Cmd.none
            )

        Reset ->
            ( { model | count = 0 }
            , Cmd.none
            )

        -- Boilerplate: Mdl action handler.
        Mdl msg_ ->
            Material.update Mdl msg_ model



-- VIEW


type alias Mdl =
    Material.Model


view : Model -> Html Msg
view model =
    div
        [ style [ ( "padding", "2rem" ) ] ]
        [ text ("Current count: " ++ toString model.count)
          {- We construct the instances of the Button component that we need, one
             for the increase button, one for the reset button. First, the increase
             button. The first three arguments are:

               - A Msg constructor (`Mdl`), lifting Mdl messages to the Msg type.
               - An instance id (the `[0]`). Every component that uses the same model
                 collection (model.mdl in this file) must have a distinct instance id.
               - A reference to the elm-mdl model collection (`model.mdl`).

             Notice that we do not have to add fields for the increase and reset buttons
             separately to our model; and we did not have to add to our update messages
             to handle their internal events.

             Mdl components are configured with `Options`, similar to `Html.Attributes`.
             The `Options.onClick Increase` option instructs the button to send the `Increase`
             message when clicked. The `css ...` option adds CSS styling to the button.
             See `Material.Options` for details on options.
          -}
        , Button.render Mdl
            [ 0 ]
            model.mdl
            [ Options.onClick Increase
            , css "margin" "0 24px"
            ]
            [ text "Increase" ]
        , Button.render Mdl
            [ 1 ]
            model.mdl
            [ Options.onClick Reset ]
            [ text "Reset" ]
        ]
        |> Material.Scheme.top



-- Load Google Mdl CSS. You'll likely want to do that not in code as we
-- do here, but rather in your master .html file. See the documentation
-- for the `Material` module for details.


main : Program Never Model Msg
main =
    Html.program
        { init = ( model, Cmd.none )
        , view = view
        , subscriptions = always Sub.none
        , update = update
        }

Add this to elm/elm-package.json:

{
    "version": "1.0.0",
    "summary": "helpful summary of your project, less than 80 characters",
    "repository": "https://github.com/user/project.git",
    "license": "BSD3",
    "source-directories": [
        "."
    ],
    "exposed-modules": [],
    "dependencies": {
				"debois/elm-mdl": "8.1.0 <= v < 8.1.1",
        "elm-lang/core": "5.0.0 <= v < 6.0.0",
        "elm-lang/html": "2.0.0 <= v < 3.0.0"
    },
    "elm-version": "0.18.0 <= v < 0.19.0"
}

cd elm && elm-package install -y && elm-make Counter.elm --output Counter.js && cd ..

git apply diff.patch where diff.patch is this:

diff --git a/client/routes/About/index.js b/client/routes/About/index.js
index 2e90d21..2014569 100644
--- a/client/routes/About/index.js
+++ b/client/routes/About/index.js
@@ -1,12 +1,12 @@
 import React from 'react';

-import H1 from '../../components/H1';
+import Elm from 'react-elm-components'
+import { Counter } from '../../../elm/Counter'
+

 function About() {
   return (
-    <div>
-      <H1>About</H1>
-    </div>
+    <Elm src={ Counter }/>
   );
 }

diff --git a/package.json b/package.json
index 496c0d3..49306f0 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
     "morgan": "1.8.1",
     "react": "15.4.2",
     "react-dom": "15.4.2",
+    "react-elm-components": "^1.0.1",
     "react-router-dom": "4.0.0-beta.8",
     "rimraf": "2.6.1",
     "styled-components": "2.0.0-7",```

npm i && npm run build && npm run dev

Visit http://localhost:3000, click on the About link, and the error should be in the console.

This doesn’t occur when using the counter example from http://guide.elm-lang.org/
It might have something to do with elm-mdl.

Error when unmounting

Right now, the code uses the ref callback in React to get the node in order to embed the Elm component but after unmounting the callback gets called again, this time with null. This is by design https://facebook.github.io/react/docs/more-about-refs.html . The problem is that when it gets called with null it still tries to embed the Elm component throwing an error:

screenshot 2016-08-13 23 18 31

The component should either use some lifecycle method (like componentDidMount) or do a null check.

Thoughts?

Error when mounting component in two separate routes

Overview

If node is nonexistent in initialize, the following error occurs, and the application breaks:

Uncaught TypeError: Cannot read property 'appendChild' of null

This happens when switching routes with react-router that have the same elm component embedded. It's easy to fix, by just ignoring null nodes:

initialize = (node) => { if (node === null) { return; } /* do stuff */ }

But, I don't know if:

a) That really solves the root of the problem, or
b) it is a necessary addition, considering this library is meant as an example for implementors.

Ideas?

Demo maxes out CPU usage in Chrome for me

Chrome tends to do so when loading a new page anyway, but here it seems stuck. I recorded my desktop to show:

https://youtu.be/SPKh7dlDvI8

I wouldn't be surprised if it actually is a Chrome bug, but it's still annoying that it causes my laptop to freeze and ignore half of the input (including making it hard to close the tab). I don't have this issue with Firefox.

Running Ubuntu 16.04

How can I get my Elm to render in mocha

I know I can test Elm files with elm-test, but I'm also trying to run a small end to end test for my react file with the Elm component, the problem however is that the <Elm /> component in react-elm-components always renders an empty <div /> in mocha.

Any ideas on how to do an end to end test of a react component that's also rendering an Elm file?

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.