Step9: MeteorのPublish/SubscribeをReactで使おう!

このステップではMeteorのPublish/Subscribe機構をReactで使う方法を学びます。

Publish / Subscribe

これまでコレクションにデータを追加すると、自動的に全てのクライアントにデータが送られていました。これはMeteorのアプリを新規作成した時に入っているautopublishというパッケージのおかげです。

しかし、データベースのデータ量が多くなった時に取得するデータ量を制限したり、ユーザー毎にプライベートなデータを持たせ、他のユーザーにはデータを配信しないようにする等の制御が必要になることがあります。

その時に使えるのが Meteor の Publish / Subscribe という仕組みです。

Publish / Subscribe の機能を使うには、autopublishパッケージを外す必要があります。以下のコマンドを実行しましょう。

$ meteor remove autopublish

続いて、MemosコレクションのPublish設定をサーバー側で行います。

$ mkdir -p imports/api/memos/server
$ touch imports/api/memos/server/publications.js

imports/api/memos/server/publications.js

import { Meteor } from 'meteor/meteor';
import { Memos } from '../memos.js';

Meteor.publish('memos.all', function memosAll() {
  return Memos.find({});
});

サーバーのmain.jsでpublicationsを読み込みます。

server/main.js

import { Meteor } from 'meteor/meteor';
import { Memos } from '../imports/api/memos/memos';
import '../imports/api/memos/methods';
import '../imports/api/memos/server/publications';

Meteor.startup(() => {
  if (Memos.find().count() === 0) {
    const data = [
      {content: 'Memo 1'},
      {content: 'Memo 2'},
      {content: 'Memo 3'},
      {content: 'Memo 4'},
      {content: 'Memo 5'},
    ];
    data.forEach(memo => Memos.insert(memo));
  }
});

Publish設定が完了したら、AppContainerでSubscribeを行います。

imports/ui/containers/AppContainer.js

import AppLayout from '../layouts/AppLayout';
import { Memos } from '../../api/memos/memos';
import { createContainer } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';
import '../../api/memos/methods';

const createMemo = content => {
  Meteor.call('Memos.insert', content);
};

const removeMemo = memoId => {
  Meteor.call('Memos.remove', memoId);
};

const updateMemoContent = (memoId, content) => {
  Meteor.call('Memos.update', memoId, content);
};

export default createContainer(() => {
  const memosHandle = Meteor.subscribe('memos.all');
  return {
    loading: !memosHandle.ready(),
    memos: Memos.find({}, {sort: {createdAt: -1}}).fetch(),
    createMemo,
    removeMemo,
    updateMemoContent,
  };
}, AppLayout);

最後にAppLayoutでSubscribe中のローディング表示を追加します。

imports/ui/layouts/AppLayout.js

import React from 'react';
import Header from '../components/Header';
import MemoList from '../components/MemoList';

export default class AppLayout extends React.Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { memos, createMemo } = this.props;
    createMemo(`New memo ${memos.length + 1}`);
  }

  render() {
    const { memos, removeMemo, updateMemoContent, loading } = this.props;
    if (loading) {
      return this.renderLoading();
    }
    return (
      <div className="container">
        <Header />
        <button className="add-button" onClick={this.onClick}>Add</button>
        <MemoList
          memos={memos}
          removeMemo={removeMemo}
          updateMemoContent={updateMemoContent}
        />
      </div>
    );
  }

  renderLoading() {
    return (
      <div className="container">
        <div className="loading">Now Loading...</div>
      </div>
    );
  }
}

AppLayout.propTypes = {
  loading: React.PropTypes.bool,
  memos: React.PropTypes.array.isRequired,
  createMemo: React.PropTypes.func.isRequired,
  removeMemo: React.PropTypes.func.isRequired,
  updateMemoContent: React.PropTypes.func.isRequired,
};

ブラウザでリロードを行うと、ローディングが表示された後、メモの一覧が表示されるようになります。

次へ