loading...

Cerebral - Part 3, Signals and Actions

reflog profile image Eli Yukelzon Originally published at blog.reflog.me ・2 min read

Refactoring

In this post we'll extend our previous counter example by refactoring it a little bit.

Let's recall how the main controller looked before:


import {Controller} from 'cerebral'

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]
  }
})

Let's try to understand what's happening here exactly.

a. Inside the controller we define two signals: onIncrease and onDecrease.
b. For each signal, we reference a function that will handle that signal
c. That function (increase/decrease) will receive a parameter called 'context'. It will contain all sorts of useful information, but since all we need from it is our 'state', we do ES6 'desctructuring' by applying {state} as it's parameter

Now, let's refactor this code a little bit, since both increase and decrease functions basically have the same code. How can we do that?

Here's one way: Let's pass a parameter to the signal, which will select the direction of counter change. This way we'll be able to use the same function for both cases.

Now our code will look like this:


import {Controller} from 'cerebral'

function changeCounter({props, state}){
  state.set("count", state.get("count") + props.param);
}

const controller = Controller({
  state: {
    count: 0
  },
  signals: {
     changeCounter
  }
})

export default controller

There's two things to notice here:
a. We now destructure two parameters to our handler: {state, props}. The props parameter is an object containing all paramers passed down to the signal
b. We no longer need two signals!

Now that we changed the controller, here's how the component will look:


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


Nice and clean!

Signal Chains

Did you notice that when we defined the signal handlers, they were initially passed as an array? The reason for that is a neat one - Cerebral's signals trigger chains of actions.

This awesome feature allows you to group together several actions that need to happen one after the other when a certain signal is triggered.

Let's see a small example of how it's done.

I'm going to add a log action that will write to console the new value of the counter on each change.


import {Controller} from 'cerebral'

function changeCounter({props, state}){
  state.set("count", state.get("count") + props.param);
}

function logAction({state}){
  console.log(`counter changed to ${state.get("count")}`)
}

const controller = Controller({
  state: {
    count: 0
  },
  signals: {
     changeCounter: 
     [
       changeCounter, 
       logAction
     ]
  }
})

export default controller

Now, each time you trigger a change - you'll see a nice log message!

The nice thing about actions, signals and chains is that they are all visible in the Debugger. Take a look:

DEBUGGER

Here's the full project on WebpackBin

In the next part of these series, I'll discuss the next amazing Cerebral feature - 'tags'.

Discussion

pic
Editor guide