Skip to content

Instantly share code, notes, and snippets.

@l0gicgate
Last active May 23, 2016 14:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save l0gicgate/45714a4389836179cfd3 to your computer and use it in GitHub Desktop.
Save l0gicgate/45714a4389836179cfd3 to your computer and use it in GitHub Desktop.
Reusable action creators / reducers.
import axios from 'axios';
class FetchActionCreators {
constructor(endpoint, actions) {
const [REQUEST, SUCCESS, FAILURE] = actions;
this.actions = {
REQUEST,
SUCCESS,
FAILURE,
};
this.endpoint = endpoint;
}
fetch({ endpoint, params }) {
return (dispatch) => {
dispatch(this.request());
return axios.get(endpoint || this.endpoint, { params })
.then(res => {
dispatch(this.receive(this.actions.SUCCESS, res.data));
})
.catch((res) => {
dispatch(this.receive(this.actions.FAILURE));
});
};
}
request() {
return {
type: this.actions.REQUEST,
};
}
receive(type, data) {
return {
type,
data,
};
}
}
export default FetchActionCreators;
class FetchReducerPrototype {
static initialState = {
data: null,
fetching: false,
receivedAt: null,
};
constructor({ actions }) {
this.actions = actions;
return this.reducer;
}
reducer = (state = FetchReducerPrototype.initialState, action = {}) => {
switch (action.type) {
case this.actions.REQUEST:
return Object.assign({}, state, {
fetching: true,
});
case this.actions.SUCCESS:
return Object.assign({}, state, {
data: action.data,
fetching: false,
receivedAt: Date.now(),
});
case this.actions.FAILURE:
return Object.assign({}, state, {
fetching: false,
});
default:
return state;
}
}
}
export default FetchReducerPrototype;
export default class ReducerChain {
constructor(reducers) {
this.reducers = reducers;
if (!Array.isArray(reducers)) {
throw new Error('To create a reducer chain you must pass in an array of functions.');
}
return this.reducer;
}
reducer = (state, action) => {
let result = state;
for (let i = 0, len = this.reducers.length; i < len; i++) {
result = this.reducers[i](result, action);
}
return result;
};
}
import { combineReducers } from 'redux';
import StreamsReducer from './StreamsReducer';
import VideosReducer from './VideosReducer';
const rootReducer = combineReducers({
streams: StreamsReducer,
videos: VideosReducer,
});
import React, { PropTypes } from 'react';
import { streamsFetchActions } from './StreamsReducer';
class StreamsContainer extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
streams: PropTypes.object.isRequired,
};
componentDidMount() {
const { dispatch, streams } = this.props;
if (!streams.fetching && !streams.data) {
dispatch(streamsFetchActions.fetch());
}
}
render() {
const { streams } = this.props;
return streams.fetching ?
<div>Loading...</div> :
<div>{/* access streams via streams.data */}</div>;
}
}
function mapStateToProps(state) {
return {
streams: state.streams,
}
}
export default connect(mapStateToProps)(StreamsContainer);
import FetchActionCreators from './FetchActionCreators';
import FetchReducerPrototype from './FetchReducerPrototype';
const REQUEST_STREAMS = 'REQUEST_STREAMS';
const RECEIVE_STREAMS_SUCCESS = 'RECEIVE_STREAMS_SUCCESS';
const RECEIVE_STREAMS_FAILURE = 'RECEIVE_STREAMS_FAILURE';
export const streamsFetchActions = new FetchActionCreators(
'http://yourwebsite.com/streams',
[
REQUEST_STREAMS,
RECEIVE_STREAMS_SUCCESS,
RECEIVE_STREAMS_FAILURE,
]
);
export default new FetchReducerPrototype(streamsFetchActions);
import React, { PropTypes } from 'react';
import { videosFetchActions, modifyVideoData } from './VideosReducer';
class VideosContainer extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
videos: PropTypes.object.isRequired,
};
componentDidMount() {
const { dispatch, videos } = this.props;
if (!videos.fetching && !videos.data) {
dispatch(videosFetchActions.fetch());
}
}
render() {
const { dispatch, videos } = this.props;
return videos.fetching ?
<div>Loading...</div> :
(
<div>
{/* access videos via videos.data */}
<button onClick={(e) => dispatch(modifyVideoData('Some value'))} />
</div>
);
}
}
function mapStateToProps(state) {
return {
videos: state.videos,
}
}
export default connect(mapStateToProps)(VideosContainer);
import FetchActionCreators from './FetchActionCreators';
import FetchReducerPrototype from './FetchReducerPrototype';
import ReducerChain from './ReducerChain';
const REQUEST_VIDEOS = 'REQUEST_VIDEOS';
const RECEIVE_VIDEOS_SUCCESS = 'RECEIVE_VIDEOS_SUCCESS';
const RECEIVE_VIDEOS_FAILURE = 'RECEIVE_VIDEOS_FAILURE';
const MODIFY_VIDEO_DATA = 'MODIFY_VIDEO_DATA';
export const videosFetchActions = new FetchActionCreators(
'http://yourwebsite.com/videos',
[
REQUEST_VIDEOS,
RECEIVE_VIDEOS_SUCCESS,
RECEIVE_VIDEOS_FAILURE,
]
);
const fetchReducerPrototype = new FetchReducerPrototype(videosFetchActions);
const videoModifyingReducer(state, action) {
switch (action.type) {
case MODIFY_VIDEO_DATA:
return {
...state,
{
data: {
...state.data,
someProp: action.someProp,
}
}
});
default:
return state;
}
}
export function modifyVideoData(someValue) {
return {
type: MODIFY_VIDEO_DATA,
someProp: someValue,
}
}
export default new ReducerChain([
fetchReducerPrototype,
videoModifyingReducer,
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment