Created
August 23, 2016 21:12
Star
You must be signed in to star a gist
To study the React Redux Starter Kit Tutorial
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
// 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