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

Changed from redux-thunk to redux-saga #325

Closed
wants to merge 2 commits 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/controllers/pages_controller.rb
Expand Up @@ -32,6 +32,11 @@ def no_router
render_html
end

def saga
redux_store("sagaCommentsStore", props: comments_json_string)
render_html
end

def simple
end

Expand Down
6 changes: 6 additions & 0 deletions app/views/pages/saga.html.erb
@@ -0,0 +1,6 @@
<h2>Using React (Server rendering) + Redux + Rails Backend (using the
<a href="https://github.com/shakacode/react_on_rails">react_on_rails gem</a>)</h2>
<%= render "header" %>

<!--Note, pre-rendering set in /config/initializers/react_on_rails.rb -->
<%= react_component('SagaApp') %>
52 changes: 52 additions & 0 deletions client/app/bundles/comments/actions/sagaCommentsActionCreators.js
@@ -0,0 +1,52 @@
import * as actionTypes from '../constants/commentsConstants';

export function setIsFetching() {
return {
type: actionTypes.SET_IS_FETCHING,
};
}

export function setIsSaving() {
return {
type: actionTypes.SET_IS_SAVING,
};
}

export function fetchCommentsSuccess(data) {
return {
type: actionTypes.FETCH_COMMENTS_SUCCESS,
comments: data.comments,
};
}

export function fetchCommentsFailure(error) {
return {
type: actionTypes.FETCH_COMMENTS_FAILURE,
error,
};
}

export function submitCommentSuccess(comment) {
return {
type: actionTypes.SUBMIT_COMMENT_SUCCESS,
comment,
};
}

export function submitCommentFailure(error) {
return {
type: actionTypes.SUBMIT_COMMENT_FAILURE,
error,
};
}

export function fetchComments() {
return setIsFetching();
}

