-
-
Save anthonybrown/a86040d8d8ab82bf5351fe89eb2c898e to your computer and use it in GitHub Desktop.
A Redux-like Flux implementation in <75 lines of code
This file contains 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
/** | |
* Basic proof of concept. | |
* - Hot reloadable | |
* - Stateless stores | |
* - Stores and action creators interoperable with Redux. | |
*/ | |
import React, { Component } from 'react'; | |
export default function dispatch(store, atom, action) { | |
return store(atom, action); | |
} | |
export class Dispatcher extends Component { | |
static propTypes = { | |
store: React.PropTypes.func.isRequired | |
}; | |
static childContextTypes = { | |
dispatch: React.PropTypes.func, | |
atom: React.PropTypes.any | |
}; | |
getChildContext() { | |
return { | |
atom: this.state.atom, | |
dispatch: this.dispatch.bind(this) | |
}; | |
} | |
constructor(props, context) { | |
super(props, context); | |
this.state = { atom: dispatch(props.store, undefined, {}) }; | |
} | |
dispatch(payload) { | |
this.setState(prevState => ({ | |
atom: dispatch(this.props.store, prevState.atom, payload) | |
})); | |
} | |
render() { | |
return typeof this.props.children === 'function' | |
? this.props.children(this.state.atom) | |
: this.props.children; | |
} | |
} | |
export class Injector extends Component { | |
static contextTypes = { | |
dispatch: React.PropTypes.func.isRequired, | |
atom: React.PropTypes.any | |
}; | |
static propTypes = { | |
actions: React.PropTypes.object | |
}; | |
performAction(actionCreator, ...args) { | |
const { dispatch } = this.context; | |
const payload = actionCreator(...args); | |
return typeof payload === 'function' | |
? payload(dispatch) | |
: dispatch(payload); | |
}; | |
render() { | |
const { dispatch, atom } = this.context; | |
const { actions: _actions } = this.props; | |
const actions = Object.keys(_actions).reduce((result, key) => { | |
result[key] = this.performAction.bind(this, _actions[key]); | |
return result; | |
}, {}); | |
return this.props.children({ actions, atom }); | |
} | |
} |
This file contains 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
/** | |
* Example usage | |
* | |
* Based on Redux's counter example | |
* https://github.com/gaearon/redux/tree/master/examples/counter | |
*/ | |
import React, { Component, PropTypes } from 'react'; | |
import { Dispatcher, Injector } from '../'; | |
const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; | |
const DECREMENT_COUNTER = 'DECREMENT_COUNTER'; | |
function counterStore(counter = 0, action) { | |
switch (action.type) { | |
case INCREMENT_COUNTER: | |
return counter + 1; | |
case DECREMENT_COUNTER: | |
return counter - 10; | |
default: | |
return counter; | |
} | |
} | |
function increment() { | |
return { | |
type: INCREMENT_COUNTER | |
}; | |
} | |
function decrement() { | |
return { | |
type: DECREMENT_COUNTER | |
}; | |
} | |
export default class CounterApp { | |
render() { | |
return ( | |
<Dispatcher | |
// Instead of specifying an object of keys mapped to stores, just use a | |
// higher-order store! | |
store={(state = {}, action) => ({ | |
counter: counterStore(state.counter, action) | |
})} | |
> | |
{() => ( | |
<Injector actions={{ increment, decrement }}> | |
{({ actions, atom }) => ( | |
<Counter | |
increment={actions.increment} | |
decrement={actions.decrement} | |
counter={atom.counter} | |
/> | |
)} | |
</Injector> | |
)} | |
</Dispatcher> | |
); | |
} | |
} | |
class Counter { | |
static propTypes = { | |
increment: PropTypes.func.isRequired, | |
decrement: PropTypes.func.isRequired, | |
counter: PropTypes.number.isRequired | |
}; | |
render() { | |
const { increment, decrement, counter } = this.props; | |
return ( | |
<p> | |
Clicked: {counter} times | |
{' '} | |
<button onClick={increment}>+</button> | |
{' '} | |
<button onClick={decrement}>-</button> | |
</p> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment