diff --git a/client/configs/context.js b/client/configs/context.js index e83c596..1042634 100644 --- a/client/configs/context.js +++ b/client/configs/context.js @@ -1,15 +1,15 @@ import * as Collections from '/lib/collections'; import {Meteor} from 'meteor/meteor'; import {FlowRouter} from 'meteor/kadira:flow-router'; -import {ReactiveDict} from 'meteor/reactive-dict'; import {Tracker} from 'meteor/tracker'; +import {createStore} from 'redux'; -export default function () { +export default function ({reducer}) { return { Meteor, FlowRouter, Collections, - LocalState: new ReactiveDict(), - Tracker + Tracker, + Store: createStore(reducer) }; } diff --git a/client/main.js b/client/main.js index b745e21..68b7cf0 100644 --- a/client/main.js +++ b/client/main.js @@ -1,15 +1,23 @@ import {createApp} from 'mantra-core'; +import {combineReducers} from 'redux'; import initContext from './configs/context'; // modules import coreModule from './modules/core'; import commentsModule from './modules/comments'; +const coreReducers = coreModule.reducers; +const commentsReducers = commentsModule.reducers; +const reducer = combineReducers({ + ...coreReducers, + ...commentsReducers +}); + // init context -const context = initContext(); +const context = initContext({reducer}); // create app const app = createApp(context); app.loadModule(coreModule); app.loadModule(commentsModule); -app.init(); +app.init(); \ No newline at end of file diff --git a/client/modules/comments/actions/comments.js b/client/modules/comments/actions/comments.js index f6ee58b..e45481d 100644 --- a/client/modules/comments/actions/comments.js +++ b/client/modules/comments/actions/comments.js @@ -1,24 +1,37 @@ export default { - create({Meteor, LocalState}, postId, text) { + create({Meteor, Store}, postId, text) { if (!text) { - return LocalState.set('CREATE_COMMENT_ERROR', 'Comment text is required.'); + return Store.dispatch({ + type: 'SET_CREATE_COMMENT_ERROR', + error: 'Comment text is required.' + }); } if (!postId) { - return LocalState.set('CREATE_COMMENT_ERROR', 'postId is required.'); + return Store.dispatch({ + type: 'SET_CREATE_COMMENT_ERROR', + error: 'postId is required.' + }); } - LocalState.set('CREATE_COMMENT_ERROR', null); + Store.dispatch({ + type: 'CLEAR_CREATE_COMMENT_ERROR' + }); const id = Meteor.uuid(); Meteor.call('posts.createComment', id, postId, text, (err) => { if (err) { - return LocalState.set('CREATE_COMMENT_ERROR', err.message); + return Store.dispatch({ + type: 'SET_CREATE_COMMENT_ERROR', + error: err.message + }); } }); }, - clearErrors({LocalState}) { - return LocalState.set('CREATE_COMMENT_ERROR', null); + clearErrors({Store}) { + return Store.dispatch({ + type: 'CLEAR_CREATE_COMMENT_ERROR' + }); } }; diff --git a/client/modules/comments/configs/reducers/comments.js b/client/modules/comments/configs/reducers/comments.js new file mode 100644 index 0000000..9fd5cee --- /dev/null +++ b/client/modules/comments/configs/reducers/comments.js @@ -0,0 +1,19 @@ +const defaultState = { CREATE_COMMENT_ERROR: null}; +const commentsReducer = (state = defaultState, action) => { + switch(action.type) { + case 'SET_CREATE_COMMENT_ERROR': + return { + ...state, + CREATE_COMMENT_ERROR: action.error + }; + case 'CLEAR_CREATE_COMMENT_ERROR': + return { + ...state, + CREATE_COMMENT_ERROR: null + }; + default: + return state; + } +}; + +export default commentsReducer; \ No newline at end of file diff --git a/client/modules/comments/configs/reducers/index.js b/client/modules/comments/configs/reducers/index.js new file mode 100644 index 0000000..f0fb321 --- /dev/null +++ b/client/modules/comments/configs/reducers/index.js @@ -0,0 +1,5 @@ +import comments from './comments'; + +export default { + comments +} diff --git a/client/modules/comments/containers/create_comment.js b/client/modules/comments/containers/create_comment.js index 699ddb3..ef1128d 100644 --- a/client/modules/comments/containers/create_comment.js +++ b/client/modules/comments/containers/create_comment.js @@ -1,14 +1,31 @@ import { - useDeps, composeWithTracker, composeAll + useDeps, compose, composeAll } from 'mantra-core'; import Component from '../components/create_comment.jsx'; export const composer = ({context, clearErrors}, onData) => { - const {LocalState} = context(); - const error = LocalState.get('CREATE_COMMENT_ERROR'); + const {Store} = context(); + + // subscribe to state updates + // and keep handle to unsubscribe + const unsubscribe = Store.subscribe(() => { + const error = Store.getState().comments.CREATE_COMMENT_ERROR; + onData(null, {error}) + }); + + // get initial state + const error = Store.getState().comments.CREATE_COMMENT_ERROR; onData(null, {error}); - return clearErrors; + // function to unsubscribe from Store + // and clearing error + const cleanup = () => { + unsubscribe(); + clearErrors(); + }; + + // running cleanup when unmounting the component + return cleanup; }; export const depsMapper = (context, actions) => ({ @@ -18,6 +35,6 @@ export const depsMapper = (context, actions) => ({ }); export default composeAll( - composeWithTracker(composer), + compose(composer), useDeps(depsMapper) )(Component); diff --git a/client/modules/comments/index.js b/client/modules/comments/index.js index d715058..afffa7d 100644 --- a/client/modules/comments/index.js +++ b/client/modules/comments/index.js @@ -1,8 +1,10 @@ import methodStubs from './configs/method_stubs'; import actions from './actions'; +import reducers from './configs/reducers'; export default { actions, + reducers, load(context) { methodStubs(context); } diff --git a/client/modules/core/actions/posts.js b/client/modules/core/actions/posts.js index 0373396..f766c58 100644 --- a/client/modules/core/actions/posts.js +++ b/client/modules/core/actions/posts.js @@ -1,23 +1,33 @@ export default { - create({Meteor, LocalState, FlowRouter}, title, content) { + create({Meteor, Store, FlowRouter}, title, content) { if (!title || !content) { - return LocalState.set('SAVING_ERROR', 'Title & Content are required!'); + return Store.dispatch({ + type: 'SET_SAVING_POST_ERROR', + error: 'Title & Content are required!' + }); } - LocalState.set('SAVING_ERROR', null); + Store.dispatch({ + type: 'CLEAR_SAVING_POST_ERROR' + }); const id = Meteor.uuid(); // There is a method stub for this in the config/method_stubs // That's how we are doing latency compensation Meteor.call('posts.create', id, title, content, (err) => { if (err) { - return LocalState.set('SAVING_ERROR', err.message); + return Store.dispatch({ + type: 'SET_SAVING_POST_ERROR', + error: err.message + }); } }); FlowRouter.go(`/post/${id}`); }, - clearErrors({LocalState}) { - return LocalState.set('SAVING_ERROR', null); + clearErrors({Store}) { + return Store.dispatch({ + type: 'CLEAR_SAVING_POST_ERROR' + }); } }; diff --git a/client/modules/core/configs/reducers/index.js b/client/modules/core/configs/reducers/index.js new file mode 100644 index 0000000..871343d --- /dev/null +++ b/client/modules/core/configs/reducers/index.js @@ -0,0 +1,5 @@ +import posts from './posts'; + +export default { + posts +} diff --git a/client/modules/core/configs/reducers/posts.js b/client/modules/core/configs/reducers/posts.js new file mode 100644 index 0000000..6646ee4 --- /dev/null +++ b/client/modules/core/configs/reducers/posts.js @@ -0,0 +1,19 @@ +const defaultState = { SAVING_POST_ERROR: null}; +const postsReducer = (state = defaultState, action) => { + switch(action.type) { + case 'SET_SAVING_POST_ERROR': + return { + ...state, + SAVING_POST_ERROR: action.error + }; + case 'CLEAR_SAVING_POST_ERROR': + return { + ...state, + SAVING_POST_ERROR: null + }; + default: + return state; + } +}; + +export default postsReducer; \ No newline at end of file diff --git a/client/modules/core/containers/newpost.js b/client/modules/core/containers/newpost.js index b9798e7..706b73e 100644 --- a/client/modules/core/containers/newpost.js +++ b/client/modules/core/containers/newpost.js @@ -1,13 +1,29 @@ import NewPost from '../components/newpost.jsx'; -import {useDeps, composeWithTracker, composeAll} from 'mantra-core'; +import {useDeps, compose, composeAll} from 'mantra-core'; export const composer = ({context, clearErrors}, onData) => { - const {LocalState} = context(); - const error = LocalState.get('SAVING_ERROR'); + const {Store} = context(); + + // subscribe to state updates + // and keep handle to unsubscribe + const unsubscribe = Store.subscribe(() => { + const error = Store.getState().posts.SAVING_POST_ERROR; + onData(null, {error}) + }); + + // get initial state + const error = Store.getState().posts.SAVING_POST_ERROR; onData(null, {error}); - // clearErrors when unmounting the component - return clearErrors; + // function to unsubscribe from Store + // and clearing error + const cleanup = () => { + unsubscribe(); + clearErrors(); + }; + + // running cleanup when unmounting the component + return cleanup; }; export const depsMapper = (context, actions) => ({ @@ -17,6 +33,6 @@ export const depsMapper = (context, actions) => ({ }); export default composeAll( - composeWithTracker(composer), + compose(composer), useDeps(depsMapper) )(NewPost); diff --git a/client/modules/core/index.js b/client/modules/core/index.js index 3f48088..5c7a10b 100644 --- a/client/modules/core/index.js +++ b/client/modules/core/index.js @@ -1,10 +1,12 @@ import methodStubs from './configs/method_stubs'; import actions from './actions'; import routes from './routes.jsx'; +import reducers from './configs/reducers'; export default { routes, actions, + reducers, load(context) { methodStubs(context); } diff --git a/package.json b/package.json index 2e240ca..e64abdc 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "mantra-core": "^1.2.0", "react": "^0.14.6", "react-dom": "^0.14.6", - "react-mounter": "^1.0.0" + "react-mounter": "^1.0.0", + "redux": "^3.3.1" }, "private": true }