Building A React/Redux/Elm Bridge

Elm’s ngReact

A. Sharif
A. Sharif
Oct 30, 2016 · 7 min read
elm-make Counter.elm --output=Counter.js
<div id="main"></div>
<script src="main.js"></script>
<script>
var node = document.getElementById('main');
var app = Elm.Main.embed(node);.
</script>
port module Counter exposing (..)
port check : Int -> Cmd msg
port counter : (Int -> msg) -> Sub msg
check 1
<script>
var node = document.getElementById('counter');
var app = Elm.Counter.embed(node);
app.ports.counter.send(3)
app.ports.check.subscribe(function(count) {
console.log('receiving data...', count);
})
</script>
<script>
// ...
app.ports.check.subscribe(function(count) {
app.ports.counter.send((count*3))
})
</script>
import React from 'react'
import
{ render } from 'react-dom'
import
Elm from 'react-elm-components'
import
{ Counter } from './Counter'
const setupPorts = ports => {
ports.check.subscribe(count => ports.counter.send((count*3)));
}
const CounterComponent = () =>
<Elm src={Counter} ports={setupPorts} />
render(<div>
<CounterComponent />
</div>, document.getElementById('app'))
port module Reducer exposing (..)import Redux
import Task exposing (..)
import Process
import Json.Encode as Json exposing ( object, int )

port
increment : ({} -> msg) -> Sub msg
port decrement : ({} -> msg) -> Sub msg

subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.batch
[ decrement <| always Decrement
, increment <| always Increment
]
-- MODELtype alias Model =
{ count : Int }
init : Int -> ( Model, Cmd Msg)
init count =
( { count = count }, Cmd.none )

encodeModel : Model -> Json.Value
encodeModel { count } =
object
[ ( "count", int count ) ]
-- VIEW


view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]

-- ACTIONS

type
Msg
= NoOp
| Increment
| Decrement

-- UPDATE

update : Msg -> Model -> ( Model, Cmd Msg )
update action model =
case action of
Increment ->
( { model | count = model.count + 1 }, Cmd.none )
Decrement ->
( { model | count = model.count - 1 }, Cmd.none )
NoOp ->
( model, Cmd.none )
main =
Redux.program
{ init = init 0
, update = update
, encode = encodeModel
, subscriptions = subscriptions
}
import React from 'react'
import
{ render } from 'react-dom'
import
{ applyMiddleware, createStore, combineReducers }
from 'redux'
import
{ connect, Provider } from 'react-redux'
import
{ compose } from 'ramda'
import
createElmMiddleware, { reducer as elmReducer }
from 'redux-elm-middleware'
const reducers = combineReducers({
elm: elmReducer,
})
const elmStore = window.Elm.Reducer.worker()
const {run, elmMiddleware} = createElmMiddleware(elmStore)
const store = createStore(reducers, {}, compose(
applyMiddleware(elmMiddleware),
))
run(store)
const Counter = ({ count = 0, Inc, Dec }) => (
<div>
<button onClick={Inc}>+</button>
<p>Current count: {count}</p>
<button onClick={Dec}>-</button>
</div>
)
const EnhancedCounter = connect(
({elm}) => ({ count: elm.count }),
dispatch => ({
Inc: () => dispatch({ type: 'INCREMENT' }),
Dec: () => dispatch({ type: 'DECREMENT' }),
}),
)(Counter)
render(
<Provider store={store}>
<EnhancedCounter />
</Provider>, document.getElementById('app')
)

Links

redux-elm-middleware

JavaScript Inside

All things JavaScript.

A. Sharif

Written by

A. Sharif

Focusing on quality. Software Development. Product Management.

JavaScript Inside

All things JavaScript.