Last active
March 23, 2017 08:08
-
-
Save mostr/00222aae6f645a53dddc5a228be59d0d to your computer and use it in GitHub Desktop.
State mgmt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function inc() { | |
return (getState, mergeState) => { | |
const currentCounter = getState().counter; | |
if(currentCounter === 5) { | |
console.log('5 reached, cannot inc') | |
return; | |
} | |
return mergeState({counter: currentCounter + 1}); | |
} | |
} | |
export function incAsync() { | |
return (getState, mergeState) => { | |
const currentCounter = getState().counter; | |
if(currentCounter === 5) { | |
console.log('5 reached, cannot inc async') | |
return; | |
} | |
setTimeout(() => mergeState({counter: getState().counter + 1}), 1000); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import {connect} from './statemgmt'; | |
import * as actions from './actions'; | |
const App = () => <Counter/> | |
export default App; | |
function _Counter(props) { | |
return ( | |
<div> | |
<h2>Current value: {props.current}</h2> | |
<hr/> | |
<button onClick={props.inc}>Inc</button> | |
<button onClick={props.incAsync}>Inc async</button> | |
</div> | |
) | |
} | |
function storeSlice(state) { | |
return { | |
current: state.counter | |
} | |
} | |
const Counter = connect(_Counter, storeSlice, actions) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import App from './App'; | |
import {createStore,Provider} from './statemgmt'; | |
const store = createStore({ | |
counter: 0 | |
}); | |
ReactDOM.render( | |
<Provider store={store}><App /></Provider>, | |
document.getElementById('root') | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
export function createStore(initialState) { | |
let state = initialState || Object.create(null); | |
let subscribedComponents = []; | |
return { | |
subscribe(component) { | |
subscribedComponents.push(component); | |
}, | |
unsubscribe(component) { | |
subscribedComponents = subscribedComponents.filter(c => c !== component); | |
}, | |
mergeState(newState) { | |
state = {...state, ...newState}; | |
subscribedComponents.forEach(c => c.forceUpdate()); | |
}, | |
getState() { | |
return state; | |
} | |
} | |
} | |
export class Provider extends React.Component { | |
static childContextTypes = { | |
store: React.PropTypes.object | |
}; | |
getChildContext() { | |
return {store: this.props.store} | |
} | |
render() { | |
return this.props.children; | |
} | |
} | |
export function connect(Component, storeProjection, actions) { | |
storeProjection = storeProjection || (() => {}); | |
if(storeProjection != null && typeof storeProjection !== 'function') { | |
throw new TypeError('Store projection must either be null or a function f(state, ownProps) => object'); | |
} | |
actions = actions || {}; | |
let nonFunctionActions = Object.keys(actions || {}).filter(key => typeof actions[key] !== 'function'); | |
if(nonFunctionActions.length) { | |
throw new TypeError(`The following actions are not functions: ${nonFunctionActions.join(', ')}`); | |
} | |
function augmentSingleAction(store, fn) { | |
return (...args) => { | |
const outer = fn(...args); | |
if(typeof outer !== 'function') { | |
throw new TypeError('Action must return a function f(getState, mergeState)'); | |
} | |
return outer(store.getState, store.mergeState); | |
} | |
} | |
function augmentActions(actions, store) { | |
return Object.keys(actions).reduce((result, key) => { | |
return {...result, [key]: augmentSingleAction(store, actions[key])}; | |
}, {}); | |
} | |
return class ConnectedComponent extends React.Component { | |
static contextTypes = { | |
store: React.PropTypes.object | |
}; | |
constructor(props, context) { | |
super(props, context); | |
this.store = context.store; | |
this.store.subscribe(this); | |
this.augmentedActions = augmentActions(actions, this.store); | |
} | |
componentWillUnmount() { | |
this.store.unsubscribe(this); | |
} | |
render() { | |
return <Component {...this.props} {...storeProjection(this.store.getState(), this.props)} {...this.augmentedActions}/> | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment