Storeon
A tiny event-based Redux-like state manager for React and Preact.
- Small. 179 bytes (minified and gzipped). No dependencies.
It uses Size Limit to control size.
- Fast. It track what state parts was changed and re-render only components
based on this state parts.
- Immutable. The same Redux reducers, but already with syntax sugar on top.
- Modular. API created to move business logic away from React components.
import createStore from 'storeon'
let increment = store => {
store.on('@init', () => ({ count: 0 }))
store.on('inc', ({ count }) => ({ count: count + 1 }))
}
export const store = createStore([increment])
import connect from 'storeon/react'
const Counter = ({ count, dispatch }) => <>
{count}
<button onClick={() => dispatch('inc')} />
</>
export default connect('count', Counter)
import { StoreContext } from 'storeon/react'
render(
<StoreContext.Provider value={store}>
<Counter></Counter>
</StoreContext.Provider>,
document.body
)
Usage
Store
The store should be created with createStore()
function. It accepts list
of the modules.
Each module is just a function, which will accept store
and bind their event listeners.
import createStore from 'storeon'
import projects from './projects'
import users from './users'
export const store = createStore([projects, users])
export default store => {
store.on('@init', () => ({ projects: [] }))
store.on('projects/add', ({ projects }, project) => {
return projects.concat([project])
})
}
The store has 3 methods:
store.get()
will return current state. The state is always an object.store.on(event, callback)
will add event listener.store.dispatch(event, data)
will emit event with optional data.
Events
There are three built-in events:
@init
will be fired in createStore
. The best moment to set an initial state.@dispatch
will be fired on every store.dispatch()
call.
It receives array with event name and event’s data.
Can be useful for debugging.@changed
will be fired every when event listeners changed the state.
It receives object with state changes.
To add an event listener, call store.on()
with event name and callback.
store.on('@dispatch', ([event, data]) => {
console.log(`Storeon: ${ event } with `, data)
})
store.on()
will return cleanup function. This function will remove
event listener.
const unbind = store.on('@changed', …)
unbind()
You can dispatch any other events. Just do not start event names with @
.
If the event listener returns an object, this object will update the state.
You do not need to return the whole state, return an object
with changed keys.
store.on('@init', () => ({ users: { } }))
Event listener accepts the current state as a first argument
and optional event object as a second.
So event listeners can be a reducer as well. As in Redux’s reducers,
you should change immutable.
store.on('users/save', ({ users }, user) => {
return {
users: { ...users, [user.id]: user }
}
})
store.dispatch('users/save', { id: 1, name: 'Ivan' })
You can dispatch other events in event listeners. It can be useful for async
operations.
store.on('users/add', async (state, user) => {
try {
await api.addUser(user)
store.dispatch('users/save', user)
} catch (e) {
store.dispatch('errors/server-error')
}
})
Components
You can bind the store to React and Preact component with connect()
decorator.
import connect from 'storeon/react'
const Users = ({ users, dispatch }) => {
const onAdd = useCallback(user => {
dispatch('users/add', user)
})
return <div>
{users.map(user => <User key={user.id} user={user} />)}
<NewUser onAdd={onAdd} />
</div>
}
export default connect('users', Users)
connect()
accept the list of state keys to pass into props
.
It will re-render only if this keys will be changed.
DevTools
Storeon supports debugging with Redux DevTools Extension.
const store = createStore([
…
process.env.NODE_ENV !== 'production' && require('storeon/devtools')
])
Or if you want to print events to console
you can use built-in logger.
It could be useful for simple cases or to investigate issue
on online error trackers.
const store = createStore([
…
process.env.NODE_ENV !== 'production' && require('storeon/logger')
])