Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add redux saga #106

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["react", "es2015"],
"presets": ["react", "es2015", "stage-0"],
}
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "1.0.0",
"description": "Boilerplate project for building Isomorphic apps using React and Redux",
"scripts": {
"test": "mocha shared/tests/*.spec.js --compilers js:babel-register",
"test:server": "cross-env NODE_ENV=test PORT=8080 MONGO_URL=mongodb://localhost:27017/mern-test mocha --compilers js:babel-register --recursive server/tests/**/*.spec.js",
"test": "mocha shared/tests/*.spec.js --compilers js:babel-core/register --require babel-polyfill",
"test:server": "cross-env NODE_ENV=test PORT=8080 MONGO_URL=mongodb://localhost:27017/mern-test mocha --compilers js:babel-core/register --require babel-polyfill --recursive server/tests/**/*.spec.js",
"start": "cross-env NODE_ENV=development node index.js",
"start:prod": "cross-env NODE_ENV=production node index.js",
"bs": "npm run clean && npm run build && npm run start:prod",
Expand Down Expand Up @@ -42,6 +42,7 @@
"react-redux": "^4.1.2",
"react-router": "^2.0.0-rc5",
"redux": "^3.1.5",
"redux-saga": "^0.9.5",
"redux-thunk": "^1.0.3",
"sanitize-html": "^1.11.3",
"slug": "^0.9.1"
Expand All @@ -54,6 +55,7 @@
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-react-hmre": "^1.1.0",
"babel-preset-stage-0": "^6.5.0",
"babel-register": "^6.4.3",
"chai": "^3.5.0",
"clean-css": "^3.4.9",
Expand Down
3 changes: 2 additions & 1 deletion server/controllers/post.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ export function deletePost(req, res) {
}

post.remove(() => {
res.status(200).end();
// Sending empty response breaks json parsing, so for now sending empty object. Will remove it as soon as that problem is fixed.
return res.json({});
});
});
}
49 changes: 12 additions & 37 deletions shared/redux/actions/actions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as ActionTypes from '../constants/constants';
import fetch from 'isomorphic-fetch';

const baseURL = typeof window === 'undefined' ? process.env.BASE_URL || (`http://localhost:${(process.env.PORT || 8000)}`) : '';
export const baseURL = typeof window === 'undefined' ? process.env.BASE_URL || (`http://localhost:${(process.env.PORT || 8000)}`) : '';

