Package Exports
- constate
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (constate) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
context + state = constate
Yet another React state management library that lets you work with local state and scale up to global state with ease.
Install
npm:
npm i -S constate
Yarn:
yarn add constate
Usage
Local state
You can start by using local state. Make the state global only when you need it.
Create your
State
component:// CounterState.js import React from 'react' import { State } from 'constate' export const initialState = { count: 0, } export const actions = { increment: amount => state => ({ count: state.count + amount }), } export const selectors = { getParity: () => state => (state.count % 2 === 0 ? 'even' : 'odd'), } const CounterState = props => ( <State initialState={initialState} actions={actions} selectors={selectors} {...props} /> ) export default CounterState
Wrap your component with
CounterState
:// CounterButton.js import React from 'react' import CounterState from './CounterState' const CounterButton = () => ( <CounterState> {({ count, increment, getParity }) => ( <button onClick={() => increment(1)}>{count} {getParity()}</button> )} </CounterState> ) export default CounterButton
Global state
Whenever you need to share state between components and/or feel the need to have a global state, just follow these steps:
Pass
context
property to yourState
components:// CounterButton.js import React from 'react' import CounterState from './CounterState' const CounterButton = () => ( <CounterState context="foo"> {({ increment }) => <button onClick={() => increment(1)}>Increment</button>} </CounterState> ) export default CounterButton
// CounterValue.js import React from 'react' import CounterState from './CounterState' const CounterValue = () => ( <CounterState context="foo"> {({ count }) => <div>{count}</div>} </CounterState> ) export default CounterValue
// CounterParity.js import React from 'react' import CounterState from './CounterState' const CounterParity = () => ( <CounterState context="foo"> {({ getParity }) => <div>{getParity()}</div>} </CounterState> ) export default CounterParity
Wrap your root component with
Provider
:// index.js import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'constate' import CounterButton from './CounterButton' import CounterValue from './CounterValue' import CounterParity from './CounterParity' const App = () => ( <Provider> <CounterButton /> <CounterValue /> <CounterParity /> </Provider> ) ReactDOM.render(<App />, document.getElementById('root'))
Overriding CounterState
properties
This is still React, so you can pass new properties to CounterState
, making it really composable.
First, let's change our CounterState
so as to receive new properties:
const CounterState = props => (
<State
{...props}
initialState={{ ...initialState, ...props.initialState }}
actions={{ ...actions, ...props.actions }}
selectors={{ ...selectors, ...props.selectors }}
/>
)
Now we can pass new initialState
, actions
and selectors
to CounterState
:
export const initialState = {
count: 10,
}
export const actions = {
decrement: amount => state => ({ count: state.count - amount }),
}
const CounterButton = () => (
<CounterState initialState={initialState} actions={actions}>
{({ decrement }) => <button onClick={() => decrement(1)}>Decrement</button>}
</CounterState>
)
Those new members will work even if you use context
.
Global initial state
You can also pass initialState
to Provider
:
const initialState = {
foo: {
count: 10,
},
}
const App = () => (
<Provider initialState={initialState}>
...
</Provider>
)
That way, components using context=foo
will have that initial state.
Testing
actions
and selectors
are pure functions. Testing is pretty straightfoward:
import { initialState, actions, selectors } from './CounterState'
test('initialState', () => {
expect(initialState).toEqual({ count: 0 })
})
test('actions', () => {
expect(actions.increment(1)({ count: 0 })).toEqual({ count: 1 })
expect(actions.increment(-1)({ count: 1 })).toEqual({ count: 0 })
})
test('selectors', () => {
expect(selectors.getParity()({ count: 0 })).toBe('even')
expect(selectors.getParity()({ count: 1 })).toBe('odd')
})
License
MIT © Diego Haz