I don't know if it's more a React or Meteor concern, maybe both, but I am currently building a web app with these two frameworks and I am facing a programmer issue. I am not a Javascript developer but a Java developer (I use daily GWT), so maybe I made some rookie mistakes.
My app keeps growing and I have more and more React components, about twenty or so. Now that I have a view that is fine, I have added some functionalities to my components but it turns up I add more and more logic in the react components which is, I believe, against MVC principles.
However, I don't know how to move the logic in "Meteor controller components". Right now, I use Meteor for its model and that's just about all. I saw many times this Pete Hunt's talk and how he built his application but it has only one 'simple' component.
In fact, without React, the view would be in html files, defined with templates. The controller would be in js files and the logic will appear to be there. I can clearly see the split between the view and the controller.
Html file (from leaderboard example):
<template name="leaderboard"> ... </template> <template name="player"> <div class="player {{selected}}"> ... </div> </template>
Javascript file (from leaderboard example):
... Template.leaderboard.players = function () { return Players.find({}, {sort: {score: -1, name: 1}}); }; Template.leaderboard.selected_name = function () { var player = Players.findOne(Session.get("selected_player")); return player && player.name; }; ...
Since React is javascript, it's really easy and tempting to put all we want in React components.
I am aware of these frameworks are relatively new for everybody but I wonder wheter some conventions exist about how to design such an MVC application in order to have a flexible and maintainable web application, any guidelines to follow? I am not looking for the 'best' way to do it but for some opinions.
Note: I deliberately didn't put a lot of code here to not be focus on it but feel free to illustrate your answer with whatever you want (code, schema, links...).
Here is an example of what I am doing. In this example, everything is done in react classes, maybe it's a the best way to do it, maybe not, I need your thoughts.
To sum up, it creates a list of elements (Boostrap list group) from an array given as input (something like [{name: itemName1, type:itemType1}, {name: itemName2, type:itemType2} ...] which generates a view like:
Each item as its own style according to its type. Then through the input text box, user can make a search trough this list, it filters the list and generates a new one that composed with the matching elements (the search algorithm is not right and will be changed). Plus, there are additional commands with certain keyboard key. Everything works fine but as you can notice, all is in react classes, I don't figure out how to fit Meteor with React.
Meteor file:
if (Meteor.isClient) { Meteor.startup(function() { //Build the view React.renderComponent( <Search items={initialItems}/>, document.getElementById('main') ); }); }
React file:
Search = React.createClass({ getInitialState : function() { return ( { items : flat(this.props.items), active : 0 } ); }, setListVisibility: function(visibility) { this.refs.list.getDOMNode().style.visibility = visibility; }, onchangeHandler: function() { var re = new RegExp(this.refs.search.getDOMNode().value, "i"); var res = []; //filter on props.items and not state.items flat(this.props.items).map(function(item){ if(item.name.search(re) >= 0) res.push(item); }); this.setState({ items : res, active : 0}); }, onkeydownHandler: function(event){ if(event.key == "ArrowDown" || event.key == "ArrowUp"){ var shift = event.key == "ArrowDown" ? 1 : -1; var newActive = this.state.active + shift; if(newActive >= 0 && newActive < this.state.items.length) this.setState({ active : this.state.active + shift }); } else if(event.key == "ArrowRight"){ if(this.state.items.length > 0){ var item = this.state.items[this.state.active]; var newItems = retrieveItem(this.props.items, item.name, typeToSubType[item.type]); newItems = flat(newItems); if(newItems.length > 0) this.setState({ items : newItems, active : 0 }); } } else if(event.key == "ArrowLeft"){ this.setState({ items : flat(this.props.items), active : 0}); } else if(event.key == "Enter"){ if(this.state.items.length > 0){ var item = this.state.items[this.state.active]; console.log("Add "+item.name+" "+item.type+" to the view"); } } }, render: function () { return ( <div> <nav className="navbar navbar-default" role="navigation"> <div className="container-fluid"> <div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <form className="navbar-form navbar-left" role="search"> <div className="form-group"> <input ref="search" type="text" className="form-control" placeholder="Search" size="100" onChange={this.onchangeHandler} onKeyDown={this.onkeydownHandler} onFocus={this.setListVisibility.bind(this, "visible")} onBlur={this.setListVisibility.bind(this, "hidden")}/> </div> </form> </div> </div> </nav> <List ref="list" items={this.state.items} active={this.state.active}/> </div> ); } }); List = React.createClass({ render: function () { var createItem = function(item, index) { var cl = "list-group-item"; if(index == this.props.active) cl += " active"; var gly = "glyphicon "; switch(item.type){ case "dimension": gly += "glyphicon-certificate"; break; case "hierarchy": gly += "glyphicon-magnet"; break; case "level": gly += "glyphicon-leaf"; break; case "measure": gly += "glyphicon-screenshot"; break; } return (<a href="#" className={cl} key={item.type+"/"+item.name}> <span className={gly}></span>{" "}{item.name} </a>); }; return ( <div className="list-group search-list"> {this.props.items.map(createItem, this)} </div> ); } });
By default Meteor already uses React when you create a new app using meteor create my-app then this basic set up will be already ready for you.
Sure it's ok. all in all it's HTML in the end. React components are set of html elements when you call the render function.
React uses an HTML-in-JavaScript syntax called JSX (JavaScript and XML). Familiarity with both HTML and JavaScript will help you to learn JSX, and better identify whether bugs in your application are related to JavaScript or to the more specific domain of React.
Your approach is sound: Meteor for the model and React for the View and ViewController.
Anything functionality that has nothing to do with presentation should be in the model (business logic, validation rules, etc).
Anything to do with presentation and responding to user input should be in React (input, validation output, etc).
Today you could consider this nice peace of code: https://github.com/reactjs/react-meteor
This repository defines a Meteor package that automatically integrates the React rendering framework on both the client and the server, to complement or replace the default Handlebars templating system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With