Exim

Lightweight architecture for React

@jhabdas

~3000 stargazers

~4000 stargazers

~750 stargazers

Architecture

  • Built on React + Flux
  • Modern MVC replacement
  • Unidirectional data flow
  • Markup and logic in one file
  • Shadow DOM
  • Synthetic events

Problems with Flux

 

Unusable for real apps

For each line of code

you write 1-2 lines of boilerplate

Horrible ecosystem [unlike in React]

Hard to just start developing today

Feels incomplete

Hello Exim

  • Simple structure
  • Intelligible router
  • First-class JS / Coffee
  • Short syntax
  • Lightweight (no jQuery)
  • Great conventions

 

Differences from pure Flux

  • No constants
  • No dispatcher
  • One-line actions
  • Reengineered Stores
  • Helpers for CoffeeScript
  • Grunt, Gulp, Brunch boilerplates

Exim vs Flux

Flux

Exim

Code examples

Actions

actions = Exim.createActions(['work', 'drink'])

People = Exim.createStore
  actions: actions

  willWork: -> # some
  onWork:   -> # function
  didWork:  -> # body

  # Alternative form.
  eat:
    will: -> # goes
    on:   -> # right
    did:  -> # here

Users Store

Users = Exim.createStore
  actions: actions

  init: ->
    meal = friend = food = ''
    @update {meal, friend, food}

  onEat: (food) ->
    @update {meal: 'Brunch', friend: 'Chaplin', food}

  onDrink: (drink) ->
    @update
      meal: 'Brunch'
      friend: 'Chaplin'
      food: "liquid #{drink}"

User View

# Backed by Users Store
UserView = Exim.createView
  mixins: [Exim.connect(Users)]

  render: ->
    {food, friend, type} = @state
    {div, h2} = Exim.DOM

    div className: 'user-view',
      h2 "Eatr"

      div {},
        "Eating some #{food} with #{friend} over #{type}"

Layout View

Sidebar = require('components/sidebar')

{div} = Exim.DOM

# Two-pane layout view with Sidebar component
TwoPane = Exim.createView
  render: ->
    div className: 'layout',
      Sidebar()
      @props.activeRouteHandler()

Routes

# Define routes
{startHistory, match} = Exim.Router
routes = startHistory
  match 'app', App, path: '/',
    match 'feedback', FeedbackPage  # Each route handler is a React view
    match 'terms', TermsPage        # @props.activeRouteHandler()
    match 'privacy', PrivacyPage    # is passed to each view.

    match TwoPane,
      match 'calendar', Calendar
      MessagesRoutes  # Easy nesting!

MessagesRoutes = match 'messages', Messages,
  match 'new', Compose, path: 'new'
  match 'conversation', Conversation, path: ':id'

# Launch app
React.render(routes, document.body)

Demo app

Made with Slides.com