Skip to content

Instantly share code, notes, and snippets.

@PolGuixe
Last active June 1, 2017 16:13
Show Gist options
  • Save PolGuixe/0e056d17016be66e98329f815cff168f to your computer and use it in GitHub Desktop.
Save PolGuixe/0e056d17016be66e98329f815cff168f to your computer and use it in GitHub Desktop.
Mantra + Meteor + Redux
//client/configs/context.js
//Not using reactiveDict: LocalState
import * as Collections from '/lib/collections';
import {Meteor} from 'meteor/meteor';
import {FlowRouter} from 'meteor/kadira:flow-router-ssr';
import {Tracker} from 'meteor/tracker';
import {createStore, combineReducers} from 'redux';
export default function ({reducers}) {
const reducer = combineReducers({...reducers});
// put all the redux middlewares here
const Store = createStore(reducer, window.devToolsExtension ? window.devToolsExtension() : f => f);
// the last bit is to make the Redux-Dev-Tools extension to work.
return {
Meteor,
FlowRouter,
Collections,
Tracker,
Store,
dispatch: Store.dispatch // I also return the dispatch method so I can access it without importing the Store
};
}
//client/main.js
import {createApp} from 'mantra-core';
import initContext from './configs/context';
// modules
import coreModule from './modules/core';
// ... other modules
// combine all module reducers
const coreReducers = coreModule.reducers;
// ... other reducers
const reducers = {
...coreReducers,
//... other reducers
};
// init context
const context = initContext({reducers});
// create app
const app = createApp(context);
app.loadModule(coreModule);
//... load other modules
app.init();
// client/modules/core/actions/post.js
export default {
save({
Meteor,
Collections,
FlowRouter,
dispatch // get dispatch method
}, postId, name, description) {
let post = {};
// ClearErrors
dispatch({type: 'CLEAR_ERROR'}); // instead of using reactiveDict: LocalState I am using Redux dispatch
if (postId !== undefined) {
post = Collections.Posts.findOne(postId);
post = assignValues(post, name, description);
post.validate((err) => {
if (err) {
dispatch({type: 'ADD_ERROR', error: err.reason}); // instead of using reactiveDict: LocalState I am using Redux dispatch
} else {
Meteor.call('product.update', product, (err) => {
if (err) {
dispatch({type: 'ADD_ERROR', error: err.reason}); // instead of using reactiveDict: LocalState I am using Redux dispatch
}
});
}
});
} else {
postId = Random.id();
post = new Collections.Posts();
post._id = postId;
post = assignValues(post, name, description);
post.validate({
fields: ['name', 'description']
}, (err) => {
if (err) {
dispatch({type: 'ADD_ERROR', error: err.reason}); // instead of using reactiveDict: LocalState I am using Redux dispatch
} else {
Meteor.call('post.insert', product, (err) => {
if (err) {
dispatch({type: 'ADD_ERROR', error: err.reason}); // instead of using reactiveDict: LocalState I am using Redux dispatch
}
});
FlowRouter.go('post.edit', {docId: productId});
}
});
}
},
clearErrors({dispatch}) {
dispatch({type: 'CLEAR_ERROR'});
}
};
// client/modules/core/containers/post_edit.js
import {useDeps, composeAll, composeWithTracker, compose} from 'mantra-core';
import PostEdit from '../components/post_edit.jsx';
//I create a specific reduxComposer
// However it is the part I like less.
// I would prefer a definition more similar to mapsStateToProps(state){} as you can use with connect() from React-Redux
export const reduxComposer = ({
context
}, onData) => {
const {Store} = context(); //Get Store from context
onData(null, {error: Store.getState().postEdit.error}); //We need to call onData and send the state here, otherwise the we will render a loading screen
return Store.subscribe(() => { //We need to return the Store subscribe in order to make this container reactive with everystore changes
onData(null, {error: Store.getState().postEdit.error});
});
};
//I creat an specific meteorComposer
//This is the composer where I handle all the Meteor data, like subscriptions and collections.
//Here is where I used to handle reactiveDict: LocalState as well.
export const meteorComposer = ({
context,
productId,
clearErrors
}, onData) => {
const {Meteor, Collections, dispatch} = context();
if (postId) {
if (Meteor.subscribe('post.edit', postId, {
onStop: (err) => {
if (err) {
dispatch({type: 'ADD_ERROR', error: err.reason});
// I am event dispatching actions to the reducers from the container
// onData needs to be triggered otherwise the loading screen will be renderer
onData(null,{});
}
}
}).ready()) {
const postt = Collections.Posts.findOne({_id: postId});
onData(null, {post});
} else {
const post = Collections.Post.findOne({_id: postId});
if (post) { // post in Minimongo. Sent it to props
onData(null, {post});
} else { // retrieving post from server. Render loading screen
onData();
}
}
} else {
// onData needs to be triggered otherwise the loading screen will be renderer
onData(null, {});
}
};
//Obviusly I still need to map the actions to the container
export const depsMapper = (context, actions) => ({
context: () => context,
save: actions.products.save,
remove: actions.products.remove,
clearErrors: actions.products.clearErrors
});
//I use composeAll to compose both the meteorComposer, the reduxComposer, and the depsMapper with the UI component
export default composeAll(composeWithTracker(meteorComposer), compose(reduxComposer), useDeps(depsMapper))(PostEdit);
//client/modules/core/index.js
//Ensure you get and export the reducers
import methodStubs from './configs/method_stubs';
import actions from './actions';
import routes from './routes.jsx';
import reducers from './reducers';
export default {
routes,
actions,
reducers,
load(context) {
methodStubs(context);
}
};
// client/modules/core/reducers/index.js
//Hmmm... boilerplate
import postEdit from './post_edit';
export default {
postEdit
};
// client/modules/core/reducers/post_edit.js
const defaultState = {
error: ''
};
function postEdit(state = defaultState, action) {
switch (action.type) {
case 'ADD_ERROR':
return Object.assign({}, state, {error: action.error});
case 'CLEAR_ERROR':
return Object.assign({}, state, {error: ''});
default:
return state;
}
}
export default postEdit;
@smooJitter
Copy link

Have you used recompose? Id like to use recompose in place in place of compose and compose all from mantra

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment