Skip to content

andrejewski/raj

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Raj

The Elm Architecture for JavaScript

npm install raj

npm Build Status Greenkeeper badge

Features

  • Understandable
    Raj is 34 lines; 190 bytes minified. This framework can fit in your head or even a tweet.

  • Testable
    Raj forces us to design for better separated concerns, simpler logic, and easier tests.

  • Minimal
    Raj provides a tiny foundation for libraries and applications.

  • Portable
    Raj is view layer agnostic. The view is a side effect of state.

Check out the homepage for resources and ecosystem packages.

Example

A counter that increments by one every time the user confirms.

import { runtime } from 'raj'

runtime({
  init: [0], // State is an integer to count
  update (message, state) {
    return [state + 1] // Increment the state
  },
  view (state, dispatch) {
    const keepCounting = window.confirm(`Count is ${state}. Increment?`)
    if (keepCounting) {
      dispatch()
    }
  }
})

Note: Raj is view layer agnostic. Here we use the browser's built-in view to play the part.

Architecture

Raj applications are structured as programs.

Every program begins with an initial state, which can be anything, and an optional effect. These are put into an array which is the init property of the program.

const init = [initialState, /* optional */ initialEffect]

"Effects" are functions which receive a function dispatch. Effects handle asynchronous work like data-fetching, timers, and managing event listeners. They can pass dispatch messages and Raj uses those to update the state.

function effect (dispatch) {
  // do anything or nothing; preferably something asynchronous
  // call dispatch 0, 1, or N times
  dispatch(message)
}

A "message" can be anything; a server response, the current time, even undefined.

When a message is dispatched, Raj passes that message and the current state to update. The update function returns a new state and optional effect. The business logic of the program is handled with this function.

function update (message, currentState) {
  return [newState, /* optional */ effect]
}

The view is a special effect that receives both the current state and the dispatch function. The view can return anything. For the React view layer, the view returns React elements to be rendered.

function view (currentState, dispatch) {
  // anything, depending on choice of view library
}

The init, update, and view form a "program" which is just an object with those properties:

const program = {
  init: [initialState, /* optional */ initialEffect],
  update (message, currentState) {
    return [newState, /* optional */ effect]
  },
  view (currentState, dispatch) {
    // anything, depending on choice of view library
  }
};

Building any program follows the same steps:

  1. Define the initial state and effect with init
  2. Define the state transitions and effects with update(message, state)
  3. Define the view with view(state, dispatch)
  4. Tie it all together into a program

Programs compose, so a parent program might contain child programs.

  • The parent's init may contain the child's init.
  • The parent's update may call the child's update with messages for the child and the child's state.
  • The parent's view may call the child's view with the child's state and dispatch.

In this way, programs most often compose into a tree structure.

The root program is passed to Raj's runtime. The runtime calls the program, manages its state, and runs its effects.

import { runtime } from 'raj'
import { program } from './app'

runtime(program)

The Raj by Example documentation covers this in greater detail.