Step6: メモの更新機能を追加しよう!

このステップではMeteorコレクションの更新方法について学びます。

メモのテキストエリア化

現在の状態でもメモの編集はできますが、ブラウザをリロードすると元に戻ってしまいます。また複数クライアント間で同期されません。テキストエリアのchangeイベントを検知してMemosコレクションを更新するように設定してみましょう。

AppContainerにメモ更新用のfucntion updateMemoContent()を追加します。

imports/ui/components/containers/AppContainer.js

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

const createMemo = content => {
  Memos.insert({content});
};

const removeMemo = memoId => {
  Memos.remove({_id: memoId});
};

const updateMemoContent = (memoId, content) => {  
  Memos.update({_id: memoId}, {$set: {content}}); 
};                                                

export default createContainer(() => {
  return {
    memos: Memos.find({}, {sort: {createdAt: -1}}).fetch(),
    createMemo,
    removeMemo,
    updateMemoContent,
  };
}, AppLayout);

AppLayout->MemoList->MemoItemの順でMemoItemのpropsに渡します。

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 } = this.props;
    return (
      <div className="container">
        <Header />
        <button className="add-button" onClick={this.onClick}>Add</button>
        <MemoList
          memos={memos}
          removeMemo={removeMemo}
          updateMemoContent={updateMemoContent}
        />
      </div>
    );
  }
}

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

imports/ui/components/MemoList.js

import React from 'react';
import MemoItem from './MemoItem';

export default class MemoList extends React.Component {
  render() {
    const { memos, removeMemo, updateMemoContent } = this.props;
    return (
      <div className="memo-list">
        {memos.map(memo => (
          <MemoItem
            key={memo._id}
            memo={memo}
            removeMemo={removeMemo}
            updateMemoContent={updateMemoContent}
          />
        ))}
      </div>
    );
  }
}

MemoList.propTypes = {
  memos: React.PropTypes.array.isRequired,
  removeMemo: React.PropTypes.func.isRequired,
  updateMemoContent: React.PropTypes.func.isRequired,
};

imports/ui/components/MemoItem.js

import React from 'react';

export default class MemoItem extends React.Component {
  constructor(props) {
    super(props);
    const { memo } = this.props;
    this.state = {
      textAreaValue: memo.content
    };
    this.onClick = this.onClick.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  onClick(event) {
    event.preventDefault();
    const { memo, removeMemo } = this.props;
    removeMemo(memo._id);
  }

  onChange(event) {
    const { memo, updateMemoContent } = this.props;
    const content = event.target.value;
    this.setState({
      textAreaValue: content
    });
    updateMemoContent(memo._id, content);
  }

  componentWillReceiveProps(nextProps) {
    if (this.state.textAreaValue !== nextProps.memo.content) {
      this.setState({
        textAreaValue: nextProps.memo.content
      });
    }
  }

  render() {
    const { memo } = this.props;
    const { textAreaValue } = this.state;
    return (
      <div className="memo-item">
        <a href="#" onClick={this.onClick} className="remove-button">
          &times;
        </a>
        <textarea
          className="textarea"
          value={textAreaValue}
          onChange={this.onChange}
        />
      </div>
    );
  }
}

MemoItem.propTypes = {
  memo: React.PropTypes.object.isRequired,
  removeMemo: React.PropTypes.func.isRequired,
  updateMemoContent: React.PropTypes.func.isRequired,
};

ポイント

  • ReactのStateを使ってテキストエリアの値を表示している
  • テキストエリアへの文字入力を検知したらsetState()で更新
    • Reactの機能により自動的に画面が書き換わる
  • MemoItemのcomponentWillReceiveProps()で他のブラウザからの変更も検知して、テキストエリア内の文字を更新している

コードを書き換えたら、Webブラウザで動作確認します。

メモを編集してWebブラウザをリロードしてみてください。

今度はメモの変更内容が保持されるようになりました。

また、他のWebブラウザでにリアルタイムに同期されるようになりました。

次のステップに進みましょう。

次へ