Last active
January 27, 2016 09:00
-
-
Save pe3/8794b27999332231ccfd to your computer and use it in GitHub Desktop.
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
import React, { PropTypes } from 'react' | |
import ReactDOM from 'react-dom' | |
import { createStore, combineReducers, applyMiddleware } from 'redux' | |
import { Provider, connect } from 'react-redux' | |
import sagaMiddleware, { take, put, fork } from 'redux-saga' | |
// React component | |
export class Counter extends React.Component { | |
render(){ | |
const { value, clickPlus, clickMinus, clickSlow } = this.props | |
return ( | |
<div className={this.props.className}> | |
<span>{value}</span> | |
<button onClick={clickMinus}>-</button> | |
<button onClick={clickPlus}>+</button> | |
<button onClick={clickSlow}>slow</button> | |
</div> | |
) | |
} | |
} | |
Counter.propTypes = { | |
value: PropTypes.number.isRequired, | |
clickPlus: PropTypes.func.isRequired, | |
clickMinus: PropTypes.func.isRequired, | |
clickSlow: PropTypes.func.isRequired | |
} | |
// Action: | |
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER' | |
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER' | |
export const SET_COUNTER = 'SET_COUNTER' | |
export const INCREMENT_ASYNC = 'INCREMENT_ASYNC' | |
const increment = () => ({type: INCREMENT_COUNTER}) | |
const decrement = () => ({type: DECREMENT_COUNTER}) | |
const setCounter = (value) => ({type: SET_COUNTER, value: parseInt(value)}) | |
const incrementAsync = () => ({type: INCREMENT_ASYNC}) | |
// Reducer: | |
export function counter(state = 0, action) { | |
switch (action.type) { | |
case INCREMENT_COUNTER: | |
return state + 1 | |
case DECREMENT_COUNTER: | |
return state - 1 | |
case SET_COUNTER: | |
return action.value | |
default: | |
return state | |
} | |
} | |
// Sagas (like threads): | |
function* watchDelayedIncrement() { | |
while(true) { | |
const nextAction = yield take(INCREMENT_ASYNC) | |
yield delay(1000) | |
// after the delay, dispatch plain INCREMENT_COUNTER | |
yield put( increment() ) | |
} | |
} | |
const delay = ms => ( | |
new Promise(resolve => { | |
setTimeout(() => resolve(), ms) | |
}) | |
) | |
function* updateWindowLocation(getState) { | |
while(true) { | |
const nextAction = yield take([INCREMENT_COUNTER, DECREMENT_COUNTER, SET_COUNTER]) | |
window.location = "#" + getState().counter | |
put(nextAction) | |
} | |
} | |
function* rootSaga(getState) { | |
yield fork(watchDelayedIncrement) | |
yield fork(updateWindowLocation, getState) | |
} | |
// Store: | |
const createStoreWithSaga = applyMiddleware( | |
sagaMiddleware(rootSaga) | |
)(createStore) | |
const rootReducer = combineReducers({counter}) | |
let store = createStoreWithSaga(rootReducer) | |
// Connected Component: | |
function mapStateToProps(state) { | |
return { | |
value: state.counter | |
} | |
} | |
function mapDispatchToProps(dispatch) { | |
return { | |
clickPlus: () => dispatch(increment()), | |
clickMinus: () => dispatch(decrement()), | |
clickSlow: () => dispatch(incrementAsync()) | |
} | |
} | |
const App = connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(Counter) | |
// initialize state once | |
const maybeId = window.location.hash.substr(1) | |
if(maybeId){ | |
store.dispatch(setCounter(maybeId)) | |
} | |
window.addEventListener("hashchange", (e) => { | |
const maybeId = window.location.hash.substr(1) | |
if(store.getState().counter !== maybeId) { | |
store.dispatch(setCounter(maybeId)) | |
} | |
}) | |
ReactDOM.render( | |
<Provider store={store}> | |
<App /> | |
</Provider>, | |
document.getElementById('react-root') | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment