Skip to content

Instantly share code, notes, and snippets.

@lcfd
Forked from acdlite/flux.js
Created September 21, 2016 06:54
Show Gist options
  • Save lcfd/a100b9472c31d79746fa4570841adace to your computer and use it in GitHub Desktop.
Save lcfd/a100b9472c31d79746fa4570841adace to your computer and use it in GitHub Desktop.
A Redux-like Flux implementation in <75 lines of code
/**
* 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 });
}
}
/**
* 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