export function submitComment(comment) {
return {
type: actionTypes.SET_IS_SAVING,
comment,
};
}
Expand Up @@ -83,9 +83,8 @@ export default class CommentForm extends BaseComponent {
handleSubmit(e) {
e.preventDefault();
const { actions } = this.props;
actions
.submitComment(this.state.comment)
.then(this.resetAndFocus);
actions.submitComment(this.state.comment);
this.resetAndFocus;
}

resetAndFocus() {
Expand Down
Expand Up @@ -35,6 +35,9 @@ const NavigationBar = (props) => {
<li className={classNames({ active: (pathname === paths.NO_ROUTER_PATH) })}>
<a href={paths.NO_ROUTER_PATH}>React Demo</a>
</li>
<li className={classNames({ active: (pathname === paths.SAGA_PATH) })}>
<a href={paths.SAGA_PATH}>React Saga Demo</a>
</li>
<li className={classNames({ active: (pathname === paths.SIMPLE_REACT_PATH) })}>
<a href={paths.SIMPLE_REACT_PATH}>Simple React</a>
</li>
Expand Down
1 change: 1 addition & 0 deletions client/app/bundles/comments/constants/paths.js
@@ -1,4 +1,5 @@
export const ROUTER_PATH = '/';
export const NO_ROUTER_PATH = '/no-router';
export const SAGA_PATH = '/saga';
export const SIMPLE_REACT_PATH = '/simple';
export const RAILS_PATH = '/comments';
31 changes: 31 additions & 0 deletions client/app/bundles/comments/containers/SagaCommentsContainer.jsx
@@ -0,0 +1,31 @@
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import BaseComponent from 'libs/components/BaseComponent';

import CommentScreen from '../components/CommentScreen/CommentScreen';
import * as sagaCommentsActionCreators from '../actions/sagaCommentsActionCreators';

function select(state) {
// Which part of the Redux global state does our component want to receive as props?
return { data: state.$$commentsStore };
}

class SagaCommentsContainer extends BaseComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
data: PropTypes.object.isRequired,
};

render() {
const { dispatch, data } = this.props;
const actions = bindActionCreators(sagaCommentsActionCreators, dispatch);
return (
<CommentScreen {...{ actions, data }} />
);
}
}

// Don't forget to actually use connect!
export default connect(select)(SagaCommentsContainer);
17 changes: 17 additions & 0 deletions client/app/bundles/comments/startup/SagaApp.jsx
@@ -0,0 +1,17 @@
import React from 'react';
import { Provider } from 'react-redux';
import ReactOnRails from 'react-on-rails';

import SagaCommentsContainer from '../containers/SagaCommentsContainer';
import rootSaga from '../store/sagas';

export default (_props, _railsContext) => {
const store = ReactOnRails.getStore('sagaCommentsStore');
store.runSaga(rootSaga);

return (
<Provider store={store}>
<SagaCommentsContainer />
</Provider>
);
};
4 changes: 4 additions & 0 deletions client/app/bundles/comments/startup/clientRegistration.jsx
Expand Up @@ -2,9 +2,11 @@ import ReactOnRails from 'react-on-rails';

import App from './App';
import RouterApp from './ClientRouterApp';
import SagaApp from './SagaApp';
import SimpleCommentScreen from '../components/SimpleCommentScreen/SimpleCommentScreen';
import routerCommentsStore from '../store/routerCommentsStore';
import commentsStore from '../store/commentsStore';
import sagaCommentsStore from '../store/sagaCommentsStore';
import NavigationBarApp from './NavigationBarApp';

ReactOnRails.setOptions({
Expand All @@ -14,11 +16,13 @@ ReactOnRails.setOptions({
ReactOnRails.register({
App,
RouterApp,
SagaApp,
NavigationBarApp,
SimpleCommentScreen,
});

ReactOnRails.registerStore({
routerCommentsStore,
commentsStore,
sagaCommentsStore,
});
4 changes: 4 additions & 0 deletions client/app/bundles/comments/startup/serverRegistration.jsx
Expand Up @@ -3,15 +3,18 @@ import ReactOnRails from 'react-on-rails';

import App from './App';
import RouterApp from './ServerRouterApp';
import SagaApp from './SagaApp';
import SimpleCommentScreen from '../components/SimpleCommentScreen/SimpleCommentScreen';
import NavigationBarApp from './NavigationBarApp';
import routerCommentsStore from '../store/routerCommentsStore';
import commentsStore from '../store/commentsStore';
import sagaCommentsStore from '../store/sagaCommentsStore';

ReactOnRails.register(
{
App,
RouterApp,
SagaApp,
NavigationBarApp,
SimpleCommentScreen,
}
Expand All @@ -20,4 +23,5 @@ ReactOnRails.register(
ReactOnRails.registerStore({
routerCommentsStore,
commentsStore,
sagaCommentsStore,
});
31 changes: 31 additions & 0 deletions client/app/bundles/comments/store/sagaCommentsStore.js
@@ -0,0 +1,31 @@
import { compose, createStore, applyMiddleware, combineReducers } from 'redux';
import createSagaMiddleware from 'redux-saga';
import loggerMiddleware from 'libs/middlewares/loggerMiddleware';
import reducers, { initialStates } from '../reducers';

export default (props, railsContext) => {
const initialComments = props.comments;
const { $$commentsState } = initialStates;
const initialState = {
$$commentsStore: $$commentsState.merge({
$$comments: initialComments,
}),
railsContext,
};

const reducer = combineReducers(reducers);

const sagaMiddleware = createSagaMiddleware();

// https://github.com/zalmoxisus/redux-devtools-extension
const reduxDevtoolsExtension = (typeof window === 'object' &&
typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f);

const store = { ...createStore(reducer, initialState,
compose(applyMiddleware(sagaMiddleware, loggerMiddleware),
reduxDevtoolsExtension)),
runSaga: sagaMiddleware.run,
};

return store;
};
36 changes: 36 additions & 0 deletions client/app/bundles/comments/store/sagas.js
@@ -0,0 +1,36 @@
import { takeEvery } from 'redux-saga';
import { call, put, fork } from 'redux-saga/effects';
import requestsManager from 'libs/requestsManager';
import * as actionTypes from '../constants/commentsConstants';
import * as actionCreators from '../actions/commentsActionCreators';

function* fetchComments() {
try {
const responce = yield call(requestsManager.fetchEntities);
yield put(actionCreators.fetchCommentsSuccess(responce.data));
} catch (error) {
yield put(actionCreators.fetchCommentsFailure(error));
}
}

function* submitComment(action) {
try {
const responce = yield call(requestsManager.submitEntity, action);
yield put(actionCreators.submitCommentSuccess(responce.data));
} catch (error) {
yield put(actionCreators.submitCommentFailure(error));
}
}

export function* fetchCommentsSaga() {
yield* takeEvery(actionTypes.SET_IS_FETCHING, fetchComments);
}

export function* submitCommentSaga() {
yield* takeEvery(actionTypes.SET_IS_SAVING, submitComment);
}

export default function* root() {
yield fork(fetchCommentsSaga);
yield fork(submitCommentSaga);
}
1 change: 1 addition & 0 deletions client/package.json
Expand Up @@ -80,6 +80,7 @@
"react-router": "^2.5.2",
"react-router-redux": "^4.0.5",
"redux": "^3.5.2",
"redux-saga": "^0.11.0",
"redux-thunk": "^2.1.0",
"resolve-url-loader": "^1.6.0",
"sass-loader": "^4.0.0",
Expand Down
1 change: 1 addition & 0 deletions client/webpack.client.base.config.js
Expand Up @@ -35,6 +35,7 @@ module.exports = {
'react-redux',
'react-on-rails',
'react-router-redux',
'redux-saga',
'redux-thunk',
],

Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Expand Up @@ -8,6 +8,7 @@

get "simple", to: "pages#simple"
get "no-router", to: "pages#no_router"
get "saga", to: "pages#saga"

# React Router needs a wildcard
get "react-router(/*all)", to: "pages#index"
Expand Down