@cerebral/mobx-state-tree

Combine Cerebral with Mobx state-tree.

Install 

npm install @cerebral/mobx-state-tree

Description 

Use the mobx-state-tree project as state layer of Cerebral. Use mobx project way of connecting to components and ignore using computed in Cerebral.

Instantiate 

Instead of creating the controller from cerebral, you create it from this package. Also any modules should be created from this package:

import { Controller, Module } from '@cerebral/mobx-state-tree'

const app = Module({
  // definition
})

export default Controller(app, {
  // options
})

considerations 

There are a couple of minor things to consider when using this addon.

  1. Any addons defining state, like @cerebral/useragent, will not work with this addon, due to the missing types definitions. Though for example cerebral/router will work as it does not have any state

  2. Avoid using types.reference, rather define it as a string and do manual lookup. The reason is simply that the debugger does not understand reference, though it works perfectly fine

state and model 

With Mobx state tree you also define models, in addition to state. You do this directly in your modules:

modules/app.js

import { Module } from '@cerebral/mobx-state-tree'
import { types } from 'mobx-state-tree'
import { set } from 'cerebral/operators'
import { state, props } from 'cerebral/tags'

export default Module({
  // Define your model as an object,
  // it will be converted to a mobx model
  model: {
    name: types.string
  },
  // Define the initial state as normal
  state: {
    name: ''
  },
  // Define getters
  // store.upperName // "BAR"
  getters: {
    upperName() {
      return this.name.toUpperCase()
    }
  },
  // Define computed (receives a value to compute)
  // store.sayHelloTo("someName")
  computed: {
    sayHelloTo(name) {
      return `Hello ${this.name}, I am ${name}`
    }
  },
  signals: {
    nameChanged: set(state`name`, props`name`)
  }
})

Note! Instead of exposing views like mobx-state-tree, we expose getters and computed. The reason is to improve the declarativeness of the code. Meaning that when you open the module file you will see all state, getters and computed defined. No file jumping or implementation details in the module file needed.

accessing state and props in components 

For example using React you will need the package mobx-react and use the provide() method of the controller:

main.js

import React from 'react'
import { render } from 'react-dom'
import controller from './controller'
import { Provider } from 'mobx-react'
import App from './components/App'

render(
  <Provider {...controller.provide()}>
    <App />
  </Provider>
)

App.js

import React from 'react'
import { observer, inject } from 'mobx-react'

@inject('store', 'signals')
@observer
class App extends React.Component {
  render () {
    const { store, signals } = this.props

    return (
      <div>
        <h1>{store.sayHelloTo('Bob')}</h1>
        <input value={store.name} onChange={event => signals.nameChanged({ name: event.target.value })}
      </div>
    )
  }
}

export default App