export function addPost(post) {
return {
Expand All @@ -23,20 +22,9 @@ export function changeSelectedPost(slug) {
}

export function addPostRequest(post) {
return (dispatch) => {
fetch(`${baseURL}/api/addPost`, {
method: 'post',
body: JSON.stringify({
post: {
name: post.name,
title: post.title,
content: post.content,
},
}),
headers: new Headers({
'Content-Type': 'application/json',
}),
}).then((res) => res.json()).then(res => dispatch(addPost(res.post)));
return {
type: ActionTypes.ADD_POST_REQUEST,
post,
};
}

Expand All @@ -48,13 +36,9 @@ export function addSelectedPost(post) {
}

export function getPostRequest(post) {
return (dispatch) => {
return fetch(`${baseURL}/api/getPost?slug=${post}`, {
method: 'get',
headers: new Headers({
'Content-Type': 'application/json',
}),
}).then((response) => response.json()).then(res => dispatch(addSelectedPost(res.post)));
return {
type: ActionTypes.FETCH_POST,
post,
};
}

Expand All @@ -73,23 +57,14 @@ export function addPosts(posts) {
}

export function fetchPosts() {
return (dispatch) => {
return fetch(`${baseURL}/api/getPosts`).
then((response) => response.json()).
then((response) => dispatch(addPosts(response.posts)));
return {
type: ActionTypes.FETCH_POSTS,
};
}

export function deletePostRequest(post) {
return (dispatch) => {
fetch(`${baseURL}/api/deletePost`, {
method: 'post',
body: JSON.stringify({
postId: post._id,
}),
headers: new Headers({
'Content-Type': 'application/json',
}),
}).then(() => dispatch(deletePost(post)));
return {
type: ActionTypes.DELETE_POST_REQUEST,
post,
};
}
3 changes: 3 additions & 0 deletions shared/redux/constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ export const ADD_POST = 'ADD_POST';
export const CHANGE_SELECTED_POST = 'CHANGE_SELECTED_POST';
export const ADD_POST_REQUEST = 'ADD_POST_REQUEST';
export const ADD_POSTS = 'ADD_POSTS';
export const FETCH_POSTS = 'FETCH_POSTS';
export const FETCH_POST = 'FETCH_POST';
export const ADD_SELECTED_POST = 'ADD_SELECTED_POST';
export const DELETE_POST = 'DELETE_POST';
export const DELETE_POST_REQUEST = 'DELETE_POST_REQUEST';
9 changes: 9 additions & 0 deletions shared/redux/sagas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Export all your sagas here
import { addPostSaga, deletePostSaga, fetchPostsSaga, fetchPostSaga } from './postsSaga';

export default [
addPostSaga,
deletePostSaga,
fetchPostsSaga,
fetchPostSaga,
];
116 changes: 116 additions & 0 deletions shared/redux/sagas/postsSaga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { take, call, put } from 'redux-saga/effects';
import fetch from 'isomorphic-fetch';
import { baseURL, addPost, deletePost, addPosts, addSelectedPost } from '../actions/actions';
import { ADD_POST_REQUEST, DELETE_POST_REQUEST, FETCH_POSTS, FETCH_POST } from '../constants/constants';

export function sendRequest(url, options) {
return fetch(url, options)
.then(response => {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
})
.then(response => response.json())
.then((data) => ({ data }))
.catch((err) => ({ err }));
}

export function* addPostSaga() {
while (true) { // eslint-disable-line
// Listen for action
const { post } = yield take(ADD_POST_REQUEST);

// Send the request
const { data, err } = yield call(sendRequest, `${baseURL}/api/addPost`, {
method: 'post',
body: JSON.stringify({
post: {
name: post.name,
title: post.title,
content: post.content,
},
}),
headers: new Headers({
'Content-Type': 'application/json',
}),
});

if (err !== undefined && err !== null) {
// Error Action
// console.log(err.response);
} else {
// Success Action
yield put(addPost(data.post));
}
}
}

export function* deletePostSaga() {
while (true) { // eslint-disable-line
// Listen for action
const { post } = yield take(DELETE_POST_REQUEST);

// Send the request
const { err } = yield call(sendRequest, `${baseURL}/api/deletePost`, {
method: 'post',
body: JSON.stringify({
postId: post._id,
}),
headers: new Headers({
'Content-Type': 'application/json',
}),
});

if (err !== undefined && err !== null) {
// Error Action
// console.log(err.response);
} else {
// Success Action
yield put(deletePost(post));
}
}
}

export function* fetchPostsSaga() {
while (true) { // eslint-disable-line
// Listen for action
yield take(FETCH_POSTS);

// Send the request
const { data, err } = yield call(sendRequest, `${baseURL}/api/getPosts`);

if (err !== undefined && err !== null) {
// Error Action
// console.log(err.response);
} else {
// Success Action
yield put(addPosts(data.posts));
}
}
}

export function* fetchPostSaga() {
while (true) { // eslint-disable-line
// Listen for action
const { post } = yield take(FETCH_POST);

// Send the request
const { data, err } = yield call(sendRequest, `${baseURL}/api/getPost?slug=${post.slug}-${post.cuid}`, {
method: 'get',
headers: new Headers({
'Content-Type': 'application/json',
}),
});

if (err !== undefined && err !== null) {
// Error Action
// console.log(err.response);
} else {
// Success Action
yield put(addSelectedPost(data.post));
}
}
}
6 changes: 4 additions & 2 deletions shared/redux/store/configureStore.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { createStore, applyMiddleware, compose } from 'redux';
import { persistState } from 'redux-devtools';
import thunk from 'redux-thunk';
import sagaMiddleware from 'redux-saga';
import rootReducer from '../reducers/reducer';
import sagas from '../sagas';
import DevTools from '../../container/DevTools/DevTools';

export function configureStore(initialState = {}) {
let finalCreateStore;

if (process.env.CLIENT) {
finalCreateStore = compose(
applyMiddleware(thunk),
applyMiddleware(thunk, sagaMiddleware(...sagas)),
DevTools.instrument(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);
} else {
finalCreateStore = applyMiddleware(thunk)(createStore);
finalCreateStore = applyMiddleware(thunk, sagaMiddleware(...sagas))(createStore);
}

const store = finalCreateStore(rootReducer, initialState);
Expand Down
Loading