The module is how you structure your application, it holds:
{
state,
sequences,
reactions,
providers,
catch,
modules,
}
You instantiate your application with a root module:
const app = App({
state: {}
})
And you extend this root module with nested modules:
const app = App({
state: {},
modules: {
moduleA,
moduleB
}
})
The state object contains the module’s local state:
{
state: {
users: [],
currentUserId: null,
isLoading: false
}
}
States from child modules are merged into a single state tree.
Sequences define the logic flows of your application:
{
sequences: {
loadUsers: [
set(state`isLoading`, true),
getUsers,
set(state`users`, props`users`),
set(state`isLoading`, false)
]
}
}
Reactions let you respond to state changes:
import { Reaction } from 'cerebral'
{
reactions: {
watchAuthentication: Reaction(
{ isAuthenticated: state`user.isAuthenticated` },
({ isAuthenticated, get }) => {
if (isAuthenticated) {
get(sequences`loadDashboard`)()
}
}
)
}
}
Providers expose functionality to your actions:
{
providers: {
api: {
getUsers() {
return fetch('/api/users').then(res => res.json())
}
},
logger: {
log(message) {
console.log(`[APP]: ${message}`)
}
}
}
}
The catch
property handles errors that occur in the module’s sequences:
import { ApiError, ValidationError } from './errors'
{
catch: [
[ApiError, sequences.handleApiError],
[ValidationError, sequences.handleValidationError],
[Error, sequences.handleGenericError]
]
}
Each handler is an array with:
Errors not caught by a module will propagate to parent modules.
Child modules are registered using the modules
property:
import adminModule from './modules/admin'
import settingsModule from './modules/settings'
{
modules: {
admin: adminModule,
settings: settingsModule
}
}
You can define modules in two ways:
const myModule = {
state: {
isLoading: false,
items: []
},
sequences: {
loadItems: [
set(state`isLoading`, true),
getItems,
set(state`items`, props`items`),
set(state`isLoading`, false)
]
}
}
You can also create modules using a function that receives information about the module:
const myModule = ({ name, path, app }) => ({
state: {
moduleName: name,
modulePath: path
},
sequences: {
initialize: [
({ store }) => {
store.set(state`initialized`, true)
}
]
}
})
The module factory receives:
name
: The module namepath
: The full path to the module (e.g., “app.settings”)app
: Reference to the app instanceModules create a hierarchy that affects how paths are resolved:
// Root module
{
state: { title: 'My App' },
modules: {
users: {
state: { list: [] }
}
}
}
This creates a state tree:
{
title: 'My App',
users: {
list: []
}
}
To access the users list:
// In components or actions
get(state`users.list`)
The moduleState
tag lets you refer to state within the current module:
import { moduleState } from 'cerebral'
function toggleLoading({ store }) {
// If in "users" module, this will toggle "users.isLoading"
store.toggle(moduleState`isLoading`)
}
Errors thrown in a module’s sequences can be caught with the catch
property:
catch: [
[HttpError, sequences.handleHttpError],
[ValidationError, sequences.handleValidationError],
[Error, sequences.handleGenericError]
]
If an error isn’t caught by the current module, it propagates up to parent modules until handled or reaching the root module.