Last active
June 1, 2017 16:13
-
-
Save PolGuixe/0e056d17016be66e98329f815cff168f to your computer and use it in GitHub Desktop.
Mantra + Meteor + Redux
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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'}); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// client/modules/core/reducers/index.js | |
//Hmmm... boilerplate | |
import postEdit from './post_edit'; | |
export default { | |
postEdit | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Have you used recompose? Id like to use recompose in place in place of compose and compose all from mantra