10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React(ES2015)とMilkcocoaでリアルタイムチャットを作ってみる #reactnative_meetup #mlkcca

Last updated at Posted at 2016-09-18

昨日の#jxugではAndroid(けっきょくC#じゃなくてJavaで)とMilkcocoaをつなげてみてました。

AndroidからMilkcocoaを使って見るメモ #mlkcca

React入門者向けハンズオンで作ってみました。ハンズオンで使ったCreate React Appも参考にしてます。

es2015な感じで書いたので誰かレビューしてほしい...

ちなみに1年前にこんな記事も書いてたらしいですがすっかり忘れてました。あのときはFluxxorが流行ってた記憶があります。

React+Fluxxor+MilkcocoaでfluxなTODOアプリ #geekhouseday

milkcocoa

IoTのバックエンドサービスとして使われているリアルタイム通信サポートのバックエンドサービス https://mlkcca.com/

今回はチャットの裏側に使います。

公式チュートリアルをいじってReactでチャットを作ってみた

まずは完成品

URL: https://private-chipmunk-54046.netlify.com/

(いつまで稼働させてるかはわからないです)

リアルタイムなチャットができます。

参考にしたチュートリアル

このチュートリアルをやるとマークダウン対応のコメント欄みたいなものをつくれます。

メインのソースコードはGitHub上のexample.jsです。

https://github.com/reactjs/react-tutorial/blob/master/public/scripts/example.js

書いたソースコード

先ほどのexample.jsをもとに書き換えてます。

app.js
const milkcocoa = new MilkCocoa('your-app-id.mlkcca.com');
const ds = milkcocoa.dataStore('react-chat');

//CommentBox -> CommentList -> Comment
class Comment extends React.Component {
    constructor(props) {
        super(props);
    };

    rawMarkup(){
        let md = new Remarkable();
        let rawMarkup = md.render(this.props.children.toString());
        return { __html: rawMarkup };
    };

    render() {
        let md = new Remarkable();
        return (
            <div className="comment">
                <h2 className="commentAuthor">{this.props.author}</h2>
                <span dangerouslySetInnerHTML={this.rawMarkup()} />
            </div>
        );
    };
};

//CommentBox->CommentList
class CommentList extends React.Component {
    render() {
        let commentNodes = this.props.data.map( (item) => {
            let comment = item.value;
            return (
                <Comment author={comment.author} key={item.id}>
                    {comment.text}
                </Comment>
            );
        });

        return (
            <div className="commentList">{commentNodes}</div>
        );
    };
};

//CommentBox->CommentForm
class CommentForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {author: '', text: ''};
    };
    
    handleAuthorChange (e) {
        this.setState({author: e.target.value});
    };
    
    handleTextChange (e) {
        this.setState({text: e.target.value});
    };
    
    handleSubmit (e) {
        e.preventDefault();
        let author = this.state.author.trim();
        let text = this.state.text.trim();
        if (!text || !author) return;
        this.props.onCommentSubmit({author: author, text: text});
        // this.setState({author: '', text: ''});
        this.setState({text: ''}); //名前は残す
    };

    render() {
        return (
            <form className="commentForm" onSubmit={this.handleSubmit.bind(this)}>
                <input
                    type="text"
                    placeholder="名前を入力してね。"
                    value={this.state.author}
                    onChange={this.handleAuthorChange.bind(this)}
                />
                <input
                    type="text"
                    placeholder="マークダウン使えるよ"
                    value={this.state.text}
                    onChange={this.handleTextChange.bind(this)}
                />
                <input type="submit" value="Post" />
            </form>
        );
    };
};

//CommentBox
class CommentBox extends React.Component {
    constructor(props) {
        super(props);
        this.state = {data: []};
    };
    
    componentDidMount () {
        this.loadCommentsFromServer();
        ds.on('push', this.loadCommentsFromServer.bind(this));
    };

    loadCommentsFromServer () {
        let history = ds.history();
        history.on('data', (data) => {
            this.setState({data: data});
        });
        history.run();
    };

    handleCommentSubmit (comment) {
        ds.push(comment);
    };
    
    render() {
        return (
            <div className="commentBox">
                <CommentForm onCommentSubmit={this.handleCommentSubmit} />
                <CommentList data={this.state.data} />
            </div>
        );
    };
};

class App extends React.Component {
    render(){
        return(
            <div className="App">
                <div className="App-header">
                    <img src="../logo.svg" className="App-logo" alt="logo" />
                    <h1>Milkcocoa+Reactでリアルタイムチャット</h1>
                </div>
                <CommentBox />
            </div>
        );
    }
}

ReactDOM.render(
    <App />,
    document.getElementById('content')
);

コメントないけど自分用メモなつもりなのであしからず。

ES5 -> ES2015書き換え

参考にしたチュートリアルはES5ベースの書き方になっているのでES2015な書き方に変更します。

これらを参考にしてReact.createClass()React.Componentに書き換えました。

主にこの辺りで若干手こずりました。

  • getInitialState()が使えないのでconstructorで初期化
getInitialState: function() {
    return {author: '', text: ''};
  },

↓ stateの初期化の仕方が変わる

    constructor(props) {
        super(props);
        this.state = {author: '', text: ''};
    };
    
  • thisのオートバインディングがなくなるので自分でバインドする
<form className="commentForm" onSubmit={this.handleSubmit}>

↓ ここもバインドしてあげる

<form className="commentForm" onSubmit={this.handleSubmit.bind(this)}>

みたいな書き方に変更します。

Milkcocoa的な部分

CommentBoxのコンポーネント内のメソッドで処理してます。

  • handleCommentSubmit内でデータ保存 / pushメソッド

MilkcocoaのpushメソッドをhandleCommentSubmit()で行ってます。

    handleCommentSubmit (comment) {
        ds.push(comment);
    };

pushメソッドが実行されると接続してるクライアントにpushイベントが発火されます。

  • loadCommentsFromsSever内でデータ取得 / historyメソッド
    loadCommentsFromServer () {
        let history = ds.history();
        history.on('data', (data) => {
            this.setState({data: data});
        });
        history.run();
    };
  • historyメソッドの初期呼び出しとpushイベントの監視 / onメソッド

今日のハンズオン資料にあるようにcomponentDidMountはコンポーネントがレンダリングされた後に一度だけ呼ばれます。

なのでそのタイミングでhistoryを発火させる(初期ロード)のと、onメソッドでpushイベントを監視を開始します。

これで他の人がコメントするたびにhistoryが実行されてデータが更新されてリアルタイムチャットになります。

componentDidMount () {
        this.loadCommentsFromServer();
        ds.on('push', this.loadCommentsFromServer.bind(this));
    };

雑感

ハンズオン中につくったので雑かもしれないですが取りあえずまとめてみました。

久しぶりにReact触ったけど、バージョンアップによって書き方が変わっててどれを参考にするのがいいのかってちゃんと動きを追ってないと大変そうだなぁという印象でした苦笑

でもとりあえずリハビリな感じになって楽しかった!

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?