【问题标题】:How to make React and Meteor fit together如何使 React 和 Meteor 结合在一起
【发布时间】:2014-04-06 13:06:54
【问题描述】:

我不知道是 React 还是 Meteor 问题,也许两者都有,但我目前正在使用这两个框架构建一个网络应用程序,我正面临程序员的问题。我不是 Javascript 开发人员,而是 Java 开发人员(我每天使用 GWT),所以也许我犯了一些菜鸟错误。

我的应用程序不断增长,我有越来越多的 React 组件,大约二十个左右。既然我的观点很好,我已经为我的组件添加了一些功能,但事实证明我在 react 组件中添加了越来越多的逻辑,我认为这违反了 MVC 原则。

但是,我不知道如何移动“流星控制器组件”中的逻辑。现在,我使用 Meteor 作为模型,仅此而已。我多次看到这个Pete Hunt's talk 以及他如何构建他的应用程序,但它只有一个“简单”组件。

事实上,如果没有 React,视图将在 html 文件中,使用模板定义。控制器将在 js 文件中,并且逻辑似乎在那里。我可以清楚地看到 viewcontroller 之间的分割

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 应用程序的约定,有什么要遵循的准则吗?我不是在寻找“最好”的方法,而是在寻找一些意见。

注意:我故意没有在这里放很多代码以不专注于它,但可以随意用任何你想要的东西(代码、架构、链接......)来说明你的答案。


这是我正在做的一个例子。在这个例子中,一切都在反应类中完成,也许这是最好的方法,也许不是,我需要你的想法。

总而言之,它从作为输入的数组(类似于 [{name: itemName1, type:itemType1}, {name: itemName2, type:itemType2} ... ] 生成如下视图:

  • 项目名称1
  • 项目名称2
  • ...

每个项目根据其类型作为自己的样式。然后通过输入文本框,用户可以通过这个列表进行搜索,它过滤列表并生成一个由匹配元素组成的新列表(搜索算法不正确,将被更改)。此外,还有一些带有特定键盘键的附加命令。一切正常,但你可以注意到,一切都在反应类中,我不知道如何将 Meteor 与 React 结合起来。

流星文件:

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

反应文件:

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

【问题讨论】:

  • 你能把你目前得到的东西发布到公共回购吗?
  • 我在帖子中添加了我主要项目中的一段代码。希望它足够可读。
  • 如果您可以将问题的复制品上传到公共存储库,您可能会更幸运地获得帮助。
  • 在将两者放在一起方面,这可能会有所帮助 - Peter Hunt,React 和 Meteor + 函数式编程 - youtube.com/watch?v=qqVbr_LaCIo

标签: javascript meteor reactjs


【解决方案1】:

您的方法是合理的:Meteor 用于模型,React 用于 View 和 ViewController。

任何与表示无关的功能都应该在模型中(业务逻辑、验证规则等)。

与呈现和响应用户输入有关的任何事情都应该在 React 中(输入、验证输出等)。

【讨论】:

    【解决方案2】:

    今天,您可以考虑一下这段代码的和平: https://github.com/reactjs/react-meteor

    这个存储库定义了一个 Meteor 包,它在客户端和服务器上自动集成 React 渲染框架,以补充或替换默认的 Handlebars 模板系统。

    【解决方案3】:

    从 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

    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 包。

    阅读更多the official Meteor guide

    【讨论】:

      猜你喜欢
      • 2016-12-31
      • 2017-09-08
      • 2013-04-13
      • 2022-01-23
      • 2016-08-17
      • 1970-01-01
      • 1970-01-01
      • 2017-01-12
      • 1970-01-01
      相关资源
      最近更新 更多