Meteor + react + react router入門

#Meteor.jsとは
node.jsのフルスタックフレームワーク。なにかプロトタイプを作ろうと思った時にさくっと作ることができます。リアルタイムwebアプリが作れで、リアクティブにデータが反映されます。
これは、ページをリロードしなくても、データの更新があれば該当の値が更新されるものです。チャットアプリのような挙動です。それがチョー簡単につくれます。

#React.jsとは
FacebookがOSSとして公開していて、実際にプロダクトにも使用しているフロントエンドのフレームワークです。スタートアップ界隈でもよく使われています。このReactの概念さえ覚えてしまえば、React Nativeをつかってネイティブアプリの構築もすることができます。

参考：[出来る限り短く説明するReact.js入門](https://qiita.com/rgbkids/items/8ec309d1bf5e203d2b19)

#Meteor インストール (MAC)
これをターミナルで叩く

```
curl https://install.meteor.com/ | sh
```

#Meteor プロジェクト作成
プロジェクトを起きたいディレクトリまで移動して、
meteor create "アプリ名"で作成

```
meteor create sample-meteor-react
```

#Meteor 起動
プロジェクトのディレクトリに移動して、”meteor”と打てばサーバー起動

```
cd sample-meteor-react
meteor
```

[http://localhost:3000/](http://localhost:3000/)を開いて、”Welcome to Meteor!”の画面が表示されていれば成功

#React インストール

```
meteor npm install --save react react-dom
```

#セキュリティ上問題のある２つのパッケージを削除
meteorにはデフォルトで```insecure```と```autopublish```というパッケージが入っています。insecureはサイトに訪れたすべての人がDBを操作できるように初期設定されているもので、autopublishもサイトに訪れた人がすべての人がすべてのデータを見れるようにしているものです。
本番環境ではデータの閲覧、操作権限誰にでもできないようにサーバーサイドでチェックを入れるのですが、meteorは公開前のプロトタイピングがしやすいように、このような権限を全開放する初期設定になっています。
いずれ消すので、とっととその２つを消し去りましょう。

```
meteor remove insecure
meteor remove autopublish
```

#既存のテンプレートを削除
meteorにはデフォルトでblazeというテンプレートが入っています。今回はReactを使用するのでこちらを削除します。

```
meteor remove blaze-html-templates
meteor add static-html
```

#React用にMeteorのファイルを変更
##main.htmlを変更
id="render-target"の中にreactの要素が流し込まれます。

```client/main.html
<head>
  <title>sample-meteor-react</title>
</head>

<body>
  <div id="render-target"></div>
</body>

```

## main.jsを変更

```client/main.js
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
 
import RouteApp from '../imports/router/RouteApp.jsx';
 
Meteor.startup(() => {
  render(<RouteApp />, document.getElementById('render-target'));
});
```
ここのRouteAppというのは後で作る、ルーティング用のReactコンポネントです。今の段階でページにアクセスするとエラーが出るのですが、気にせんといて次に進んでください。

#React用のディレクトリ作成
importsフォルダを作成、中身はこのような感じ

```
imports/
  ├ router
  ├ ui
     └ components
     └ pages
```

#react-router-domインストール
[https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom](https://github.com/ReactTraining/react-router/tree/master/packages/react-router-dom)

```
npm install --save react-router-dom
```

#react routerを設定
router/RouteAppを編集

```router/RouteApp.jsx
import React, {Component} from 'react';
import {
  BrowserRouter as Router,
  Route,
  Switch,
} from 'react-router-dom';
import Top from '../ui/pages/Top.jsx';
import AnotherPage from '../ui/pages/AnotherPage.jsx';

export default class RouteApp extends Component{
  render(){
    return(
      <Router>
        <Switch>
          <Route exact path="/" component={Top}/>
          <Route exact path="/another" component={AnotherPage}/>
        </Switch>
      </Router>
    )
  }
}
```

これでルーティングの設定は完了です。
他のページを追加したいときも```<Switch></Switch>```の中に
``` <Route exact path="パス" component={表示するコンポネント}/>```
の形で追加していけばよいです。シンプルですね。

#Reactコンポネント作成
imports/ui/pagesの中に

- Top.jsx
- AnotherPage.jsx

を作成します。


```imports/ui/pages/Top.jsx
import React, {Component} from "react";

export default class Top extends Component{
  render(){
    return(
      <div>topページ</div>
    )
  }
}
```

```imports/ui/pages/AnotherPage.jsx
import React, {Component} from "react";

export default class AnotherPage extends Component{
  render(){
    return(
      <div>違うページ</div>
    )
  }
}
```

#ページ確認
これで、
[http://localhost:3000/](http://localhost:3000/)
[http://localhost:3000/another](http://localhost:3000/another)
にアクセスして、さっき作ったページが表示されていればOKです！
ページ遷移はこれで完璧です。

#Meteorでのデータの扱い
ここからは、meteorでのデータの扱うために、サンプルでto doアプリを作成します。

## collectionの作成
collectionとは、SQLのテーブルのようなものです。
- imports/apiフォルダを作成
- apiフォルダの中にcollections.jsを作成

```imports/api/collections.js
import {Mongo} from 'meteor/mongo'

export const Todos = new Mongo.Collection('todo');
```

これをserver/main.jsにてimport

```server/main.js
import { Meteor } from 'meteor/meteor';
import '../imports/api/collections.js';

Meteor.startup(() => {
  // code to run on server at startup
});
```

##Methodの作成
collectionが作成できたので、次はメソッドの作成です。
メソッドとは、実査にDBへの処理記述するもので、MVCでいうモデルに該当します。

- imports/api/にmethods.jsを作成

```imports/api/methods.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { Todos } from './collections.js';

Meteor.methods({
  'addTodos'(){
    todo = {
      date:new Date(),
      content:"test!"
    };
      Todos.insert(todo);
  },
})
```

ここでは、todoリストにデータを挿入するメソッドを作成しました。

そして、コレクションのときと同じようにserver/main.jsにてimport

```server/main.js
import { Meteor } from 'meteor/meteor';
import '../imports/api/collections.js';
import '../imports/api/methods.js';

Meteor.startup(() => {
  // code to run on server at startup
});
```


##メソッドを実行する方法
Top.jsxを以下のように編集します

```imports/ui/pages/Top.jsx
import React, {Component} from "react";

export default class Top extends Component{
  addTodo(){
    Meteor.call('addTodo')
  }

  render(){
    return(
      <div>
        <div>topページ</div>
        <a onClick={(e)=>this.addTodo(e)}>to do リストに追加</a>
      </div>
    )
  }
}
```

これで、to do リストに追加をクリックすれば、データが追加できるようになりました。

#データの表示
Meteorはリアクティブなフレームワークです。ですが、全ての情報をクライアント側に送るわけにはいかないので、どの情報を送るかどうかをこちらで管理しなければいけません。それを制御するために、以下のファイルを作成します。

```impots/api/server/main.js
import { Meteor } from 'meteor/meteor';
import { Todos } from '../collections.js';

if(Meteor.isServer){
  Meteor.publish('getTodos', function(){
     return Todos.find({});
  });
}
```

今回はすべてのtodoリストをクライアント側に返すものを作成しました。
返したいものをreturnするだけです。

また、これもserver/main.jsにimportします

```server/main.js
import { Meteor } from 'meteor/meteor';
import '../imports/api/collections.js';
import '../imports/api/methods.js';
import '../imports/api/server/publications.js';

Meteor.startup(() => {
  // code to run on server at startup
});

```

#to doリストの表示

MeteorのデータをReactに流し込むためには、withTrackerを使えるようにする必要がります。そのためにreact-meteor-dataをインストール

```
meteor add react-meteor-data
```

今回はtopページに表示するので、Top.jsxを以下のように編集

```imports/ui/pages/Top.jsx
import React, {Component} from "react";
import { withTracker } from 'meteor/react-meteor-data';
import {Todos} from '../../api/collections.js';

class Top extends Component{
  addTodo(){
    Meteor.call('addTodo')
  }

  renderTodos(){
    let todos = this.props.todos;
    if(todos){
      return todos.map((todo,index)=>{
        return(
          <li key={index}>
            {todo.content}
          </li>
        );
      });
    }
  }

  render(){
    return(
      <div>
        <div>topページ</div>
        <a onClick={(e)=>this.addTodo(e)}>to do リストに追加</a>
        <ul>
        {this.renderTodos()}
        </ul>
      </div>
    )
  }
}

export default withTracker(()=>{
  let todos;

  if(Meteor.subscribe('getTodos').ready()){
    todos = Todos.find().fetch();
  }

  return{
    todos:todos
  }
})(Top);
```

withTrackerでreturnしたデータはpropsとして参照することができます。
先程、定義したものを

```imports/api/server/publication.js
Meteor.publish('getTodos', function(){
     return Todos.find({});
  });

```

このように呼び出すことができます。
```Meteor.subscribe('getTodos')```

また、ready()をつけることによって、データのsubscribeの準備ができたかどうかを確認しています。react + meteorの組み合わせだと、このサブスクライブの準備ができるまでに一回renderされるので、チェックが必要です。

これで、”to do リストに追加”をクリックする度にどんどん”test!”という項目が追加されていっているかと思います。

#Methodに引数を渡して、入力した値をDBに格納
このままだと、同じ文章が並ぶので、入力欄を設けて、メソッドに引数を渡して処理をする方法がこちらです。

まずはformを作成し、送信できるようにTop.jsxを修正

```imports/ui/pages/Top.jsx
import React, {Component} from "react";
import { withTracker } from 'meteor/react-meteor-data';
import {Todos} from '../../api/collections.js';

class Top extends Component{
  constructor(props){
    super(props);
    this.state = {
      todoText:"",
    }
  }

  addTodo(){
    Meteor.call('addTodo')
  }

  renderTodos(){
    let todos = this.props.todos;
    if(todos){
      return todos.map((todo,index)=>{
        return(
          <li key={index}>
            {todo.content}
          </li>
        );
      });
    }
  }

  submitForm(e){
    e.preventDefault();
    let todoText = this.state.todoText;
    Meteor.call('addTodo',todoText);
  }

  render(){
    return(
      <div>
        <div>topページ</div>
 

        <form onSubmit={(e)=>this.submitForm(e)}>
          <input type="text" onChange={(e)=>this.setState({todoText:e.target.value})} value={this.state.todoText}/>
          <input type="submit" value="リストに追加"/>
        </form>
        <a onClick={(e)=>this.addTodo(e)}>to do リストに追加</a>
        <ul>
        {this.renderTodos()}
        </ul>
      </div>
    )
  }
}

export default withTracker(()=>{
  let todos;

  if(Meteor.subscribe('getTodos').ready()){
    todos = Todos.find().fetch();
  }

  return{
    todos:todos
  }
})(Top);
```

次に、methods.jsも変更

``` imports/api/methods.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { Todos } from './collections.js';

Meteor.methods({
  'addTodo'(content){
    var todo = {
      date:new Date(),
      content:content
    };
    Todos.insert(todo);
  },
})
```


メソッドは第二引数以降で、引数を渡せます。
```Meteor.call('メソッド名',args...)```

また、Meteor.subscribeも第二引数以降で引数を渡せます。
```Meteor.subscription('パブリッシュ名',args...)```

#完成
これで自由に値の入力ができるようになりました。
todoリストとしては編集も削除もできず、すべてのユーザに同じものが表示されるゴミですが、
```reactでイベント作る→メソッドを作成してそれを呼び出す→メソッド内でmongoDBを操作```
で同じようにDBの処理ができ、
```表示したいデータをpublication.jsで定義→それを使用するページでMeteor.subscribe('publications.js定義した関数名')で呼び出し```
でデータの受け取りを行うことができます。
例えば、指定したuserIdのtodoしかか表示しない場合は、Meteor.subscribeにuser引数を渡して、

```
Meteor.publish('getTodos', function(userId){
     return Todos.find({userId:userId});
  });
```
のようにすればOkです。

#まとめ
meteorの解説中心となってしまいました。Reactは他の方々がわかりやすい説明をしているので、ググってそちらを見ていただくほうが良いですな。
React + meteor + react routerの組み合わせはこのようにサクッとプロダクトを作れるので、まだまだ駆け出しで、jsしか知らない人がさくっと高速開発するのにはおすすめです。（ただ、デプロイちょっと面倒です。今度書きます。）
あとは、使う場面があれば、リアルタイムDBもすごく魅力的。

今回のコードはこちらです。
[https://github.com/keitaisozaki/sample-meteor-react](https://github.com/keitaisozaki/sample-meteor-react)

日本はあまり使っている人に出会ったことがないのですが、使う人増えたら嬉しいです:relaxed: