Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
 <script src="https://fb.me/react-0.14.0.min.js"></script>
  <script src="https://fb.me/react-dom-0.14.0.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.5.2/redux.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/4.4.5/react-redux.min.js"></script>
  <script src="https://npmcdn.com/xstream@9.0.0/dist/xstream.min.js"></script>
  <script src="https://npmcdn.com/@cycle/xstream-run@4.0.0/dist/cycle.min.js"></script>
  
<script>
const xs = xstream.default;
function createCycleMiddleware () {
  let store = null;
  let actionListener = null;
  let stateListener = null;
  const cycleMiddleware = _store => {
    store = _store;
    return next => {
      return action => {
        let result = next(action)
        if (actionListener) {
          actionListener.next(action);
        }
        if (stateListener) {
          stateListener.next(store.getState());
        }
        return result
      }
    }
  }
  cycleMiddleware.makeActionDriver = () => {
    return function actionDriver(outgoing$) {
      outgoing$.addListener({
        next: outgoing => {
          if (store) {
            store.dispatch(outgoing);
          }
        },
        error: () => {},
        complete: () => {},
      });
      return xs.create({
        start: listener => {
          actionListener = listener;
        },
        stop: () => {},
      });
    }
  }
  cycleMiddleware.makeStateDriver = () => {
    const isSame = {};
    const getCurrent = store.getState;
    return function stateDriver() {
      return xs.create({
        start: listener => {
          stateListener = listener;
        },
        stop: () => {},
      })
      .fold((prevState, currState) => {
        if (prevState === getCurrent) {
          prevState = getCurrent();
        }
        if (prevState === currState) {
          return isSame;
        }
        return currState;
      }, getCurrent)
      .map(state => state === getCurrent ? getCurrent() : state)
      .filter(state => state !== isSame);
    }
  }
  return cycleMiddleware;
}
</script>
</head>
  
<body>
  <div id='root'></div>
</body>
</html>
 
// https://github.com/lmatteis/redux-cycle-middleware/
function main(sources) {
  const state$ = sources.STATE;
  const isOdd$ = state$
    .map(state => state % 2 === 1)
    .take(1);
  
  const incrementIfOdd$ = sources.ACTION
    .filter(action => action.type === 'INCREMENT_IF_ODD')
    .map(action =>
      isOdd$
    )
    .flatten()
    .filter(isOdd => isOdd)
    .mapTo({ type: 'INCREMENT' });
  const increment$ = sources.ACTION
    .filter(action => action.type === 'INCREMENT_ASYNC')
    .mapTo({ type: 'INCREMENT' });
  
  const decrement$ = sources.ACTION
    .filter(action => action.type === 'DECREMENT_ASYNC')
    .mapTo({ type: 'DECREMENT' }); 
  
  const both$ = xs.merge(increment$, decrement$)
  
  return {
    ACTION: xs.merge(both$, incrementIfOdd$)
  }
}
/*
 * How is the next application state calculated,
 * given the current state and the action?
 */
const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
} 
/*
 * What does UI look like, assuming it doesn't know
 * about the state or actions, and is a function
 * of the props?
 */
const Counter = ({
  value,
  onIncrement,
  onDecrement,
  onIncrementOdd
}) => (
  <div>
    <h1>{value}</h1>
    <button onClick={onIncrement}>+</button>
    <button onClick={onDecrement}>-</button>
    <button onClick={onIncrementOdd}>odd +</button>
  </div>
);
/*
 * Which injected props should be calculated
 * from the application state and how?
 */
const mapStateToProps = (state) => {
  return {
    value: state
  };
}
/*
 * Which injected props should be callbacks
 * that dispatch actions, and which actions?
 */
const mapDispatchToProps = (dispatch) => {
  return {
    onIncrement: () => {
      dispatch({
        type: 'INCREMENT_ASYNC'           
      })            
    },
    onDecrement: () => {
      dispatch({
        type: 'DECREMENT_ASYNC'           
      })            
    },
    onIncrementOdd: () => {
      dispatch({
        type: 'INCREMENT_IF_ODD'           
      })            
    }
  };
};
/*
 * Let's create a container component
 * that injects props into the pure UI component
 * according to the instructions above, but
 * instead of all those props, accepts a store.
 */
const { connect } = ReactRedux;
const CounterContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);
/*
 * Let's create a store.
 */
const { createStore, applyMiddleware } = Redux;
const cycleMiddleware = createCycleMiddleware();
const { makeActionDriver, makeStateDriver } = cycleMiddleware;
const store = createStore(
  counter,
  applyMiddleware(cycleMiddleware)
);
Cycle.run(main, {
  ACTION: makeActionDriver(),
  STATE: makeStateDriver()
})
/*
 * Finally, render the container,
 * passing the store to it.
 */
ReactDOM.render(
  <CounterContainer store={store} />,
  document.getElementById('root')
);
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
anonymouspro
0viewers