56

Achieve more with Less code, Elm vs React-Redux-Flow

 5 years ago
source link: https://www.tuicool.com/articles/hit/ZFZjIfA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Elm Language was specifically designed to build front-end applications, and it offers a great experience as a whole. Its most shiny features are:

  1. Great performance using the virtual DOM
  2. Functional language
  3. Safety through a Powerful type system
  4. State management using the built-in Elm architecture
  5. 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.

Alaoui Abdellah

React and React Native developer. Loves Flow and currently exploring OCaml and ReasonML. Curious and eager to learn.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK