medux
v1.2.0
Published
Medux: Modular immutable state management
Readme
Medux: Modular immutable state management
Manage state and actions using immer
Getting Started
yarn add meduxStore
import { createStore } from 'medux'
const storeProps = {
createState: () => ({
count: 0
}),
actions: {
increment(num = 1) {
// `this` has getState, setState, actions
// Produce new state by modifying draft
this.setState(draft => {
draft.count += num
})
// ..or merge into new state
this.setState({
count: this.state.count + 1
})
// Optionally call other actions
this.actions.action()
},
// Action can be async
async action(props) {}
}
}
const store = createStore(storeProps)
store.on('action', (key, props) =>{
console.log('Action called', key, props)
console.log('State changed', store.state)
})
store.actions.increment(5)
console.log(store.state) // { count: 5 }Immutability
const oldState = store.state
store.actions.increment(5)
const newState = store.state // oldState !== newState
store.state.count++ // Error, must use setState or actionCore
If immutability is not needed, there's a much smaller core that can be imported.
import { createStore } from 'medux/core'It has the same interface, but instead of using immer, it will mutate the store's state with Object.assign. This is simpler and faster, but does not have the benefits of immutability.
Context
For the createStore function, one can pass a property context with an object.
const storeContext = {}
createStore({
context: storeContext
})This is available to actions as this.context.
Events
The store instance is an event emitter with methods on, off, and emit.
They can be accessed from actions as this.on, this.off, this.emit.
const handler = data => {}
// Subscribe
store.on('eventName', handler)
// Unsubscribe
store.off('eventName', handler)
// Emit event
const data = {}
store.emit('eventName', data)The on function returns an unsubscriber, which can be called with no arguments.
Child stores
const store = createStore({
createState: () => ({
...parentState,
child: childState
}),
actions: {
...parentActions,
child: childActions
}
})
store.actions.child.increment(5)
console.log(store.state.child) // { count: 5 }Compose stores
The method composeStore creates a store from an array of store props.
This is a way to merge sets of states and actions.
import { composeStore } from 'medux' // or medux/core
const feature = {
state: {},
actions: {}
}
const anotherFeature = {
state: {},
actions: {}
}
const store = composeStore([
feature,
anotherFeature
], context)Redux DevTools
import { connectReduxDevTools } from 'medux/redux-devtools'
const store = useStore(storeProps)
connectReduxDevTools(store)React
The method useStore connects a component to a store, to render on state changes.
import { useStore } from 'medux/react'
const Component = () => {
const store = useStore(storeProps)
const { state, actions } = store
return <button onClick={() => actions.increment()}>
Increment: { state.count }
</button>
}It also accepts an array of store props, in which case their states and actions will be merged using composeStore.
The store can also be created outside the component, and passed to useStore.
import { createStore } from 'medux'
import { useStore } from 'medux/react'
const store = createStore(storeProps)
const Component = () => {
useStore({ store })
const { state, actions } = store
return <button onClick={() => actions.increment()}>
Increment: { state.count }
</button>
}React with Redux DevTools
import { useEffect } from 'react'
import { useStore } from 'medux/react'
import { connectReduxDevTools } from 'medux/redux-devtools'
const Component = () => {
const store = useStore(storeProps)
useEffect(() => {
connectReduxDevTools(store)
}, [])
return ..
}With options for Redux DevTools
connectReduxDevTools(store, options)With store instance name
connectReduxDevTools('App', store, options)Develop this library
Install dependencies
yarnDevelop: Watch files; Recompile, type check and test on changes
yarn devBuild
yarn buildPublish to NPM
npm run release