Reactions allow you to respond to state changes outside the normal rendering flow. This guide covers common usage patterns for reactions in different scenarios.
Reactions are useful for:
When creating a module, you can attach reactions that respond to state changes:
// modules/admin.js
import { Reaction } from 'cerebral'
import { state, sequences } from 'cerebral'
// Define reactions in a separate file
export const watchPermissions = Reaction(
{ permissions: state`user.permissions` },
({ permissions, get }) => {
if (!permissions.includes('admin')) {
get(sequences`redirectToHome`)()
}
}
)
This pattern allows you to define side effects that run automatically when specific state changes.
Components often need to react to state changes for side effects like focus management:
import * as React from 'react'
import { connect } from '@cerebral/react'
import { state, sequences } from 'cerebral'
export default connect(
{
inputValue: state`form.inputValue`,
changeInputValue: sequences`changeInputValue`
},
function FormInput({ inputValue, changeInputValue, reaction }) {
const inputRef = React.useRef(null)
// The reaction prop is provided by the connect HOC
reaction(
'focusOnError', // Name for debugging
{ error: state`form.error` },
({ error }) => {
if (error && inputRef.current) {
inputRef.current.focus()
}
}
)
return (
<input
ref={inputRef}
value={inputValue}
onChange={(event) => changeInputValue({ value: event.target.value })}
/>
)
}
)
Reactions can depend on computed values:
import { Reaction } from 'cerebral'
import { state } from 'cerebral'
// Define a computed value
export const filteredItems = (get) => {
const items = get(state`items`)
const filter = get(state`filter`)
return items.filter((item) => item.type === filter)
}
// Create app state with the computed
const appState = {
items: [],
filter: 'all',
filteredItems
}
// Create a reaction to the computed
export const onFilteredItemsChange = Reaction(
{ filteredItems: state`filteredItems` },
({ filteredItems }) => {
console.log('Filtered items changed:', filteredItems.length)
}
)
Reactions provide a get
function to access state not declared in dependencies:
Reaction({ isLoggedIn: state`user.isLoggedIn` }, ({ isLoggedIn, get }) => {
if (isLoggedIn) {
// Only access these values when needed
const username = get(state`user.name`)
const permissions = get(state`user.permissions`)
// Use the dynamically accessed values
analytics.identify(username, { permissions })
}
})
A single reaction can depend on multiple state paths:
Reaction(
{
filter: state`items.filter`,
sort: state`items.sort`,
items: state`items.list`
},
({ filter, sort, items }) => {
// This runs when filter, sort, or items change
const processed = processItems(items, filter, sort)
localStorage.setItem('processedItems', JSON.stringify(processed))
}
)