Elm Language was specifically designed to build front-end applications, and it offers a great experience as a whole. Its most shiny features are:
- Great performance using the virtual DOM
- Functional language
- Safety through a Powerful type system
- State management using the built-in Elm architecture
- A powerful and helpful compiler
We will see how to achieve 3, 4 and 5 using React, Redux and Flow.
Let's look at some Elm code:
import Html exposing (Html, button, div, text) import Html.Events exposing (onClick) main = Html.beginnerProgram { model = model , view = view , update = update } -- MODEL type alias Model = Int model : Model model = 0 -- UPDATE type Msg = Increment | Decrement update : Msg -> Model -> Model update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 -- VIEW view : Model -> Html Msg view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
https://gist.github.com/haikyuu/abd1145667e02aee2332d55e0d99bd33
The beauty of Elm is that every Elm app is written using the same parts:
Model: what is the state of your app
Update: how to update the state
View: how to display the state
The syntax is a bit weird for newcomers but it just takes some getting used to.
And its equivalent (commented) using React, Redux and Flow:
//@flow import React from 'react'; import ReactDOM from 'react-dom'; import { createStore, bindActionCreators } from 'redux' import { Provider, connect } from 'react-redux' type Increment = { type: "INCREMENT" } type Decrement = { type: "DECREMENT" } type CounterActions = | Increment | Decrement type Action = CounterActions // whenever we add a reducer, we add its actions here //make sure dispatch can only dispatch our predefined actions //and returns the action dispatched type domainIdentity<T> = <A: T>(a: A) => A type Dispatch = domainIdentity<Action> type CounterState = number //reducer const counter = (state: CounterState = 0, action: CounterActions) =>{ switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: (action: empty); //to make sure we handle all the actions //it will trigger a flow error when we forget a case !! return state } } //action creators const increment = (): Increment => ({ type: "INCREMENT" }) const decrement = (): Decrement => ({ type: "DECREMENT" }) // our global state, also needs to be modified whenever we add a reducer type State = CounterState // passing state to our component as props const mapStateToProps = (state: State) => ({state}) const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({ increment, decrement, }, dispatch) //helper flow types .. should be in a config file type _ExtractReturn<B, F: (...args: any[]) => B> = B; /*export*/ type ExtractReturn<F> = _ExtractReturn<*, F>; //redux state and action creators types type ReduxProps = ExtractReturn<typeof mapStateToProps>; type ReduxActions = ExtractReturn<typeof mapDispatchToProps>; //View //we don't have any props except redux ones type Props = {} & ReduxProps & ReduxActions const App = ({ decrement, increment, state }: Props) =>( <div> <button onClick={decrement}>-</button> <div>{state}</div> <button onClick={increment}>+</button> </div> ) //connect our component with the redux store const ConnectedApp = connect(mapStateToProps, mapDispatchToProps)(App) let store = createStore(counter) ReactDOM.render( (<Provider store={store}> <ConnectedApp /> </Provider>), document.getElementById('root') );
https://gist.github.com/haikyuu/a540268b3336a52de69ae94d8e3b9e3c
A few things to note:
Elm’s type system is much more powerful and descriptive than Flow (and typescript)
The Elm architecture is integrated in the language level, so you can achieve more with less code in Elm compared to how much boilerplate you need to write in Redux.
Redux was inspired from Elm architecture
If you cannot use Elm in your day job, you can still benefit from similar goodies using Flow.
You will also become a better React developer by learning Elm, so check it out.
by Alaoui Abdellah