Cerebral

4 minute read Published:

Introduction to the new hotness in the world of React state management
Next article in series

Table of Contents

I want to preface this post with the following disclaimer:

I am not a fan of Redux. It became a de-facto standard in state management of React apps and seems to be working great for a lot of people, but I find it to be very verbose and hard to work with.

Ok. Now that it’s out of the way, let’s see what else exists in the world today that can help us maintain our application state and keep our sanity.

The project I’m going to discuss is called Cerebraland it was created by¬† Christian Alfoni,¬†Aleksey Guryanov and many others specifically to address the downsides of Flux and Redux.

I highly recommend reading Christian’s introduction article to Cerebral 2 to get a sense of the main differences between the frameworks.

In this post I’m going to make a small introduction to Cerebral by comparing the basic Counter example written using Redux to one in Cerebral.

In upcoming posts I’ll start introducing more advanced concepts and that’s where things will start getting really fun :)

Redux Counter

A simple Redux application consists of:

Entry point

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import counterApp from './reducer'
import Counter from './Counter'

let store = createStore(counterApp)

render(
  <Provider store={store}>
    <Counter />
  </Provider>,
  document.getElementById('root')
)

Reducer

export default (state = 0, action) => {
  switch (action.type) {
    case 'INCREASE':
      return state + 1
    case 'DECREASE':
      return state - 1
    default:
      return state
  }
}

Main component

import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { increase, decrease } from './actions'

const mapStateToProps = (state) => {
  return {
    count: state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onIncrease: () => {
      dispatch(increase())
    },
    onDecrease: () => {
      dispatch(decrease())
    }
  }
}

const Counter = ({ onIncrease, onDecrease, count }) => (
  <div>
    <button onClick={onIncrease}>+</button>
    {count}
    <button onClick={onDecrease}>-</button>
  </div>
)

Counter.propTypes = {
  onIncrease: PropTypes.func.isRequired,
  onDecrease: PropTypes.bool.isRequired,
  count: PropTypes.string.isRequired
}


export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

Actions

export const increase = () => {
  return {
    type: 'INCREASE'
  }
}

export const decrease = () => {
  return {
    type: 'DECREASE'
  }
}

And it works like follows: you define your actions separately, then define the ‘reaction’ to those actions in the reducer, i.e. how the state will be affected. Then you connect the component to the state.

Here’s the full project on WebpackBin

Cerebral Counter

A simple Cerebral application consists of:

Entry point

import React from 'react'
import {render} from 'react-dom'
import {Container} from 'cerebral/react'
import controller from './controller'
import App from './App'

render((
  <Container controller={controller}>
    <App />
  </Container>
), document.querySelector('#app'))

Controller

import {Controller} from 'cerebral'
import {set} from 'cerebral/operators'
import {state, string} from 'cerebral/tags'

function increase ({state}) {
  state.set('count', state.get('count') + 1)
}

function decrease ({state}) {
  state.set('count', state.get('count') - 1)
}

const controller = Controller({
  state: {
    count: 0
  },
  signals: {
     onIncrease: [increase],
     onDecrease: [decrease]
  }
})

export default controller

Main component

import React from 'react'
import {connect} from 'cerebral/react'
import {state, signal} from 'cerebral/tags'
export default connect({
  count: state`count`,
  onIncrease: signal`onIncrease`,
  onDecrease: signal`onDecrease`
},
function App ({ onIncrease, onDecrease, count }) {
  return (
   <div>
    <button onClick={() => onIncrease()}>+</button>
    {count}
    <button onClick={() => onDecrease()}>-</button>
  </div>
  )
})

And it works like follows: you define a controller that contains a state and a list of signals that are handled by it. Then you connect a component to specific state elements and signals and use them directly.

Here’s the full project on WebpackBin

As you can see there are quite a few differences here:

  1. You don’t need to pre-define actions.
  2. There is no “string” magic
  3. Code is much less verbose

And what you’ve seen here is just the absolute tip of the iseberg. Cerebral provides so much more! I hope to get into all of it in upcoming posts.

comments powered by Disqus