From 547a7519185330a7fecd05eb3e5ea860b5e2e306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Po=C3=A7a?= Date: Tue, 20 Sep 2016 15:38:42 +0100 Subject: [PATCH] Updated version Why: In this version I'm using the package `meteor-ditto`. It's a nice abstraction that implements the system I talk about in the blog post. This also solves the "loading" message, which wasn't working on the previous demo app. --- .gitignore | 3 +- README.md | 14 ++++++-- client/actions/actions.js | 6 ++++ .../components/MessagesList/MessagesList.jsx | 18 ++++++++-- client/containers/App.jsx | 25 +++++++++---- client/helpers/SubscribeComponent.jsx | 35 ------------------- client/reducers/reducers.js | 12 ++----- client/store/createStore.js | 16 ++++----- package.json | 1 + server/methods.js | 8 +++++ 10 files changed, 72 insertions(+), 66 deletions(-) delete mode 100644 client/helpers/SubscribeComponent.jsx diff --git a/.gitignore b/.gitignore index 40b878d..d5733f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +.tern-port diff --git a/README.md b/README.md index 50d0078..aa21365 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,16 @@ Meteor Redux Demo ================= -This repository contains a demonstration application for the article [A bridge between React and Meteor](https://subvisual.co/blog/posts/79-working-with-meteor-react-and-redux). In this application you can write and see messages. If you try to write an empty message you'll see an error. +This repository contains a demonstration application for the article [A bridge +between React and +Meteor](https://subvisual.co/blog/posts/79-working-with-meteor-react-and-redux). +In this application you can write, remove, and see messages. If you try to write an empty +message you'll see an error. While the messages are loading you see a +"loading" message. -The purpose of this application is to demonstrate a possible setup for Redux on Meteor, most code written here is not acceptable on production application. +The purpose of this application is to demonstrate a possible setup for Redux on +Meteor, most code written here is not acceptable on production application. + +This application uses the package +[meteor-ditto](https://github.com/gabrielpoca/meteor-ditto). It's a working in +progress implementing the system I talk about in the blog post. diff --git a/client/actions/actions.js b/client/actions/actions.js index cad0b33..dda33bc 100644 --- a/client/actions/actions.js +++ b/client/actions/actions.js @@ -12,3 +12,9 @@ export const createMessage = params => { }); }; }; + +export const removeMessage = id => { + return () => { + Meteor.call('removeMessage', id); + }; +}; diff --git a/client/components/MessagesList/MessagesList.jsx b/client/components/MessagesList/MessagesList.jsx index 5490415..758f2e9 100644 --- a/client/components/MessagesList/MessagesList.jsx +++ b/client/components/MessagesList/MessagesList.jsx @@ -1,17 +1,29 @@ import React, { Component, PropTypes } from 'react'; class MessagesList extends Component { - renderMessage(message, index) { - return
  • {message.text}
  • ; + renderMessage({ _id, text }, index) { + return ( +
  • + {text} this.props.onRemove(_id)}>Remove +
  • + ); + } + + renderLoading() { + return

    Loading messages...

    ; } render() { - return ; + if (!this.props.ready) + return this.renderLoading(); + return ; } } MessagesList.propTypes = { messages: PropTypes.array.isRequired, + onRemove: PropTypes.func.isRequired, + ready: PropTypes.bool, }; export default MessagesList; diff --git a/client/containers/App.jsx b/client/containers/App.jsx index ece3ce9..e97f53f 100644 --- a/client/containers/App.jsx +++ b/client/containers/App.jsx @@ -1,10 +1,10 @@ import React, { Component, PropTypes } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import { createMessage } from '../actions/actions'; +import { createMessage, removeMessage } from '../actions/actions'; import { reset } from 'redux-form'; -import SubscribeComponent from '../helpers/SubscribeComponent'; +import { SubscriptionComponent } from 'meteor-ditto'; import MessagesList from '../components/MessagesList/MessagesList'; import MessagesEditor from '../components/MessagesEditor/MessagesEditor'; import Errors from '../components/Errors/Errors'; @@ -19,10 +19,18 @@ class App extends Component { this.props.reset('new-message'); } + handleRemove(id) { + this.props.removeMessage(id); + } + render() { return (
    - + @@ -34,18 +42,23 @@ class App extends Component { App.propTypes = { subscribe: PropTypes.func.isRequired, + subscriptionReady: PropTypes.func.isRequired, createMessage: PropTypes.func.isRequired, + removeMessage: PropTypes.func.isRequired, reset: PropTypes.func.isRequired, messages: PropTypes.array.isRequired, errors: PropTypes.array.isRequired, }; const mapStateToProps = state => { - return { messages: state.messages, errors: state.errors }; + return { + messages: state.mongo.collections.messages || [], + errors: state.errors, + }; }; const mapDispatchToProps = dispatch => { - return bindActionCreators({ createMessage, reset }, dispatch); + return bindActionCreators({ createMessage, removeMessage, reset }, dispatch); }; -export default connect(mapStateToProps, mapDispatchToProps)(SubscribeComponent(App)); +export default connect(mapStateToProps, mapDispatchToProps)(SubscriptionComponent(App)); diff --git a/client/helpers/SubscribeComponent.jsx b/client/helpers/SubscribeComponent.jsx deleted file mode 100644 index f924d71..0000000 --- a/client/helpers/SubscribeComponent.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import React, { Component } from 'react'; - -export default ComposedComponent => class extends Component { - constructor() { - super(); - this.subs = {}; - } - - subscribe(name, ...args) { - if (this.subs[name]) - this.subs[name].stop(); - - this.subs[name] = Meteor.subscribe(name, ...args); - } - - subscriptionReady(name) { - if (this.subs[name].ready()) - return this.subs[name].ready(); - } - - componentWillUnmount() { - Object.keys(this.subs).map(key => this.subs[key].stop()); - } - - render() { - return ( - - ); - } -}; diff --git a/client/reducers/reducers.js b/client/reducers/reducers.js index 526ba12..c70b7b6 100644 --- a/client/reducers/reducers.js +++ b/client/reducers/reducers.js @@ -1,14 +1,6 @@ import { combineReducers } from 'redux'; import { reducer as formReducer } from 'redux-form'; - -const messagesReducer = (state = [], action) => { - switch (action.type) { - case 'SET_MESSAGES': - return action.messages; - default: - return state; - } -}; +import { mongo } from 'meteor-ditto'; const errorsReducer = (state = [], action) => { switch (action.type) { @@ -20,7 +12,7 @@ const errorsReducer = (state = [], action) => { }; export default combineReducers({ - messages: messagesReducer, errors: errorsReducer, form: formReducer, + mongo, }); diff --git a/client/store/createStore.js b/client/store/createStore.js index c9a37cd..6169998 100644 --- a/client/store/createStore.js +++ b/client/store/createStore.js @@ -1,19 +1,17 @@ -import { Tracker } from 'meteor/tracker'; -import { createStore, applyMiddleware } from 'redux'; +import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; +import { connect as connectCollection } from 'meteor-ditto'; import reducers from '../reducers/reducers'; import Messages from '../../lib/messages'; export default () => { - const store = createStore(reducers, applyMiddleware(thunk)); + const store = createStore(reducers, compose( + applyMiddleware(thunk), + window.devToolsExtension ? window.devToolsExtension() : fn => fn, + )); - Tracker.autorun(() => { - store.dispatch({ - type: 'SET_MESSAGES', - messages: Messages.find().fetch(), - }); - }); + connectCollection(Messages, store); return store; }; diff --git a/package.json b/package.json index 180344c..ea2829b 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ }, "dependencies": { "lodash": "^4.11.1", + "meteor-ditto": "0.0.4", "meteor-node-stubs": "~0.2.0", "react": "^15.0.1", "react-dom": "^15.0.1", diff --git a/server/methods.js b/server/methods.js index 0c3af96..b1ff9ca 100644 --- a/server/methods.js +++ b/server/methods.js @@ -8,6 +8,14 @@ const createMessage = function(params) { Messages.insert(params); }; +const removeMessage = function(_id) { + if (!_id) + throw new Meteor.Error('id missing', 'An id is needed to remove a message'); + + Messages.remove({ _id }); +}; + Meteor.methods({ createMessage, + removeMessage, });