如何让React和Meteor配合使用

27

我不确定这更多是关于React还是Meteor的问题,也许两者都是,但我目前正在使用这两个框架构建一个Web应用程序并遇到了编程问题。我不是JavaScript开发人员,而是Java开发人员(我每天使用GWT),因此可能犯了一些新手错误。

我的应用程序不断增长,我有越来越多的React组件,大约有二十个左右。现在我已经拥有了一个良好的视图,我已经为我的组件添加了一些功能,但结果是我在React组件中添加了越来越多的逻辑,我相信这违反了MVC原则。

然而,我不知道如何将逻辑移动到“Meteor控制器组件”中。目前,我只使用Meteor作为其模型。我看过很多次这篇Pete Hunt的演讲以及他如何构建应用程序,但它只有一个“简单”的组件。

实际上,如果没有React,视图将在HTML文件中定义为模板。控制器将在JS文件中,逻辑将出现在那里。我可以清楚地看到视图和控制器之间的分离。

HTML文件(来自排行榜示例):

<template name="leaderboard">
  ...
</template>

<template name="player">
  <div class="player {{selected}}">
    ...
  </div>
</template>

Javascript文件(来自排行榜示例):

...     
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;
 };
...

由于React是Javascript编写的,因此很容易而且很诱人地把我们想要的所有内容都放在React组件中。

我知道这些框架对每个人来说相对较新,但我想知道是否存在一些关于如何设计这样一个MVC应用程序的约定,以便拥有灵活和可维护的Web应用程序,是否有任何指导方针可以遵循? 我不是在寻找“最佳”方法,而是在寻找一些意见。

备注:我故意没有放太多代码在这里,以免重点集中在它上面,但你可以随意使用任何你想要的东西来说明你的答案(代码、图表、链接等)。


这是我正在做的一个示例。在这个例子中,所有的事情都是在React类中完成的,也许这是最好的方法,也许不是,我需要你们的想法。

总之,它从作为输入的数组(类似[{name: itemName1, type:itemType1}, {name: itemName2, type:itemType2} ...]的东西)创建了一个元素列表(Boostrap列表组),生成了一个视图,如下所示:

  • itemName1
  • itemName2
  • ...

每个项目都有其自己的样式,根据其类型进行设置。然后通过输入文本框,用户可以在此列表中进行搜索,它会过滤列表并生成一个由匹配元素组成的新列表(搜索算法不正确,将被更改)。此外,还有某些键盘按键的附加命令。一切都很好,但是你可以注意到,所有的东西都在React类中,我无法弄清楚如何将Meteor与React结合使用。

Meteor文件:

if (Meteor.isClient) {
  Meteor.startup(function() {
    //Build the view
    React.renderComponent(
      <Search items={initialItems}/>,
      document.getElementById('main')
      );
  });
}

React文件:

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>
            );
    }
});

你能把你目前的代码发布到公共仓库吗? - alanning
2
我在我的帖子中添加了一段来自我的主要项目的代码。希望它足够易读。 - Paul
如果您能将问题的复现上传到公共存储库中,那么您可能会更容易地获得帮助。 - alanning
就将这两者结合起来而言,这个视频或许会有所帮助- Peter Hunt, React 和 Meteor + 函数式编程 - https://www.youtube.com/watch?v=qqVbr_LaCIo - CodeWalrus
3个回答

7
你的方法是正确的:使用Meteor作为模型,React作为视图和控制器。
任何与演示无关的功能应该放在模型中(业务逻辑、验证规则等)。
与展示和响应用户输入有关的内容应该放在React中(输入、验证输出等)。

2
今天你可以考虑这个不错的代码: https://github.com/reactjs/react-meteor 这个代码库定义了一个Meteor包,自动将React渲染框架集成到客户端和服务器上,以补充或替换默认的Handlebars模板系统。

1
博客文章位于http://blog.percolatestudio.com/engineering/reactive-user-interfaces/。 - Dan Dascalescu

2

从Meteor 1.3开始,官方支持使用React与Meteor集成。因此,对于今天遇到这个问题的任何人,以下是Meteor 1.3+的答案:

要将React用作Meteor的视图层,请首先从npm添加React包:

meteor npm install --save react react-dom

现在你可以轻松导入React并在你的项目中使用它。要呈现一个简单的React组件,请创建一个简单的HTML容器: client/main.html
<body>
  <div id="app"></div>
</body>

在其中渲染你的React组件:

client/main.jsx

(注:此处翻译保留了原文中的HTML标签,不做详细解释。)
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';

class HelloWorld extends React.Component {
  render() {
    return <p>Hello World!</p>;
  }
}

Meteor.startup(() => {
  render(<HelloWorld />, document.getElementById('app'));
});

要在React组件内使用响应式Meteor数据源,例如Minimongo集合,请使用react-meteor-data包。

官方Meteor指南中了解更多信息


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接