cerebral
Declarative state and side effects management for popular JavaScript frameworks
Top Related Projects
A JS library for predictable global state management
Simple, scalable state management.
🗃️ Centralized State Management for Vue.js.
Reactive State for Angular
Application Architecture for Building User Interfaces
A reactive programming library for JavaScript
Quick Overview
Cerebral is a state management library for JavaScript applications. It provides a declarative and centralized approach to managing application state, side effects, and business logic. Cerebral aims to simplify complex state management scenarios and improve developer productivity.
Pros
- Centralized state management with a single state tree
- Powerful debugging tools with time-travel debugging
- Supports both React and Vue.js frameworks
- Modular architecture allowing for easy scaling of large applications
Cons
- Steeper learning curve compared to simpler state management solutions
- Requires additional setup and configuration
- May be overkill for small to medium-sized applications
- Less community support compared to more popular state management libraries
Code Examples
- Defining a state tree and actions:
import { Controller, Module } from 'cerebral'
const app = Module({
state: {
count: 0
},
signals: {
increment: ({ state }) => state.set('count', state.get('count') + 1),
decrement: ({ state }) => state.set('count', state.get('count') - 1)
}
})
const controller = Controller(app)
- Using Cerebral with React:
import React from 'react'
import { connect } from '@cerebral/react'
const Counter = connect(
{
count: state`count`,
increment: signal`increment`,
decrement: signal`decrement`
},
function Counter({ count, increment, decrement }) {
return (
<div>
<h1>{count}</h1>
<button onClick={() => increment()}>+</button>
<button onClick={() => decrement()}>-</button>
</div>
)
}
)
- Defining and using computed state:
import { Compute } from 'cerebral'
const doubleCount = Compute(
state`count`,
(count) => count * 2
)
const DoubleCounter = connect(
{
doubleCount
},
function DoubleCounter({ doubleCount }) {
return <h2>Double count: {doubleCount}</h2>
}
)
Getting Started
To get started with Cerebral, follow these steps:
-
Install Cerebral and its React bindings:
npm install cerebral @cerebral/react
-
Create a Cerebral controller:
import { Controller, Module } from 'cerebral' const app = Module({ state: { // Your initial state }, signals: { // Your signals (actions) } }) const controller = Controller(app)
-
Wrap your React app with the Cerebral provider:
import { Container } from '@cerebral/react' ReactDOM.render( <Container controller={controller}> <App /> </Container>, document.getElementById('root') )
-
Connect your components to the Cerebral state:
import { connect } from '@cerebral/react' export default connect( { // Map state and signals }, function YourComponent(props) { // Your component logic } )
Competitor Comparisons
A JS library for predictable global state management
Pros of Redux
- Larger ecosystem and community support
- More widespread adoption, leading to better documentation and resources
- Simpler learning curve for developers familiar with Flux architecture
Cons of Redux
- More boilerplate code required for setup and state management
- Can become complex in large applications with many actions and reducers
- Less opinionated, which may lead to inconsistent implementations across projects
Code Comparison
Redux:
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
};
Cerebral:
export const increment = ({ state }) => {
state.set('count', state.get('count') + 1);
};
Redux typically uses switch statements in reducers to handle actions, while Cerebral uses a more declarative approach with state mutations. Cerebral's syntax is often more concise, but Redux's explicit nature can make it easier to understand the flow of data in larger applications.
Both libraries aim to simplify state management in complex applications, but they take different approaches. Redux focuses on predictability and a single source of truth, while Cerebral emphasizes declarative state changes and side-effect management. The choice between them often depends on project requirements and team preferences.
Simple, scalable state management.
Pros of MobX
- Simpler learning curve and more intuitive API
- Better performance for large-scale applications
- More flexible and less opinionated about application structure
Cons of MobX
- Less predictable state changes due to mutable state
- Potential for overuse of observables, leading to unnecessary re-renders
- Lack of built-in time-travel debugging
Code Comparison
MobX:
import { makeObservable, observable, action } from "mobx";
class TodoStore {
todos = [];
constructor() {
makeObservable(this, {
todos: observable,
addTodo: action
});
}
addTodo(text) {
this.todos.push({ text, completed: false });
}
}
Cerebral:
import { state, sequence } from 'cerebral'
export const state = {
todos: []
}
export const addTodo = sequence('Add todo', [
({ state, props }) => {
state.todos.push({ text: props.text, completed: false })
}
])
Both MobX and Cerebral are state management libraries for JavaScript applications, but they have different approaches. MobX focuses on reactive programming and uses observables, while Cerebral uses a more explicit signal-state-model architecture. MobX is generally easier to learn and integrate into existing projects, while Cerebral provides a more structured approach to state management.
🗃️ Centralized State Management for Vue.js.
Pros of Vuex
- Tightly integrated with Vue.js ecosystem, providing seamless state management for Vue applications
- Simple and intuitive API, making it easy to learn and use for developers familiar with Vue
- Extensive documentation and large community support
Cons of Vuex
- Limited to Vue.js applications, not suitable for other frameworks or vanilla JavaScript
- Can become complex in large applications with many modules and nested states
Code Comparison
Vuex:
const store = new Vuex.Store({
state: { count: 0 },
mutations: {
increment(state) { state.count++ }
}
})
Cerebral:
const controller = Controller({
state: { count: 0 },
signals: {
increment: [({ state }) => state.set('count', state.get('count') + 1)]
}
})
Both Vuex and Cerebral provide state management solutions, but Vuex is specifically designed for Vue.js applications, while Cerebral is framework-agnostic. Vuex offers a more straightforward API for simple use cases, whereas Cerebral provides a more flexible and powerful approach to complex state management scenarios.
Reactive State for Angular
Pros of NgRx
- Tightly integrated with Angular, providing a seamless experience for Angular developers
- Extensive ecosystem with additional libraries like @ngrx/effects and @ngrx/entity
- Strong community support and regular updates
Cons of NgRx
- Steeper learning curve, especially for developers new to Redux concepts
- More boilerplate code required compared to Cerebral
- Can be overkill for smaller applications
Code Comparison
NgRx:
@Component({...})
export class MyComponent {
data$ = this.store.select(selectData);
constructor(private store: Store) {}
loadData() {
this.store.dispatch(loadData());
}
}
Cerebral:
connect({
data: state`data`,
loadData: signal`loadData`
},
function MyComponent({ data, loadData }) {
return <button onClick={() => loadData()}>Load Data</button>
}
Both NgRx and Cerebral are state management solutions, but they differ in their approach and ecosystem. NgRx is specifically designed for Angular applications, while Cerebral is framework-agnostic. NgRx follows a more traditional Redux pattern, whereas Cerebral uses a unique signal-state model. The code comparison shows how actions/signals are dispatched in each library, with NgRx requiring more setup but offering tighter TypeScript integration.
Application Architecture for Building User Interfaces
Pros of Flux
- Simpler learning curve due to its straightforward unidirectional data flow concept
- Better integration with React ecosystem, being developed by Facebook
- More widespread adoption and larger community support
Cons of Flux
- Less flexibility in handling complex state management scenarios
- Lack of built-in tools for handling side effects and asynchronous operations
- Requires more boilerplate code for larger applications
Code Comparison
Flux:
var AppDispatcher = require('./AppDispatcher');
var TodoConstants = require('./TodoConstants');
var TodoActions = {
create: function(text) {
AppDispatcher.dispatch({
actionType: TodoConstants.TODO_CREATE,
text: text
});
}
};
Cerebral:
import {set} from 'cerebral/operators'
import {state} from 'cerebral/tags'
export const createTodo = ({props, store}) => {
store.set(state`todos.${props.id}`, props.todo)
}
Cerebral offers a more declarative approach to state management, with built-in operators and tags for easier state manipulation. Flux, on the other hand, relies on a more traditional dispatch-based system, which can be more verbose but is also more familiar to developers coming from other frameworks.
A reactive programming library for JavaScript
Pros of RxJS
- Widely adopted and mature ecosystem with extensive documentation
- Powerful operators for complex data transformations and event handling
- Cross-platform support (browser, Node.js, and mobile)
Cons of RxJS
- Steeper learning curve due to its functional reactive programming paradigm
- Can lead to increased complexity for simple use cases
- Potential performance overhead for large-scale applications
Code Comparison
RxJS:
import { fromEvent } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';
fromEvent(input, 'input')
.pipe(
debounceTime(300),
map(event => event.target.value)
)
.subscribe(value => console.log(value));
Cerebral:
import { state, sequence } from 'cerebral';
export const updateInput = sequence('updateInput', [
({ props, store }) => {
store.set(state.inputValue, props.value);
},
]);
RxJS excels in handling complex event streams and asynchronous operations, while Cerebral provides a more structured approach to state management. RxJS offers greater flexibility but may require more boilerplate, whereas Cerebral aims for simplicity in defining state changes. The choice between the two depends on the specific requirements of your project and your team's familiarity with reactive programming concepts.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Cerebral
A declarative state and side effects management solution for popular JavaScript frameworks
Maintainer needed
https://gist.github.com/christianalfoni/f1c4bfe320dcb24c403635d9bca3fa40
Documentation
Contribute
The entire Cerebral codebase has been rewritten to encourage contributions. The code is cleaned up, commented and all code is in a "monorepo". That means you can run tests across projects and general management of the code is simplified a lot.
- Clone the monorepo:
git clone https://github.com/cerebral/cerebral.git
- In root:
npm install
The packages are located under packages
folder and there is no need to run npm install
for each package.
Using monorepo for your own apps
If you want to use Cerebral 2 directly from your cloned repo, you can create a symlinks for following
directories into the node_modules
directory of your app:
packages/node_modules/cerebral
packages/node_modules/function-tree
packages/node_modules/@cerebral
If your app and the cerebral monorepo are in the same folder you can do from inside your app directory:
$ ln -s ../../cerebral/packages/node_modules/cerebral/ node_modules/
# ...
Just remember to unlink the package before installing it from npm:
$ unlink node_modules/cerebral
# ...
Running demos
Go to the respective packages/demos/some-demo-folder
and run npm start
Testing
You can run all tests in all packages from root:
npm test
Or you can run tests for specific packages by going to package root and do the same:
npm test
Changing the code
When you make a code change you should create a branch first. When the code is changed and backed up by a test you can commit it from the root using:
npm run commit
This will give you a guide to creating a commit message. Then you just push and create a pull request as normal on Github.
Release process
- Review and merge PRs into
next
branch. It is safe to use "Update branch", the commit created by Github will not be part ofnext
history - If changes to
repo-cooker
, clean Travis NPM cache - From command line:
$ git checkout next
$ git pull
$ npm install # make sure any new dependencies are installed
$ npm install --no-save repo-cooker # needed to test release, make sure you have latest
$ npm run release # and check release notes
$ git checkout master
$ git pull
$ git merge --ff-only next
$ git push
Top Related Projects
A JS library for predictable global state management
Simple, scalable state management.
🗃️ Centralized State Management for Vue.js.
Reactive State for Angular
Application Architecture for Building User Interfaces
A reactive programming library for JavaScript
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot