Created
August 23, 2016 21:12
-
-
Save afair/18b40f28293e4790c74f486674d0c64c to your computer and use it in GitHub Desktop.
To study the React Redux Starter Kit Tutorial
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
// https://suspicious.website/2016/04/29/starting-out-with-react-redux-starter-kit/ | |
// ################################################################################ | |
// src/routes/Zen/interfaces/zen.js | |
/* @flow */ | |
export type ZenObject = { | |
id: number, | |
value: string | |
} | |
export type ZenStateObject = { | |
current: ?number, | |
fetching: boolean, | |
saved: Array<number>, | |
zens: Array<ZenObject> | |
} | |
// src/routes/index.js | |
import { injectReducer } from '../../store/reducers' | |
export default (store) => ({ | |
path: 'zen', | |
getComponent (nextState, next) { | |
require.ensure([ | |
'./containers/ZenContainer', | |
'./modules/zen' | |
], (require) => { | |
/* These modules are lazily evaluated using require hook, and | |
will not loaded until the router invokes this callback. */ | |
const Zen = require('./containers/ZenContainer').default | |
const zenReducer = require('./modules/zen').default | |
injectReducer(store, { | |
key: 'zen', | |
reducer: zenReducer | |
}) | |
next(null, Zen) | |
}) | |
} | |
}) | |
// src/routes/Zen/modules/zen.js | |
/* @flow */ | |
import type { ZenObject, ZenStateObject } from '../interfaces/zen.js' | |
// ------------------------------------ | |
// Constants | |
// ------------------------------------ | |
export const REQUEST_ZEN = 'REQUEST_ZEN' | |
export const RECIEVE_ZEN = 'RECIEVE_ZEN' | |
export const SAVE_CURRENT_ZEN = 'SAVE_CURRENT_ZEN' | |
// ------------------------------------ | |
// Actions | |
// ------------------------------------ | |
export function requestZen (): Action { | |
return { | |
type: REQUEST_ZEN | |
} | |
} | |
let availableId = 0 | |
export function recieveZen (value: string): Action { | |
return { | |
type: RECIEVE_ZEN, | |
payload: { | |
value, | |
id: availableId++ | |
} | |
} | |
} | |
export function saveCurrentZen (): Action { | |
return { | |
type: SAVE_CURRENT_ZEN | |
} | |
} | |
export const fetchZen = (): Function => { | |
return (dispatch: Function): Promise => { | |
dispatch(requestZen()) | |
return fetch('https://api.github.com/zen') | |
.then(data => data.text()) | |
.then(text => dispatch(recieveZen(text))) | |
} | |
} | |
export const actions = { | |
requestZen, | |
recieveZen, | |
fetchZen, | |
saveCurrentZen | |
} | |
// ------------------------------------ | |
// Action Handlers | |
// ------------------------------------ | |
const ZEN_ACTION_HANDLERS = { | |
[REQUEST_ZEN]: (state: ZenStateObject): ZenStateObject => { | |
return ({ ...state, fetching: true }) | |
}, | |
[RECIEVE_ZEN]: (state: ZenStateObject, action: {payload: ZenObject}): ZenStateObject => { | |
return ({ ...state, zens: state.zens.concat(action.payload), current: action.payload.id, fetching: false }) | |
}, | |
[SAVE_CURRENT_ZEN]: (state: ZenStateObject): ZenStateObject => { | |
return state.current != null ? ({ ...state, saved: state.saved.concat(state.current) }) : state | |
} | |
} | |
// ------------------------------------ | |
// Reducers | |
// ------------------------------------ | |
const initialState: ZenStateObject = { fetching: false, current: null, zens: [], saved: [] } | |
export default function zenReducer (state: ZenStateObject = initialState, action: Action): ZenStateObject { | |
const handler = ZEN_ACTION_HANDLERS[action.type] | |
return handler ? handler(state, action) : state | |
} | |
// src/routes/Zen/containers/ZenContainer.js | |
/* @flow */ | |
import { connect } from 'react-redux' | |
import { fetchZen, saveCurrentZen } from '../modules/zen' | |
import Zen from '../components/Zen' | |
import type { ZenObject } from '../interfaces/zen' | |
const mapActionCreators: {fetchZen: Function, saveCurrentZen: Function} = { | |
fetchZen, | |
saveCurrentZen | |
} | |
const mapStateToProps = (state): { zen: ?ZenObject, saved: Array<ZenObject> } => ({ | |
zen: state.zen.zens.find(zen => zen.id === state.zen.current), | |
saved: state.zen.zens.filter(zen => state.zen.saved.indexOf(zen.id) !== -1) | |
}) | |
export default connect(mapStateToProps, mapActionCreators)(Zen) | |
// src/routes/Zen/components/Zen.js | |
/* @flow */ | |
import React from 'react' | |
import classes from './Zen.scss' | |
import type { ZenObject } from '../interfaces/zen' | |
type Props = { | |
zen: ?ZenObject, | |
saved: Array<ZenObject>, | |
fetchZen: Function, | |
saveCurrentZen: Function | |
} | |
export const Zen = (props: Props) => ( | |
<div> | |
<div> | |
<h2 className={classes.zenHeader}> | |
{props.zen ? props.zen.value : ''} | |
</h2> | |
<button className='btn btn-default' onClick={props.fetchZen}> | |
Fetch a wisdom | |
</button> | |
{' '} | |
<button className='btn btn-default' onClick={props.saveCurrentZen}> | |
Save | |
</button> | |
</div> | |
{props.saved.length | |
? <div className={classes.savedWisdoms}> | |
<h3> | |
Saved wisdoms | |
</h3> | |
<ul> | |
{props.saved.map(zen => | |
<li key={zen.id}> | |
{zen.value} | |
</li> | |
)} | |
</ul> | |
</div> | |
: null | |
} | |
</div> | |
) | |
Zen.propTypes = { | |
zen: React.PropTypes.object, | |
saved: React.PropTypes.array.isRequired, | |
fetchZen: React.PropTypes.func.isRequired, | |
saveCurrentZen: React.PropTypes.func.isRequired | |
} | |
export default Zen |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment