Pragmatic and concise state management.
- store modified via actions
- can be used as global store or local component state
- out of the box support for React and Preact
- first class React hooks support
- highly optimised with batched rerenders
- beautiful console logger
- battle tested and hardened
How is this different from redux? The key differences are:
- Actions in tiny-atom are self contained units of business logic. They can read and update the state and dispatch other actions any number of times. This removes layers of boilerplate while preserving the benefits of redux like stores.
- Tiny-atom includes useful utilities to make it completely sufficient for building application of any size.
Installation
$ npm install tiny-atom
Docs
Read the full docs or pick one of the highlights:
Example
import createAtom from 'tiny-atom'
const atom = createAtom(
{ unicorns: 0, rainbows: [] },
{
incrementUnicorns({ get, set }, n) {
set({ unicorns: get().unicorns + n })
},
async fetchRainbows({ set, actions }) {
set({ loading: true })
const { data: rainbows } = await axios.get('/api/rainbows')
set({ rainbows, loading: false })
actions.incrementUnicorns(1)
}
}
)
atom.observe(atom => {
console.log('atom', atom)
const { rainbows, unicorns } = atom.get()
render(unicorns).onClick(e => atom.actions.incrementUnicorns(10))
})
React Example
Provide the store:
import React from 'react'
import ReactDOM from 'react-dom'
import createAtom from 'tiny-atom'
import { Provider } from 'tiny-atom/react'
const atom = createAtom(
{ user: { name: 'Atom' } },
{
message({ get, set, swap, actions }, msg) {
console.log(msg)
}
}
)
ReactDOM.render(
<Provider atom={atom}>
<App />
</Provider>,
document.querySelector('root')
)
Connect using React hooks:
import React from 'react'
import { useAtom, useActions } from 'tiny-atom/react/hooks'
export default function Hello() {
const user = useAtom(state => state.user)
const { message } = useActions()
return <button onClick={() => message('hi')}>{user.name}</button>
}
API
createAtom(initialState, actions, options)
Create an atom.
initialState
type: any
default: {}
The initial state of the atom.
actions
type: object
default: {}
An object with action functions. The signature of an action function is ({ get, set, swap, actions, dispatch }, payload)
. If you provide nested action objects or other structure, make sure to also specify an appropriate options.evolve
implementation to handle your actions appropriately.
get()
- returns the current stateset(patch)
- updates the state with the patch object by merging the patch usingObject.assign
swap(state)
- replace the entire state with the provided onedispatch
- same asatom.dispatch
, dispatches an actionactions
- actions prebound to dispatch, i.e. actions.increment(1) is equivalent to dispatch('increment', 1)
options.evolve
type: function
A function that receives all of the dispatched action objects and calls the action functions. The function signature is (atom, action, actions)
. Note that atom
in this place has an extra added function set
, a function that is used to update the state, this function does not exist on the actual atom. The default implementation uses action.type
to find the matching function in the actions
object.
options.debug
type: function | function[]
default: null
A function that will be called on each action and state update. The function is passed an info
object of shape { type, atom, action, sourceActions, prevState }
. Tiny atom comes with 2 built in debug functions tiny-atom/log
and tiny-atom/devtools
.
createAtom(
{ count: 1 },
{
increment: ({ get, set }, payload) => set({ count: get().count + payload }),
inc: ({ actions }, payload) => actions.increment(payload)
}
)
atom.get()
Get current state.
atom.get()
atom.get().feed.items
atom.set(update)
Update state by shallowly merging an update.
atom.set({ user })
atom.set({ entities: { ...get().entities, posts } })
atom.swap(state)
Replace the entire state with the provided one.
atom.swap(nextState)
atom.dispatch(type, payload)
Send an action
atom.dispatch('fetchMovies')
atom.dispatch('increment', 5)
atom.actions
A map of prebound actions. For example, if your actions passed to atom are
const actions = {
increment({ get, set }) {
const { count } = get()
set({ count: count + 1 })
}
}
They will be bound such that calling atom.actions.increment(1)
dispatches action with `dispatch('increment', 1).
atom.observe(cb)
Register a callback for when atom changes. Returns the unobserve function.
atom.observe(render)
atom.observe(atom => render(atom.get(), atom.dispatch))
atom.fuse(state, actions)
Extend atom's state and the action object. Convenient for composing atom from slices of state and actions from several modules.
const state = {
project: { name: 'tiny-atom' }
}
const actions = {
star: ({ get, set }) =>
set({
project: { starred: true }
})
}
atom.fuse(state, actions)
React / Preact bindings
For documentation on the set of react and preact components <Provider />
, <Consumer />
, connect
, useAtom
, useActions
and useDispatch
see react or preact docs.