Skip to content

Instantly share code, notes, and snippets.

@afair
Created August 23, 2016 21:12
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save afair/18b40f28293e4790c74f486674d0c64c to your computer and use it in GitHub Desktop.
To study the React Redux Starter Kit Tutorial
// 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