はじめに
- 初心者による初心者のための記事
- React.js公式チュートリアルの和訳風
- ES6
さらっと仮想DOM調べてみたよ、ぐらいの人向けです。
JavaScript初心者、ES6初心者、React初心者向けかつ僕がそうです。
なので間違いがあるかもしれません。見つけたら教えて下さい。
公式のチュートリアルはこちらです。
環境
- Mac OSX
- Node.js v0.12.7
- Gulp 3.9.0
- React 0.13.3
作るもの
公式のTutorialに沿って、シンプルなコメントボックスをReactで作成します。
次の機能を提供するものです。
- すべてのコメントの表示機能
- コメント投稿フォーム
- バックエンドサーバーとの連携
また、次の特徴を持っています。
- 更新最適化: 新しいコメントはサーバーに保存される前にリストに表示して体感速度を速くします
- ライブアップデート: 他の人が投稿したコメントもリアルタイムで更新されます
- Markdownサポート: マークダウンが使えます
準備
nodeインストール
npmを使うため、なにはともあれnodeをインストールします。npmはnode用のパッケージ管理システムです。yumとかgemとかbrewとかのnode版ですね。
nodeのインストールはndenvを使います。ndenvのインストールにはanyenvを使います。anyenvをインストールしてない方はインストールしてください。
参考: Mac OS Xに**env管理ツールのanyenvをインストール
まずはndenvをインストールします。
anyenv install ndenv
exec $SHELL -l
次はndenvを使ってnodeをインストールします。
ndenv install v0.12.7
ndenv global v0.12.7
ndenv install --list
でインストール可能なバージョン一覧を確認できます。
gulpインストール
gulpはビルドツールです。今イケてるらしいです。GuardとかGruntとかの仲間ですね。
npmでサクッとインストールします。-g
はグローバルにインストールするというオプションです。
npm install -g gulp
ndenv rehash
ディレクトリ作成
チュートリアルで使用するディレクトリを作成して入っときます。
mkdir react_es6
cd react_es6
package.json作成
チュートリアルで使用するJavascriptのパッケージを管理するためpackage.json
を作成します。
npm init
コマンドでpackage.json
できます。
色々聞かれるので適当に答えていきます。
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
name: (react_es6)
version: (1.0.0)
description: React Tutorial
entry point: (index.js)
test command:
git repository:
keywords:
author: nownabe
license: (ISC) MIT
About to write to /Users/nownabe/study/tutorials/react_es6/package.json:
{
"name": "react_es6",
"version": "1.0.0",
"description": "React Tutorial",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "nownabe",
"license": "MIT"
}
Is this ok? (yes)
パッケージ追加
package.json
のひな形ができたので、開発に必要なパッケージを追加していきます。
パッケージはnpm install パッケージ名 --save-dev
コマンドで追加します。
npm install --save-dev babelify@6.4.0
npm install --save-dev body-parser
npm install --save-dev browser-sync
npm install --save-dev browserify
npm install --save-dev express
npm install --save-dev jquery
npm install --save-dev marked
npm install --save-dev node-dev@2.6.2
npm install --save-dev react
npm install --save-dev vinyl-buffer
npm install --save-dev vinyl-source-stream
以上がチュートリアルで使うパッケージです。
最終的にpackage.json
はこのようになります。
バージョンは異なるかもしれません。
{
"name": "react_es6",
"version": "1.0.0",
"description": "React Tutorial",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "nownabe",
"license": "MIT",
"devDependencies": {
"babelify": "^6.1.3",
"body-parser": "^1.13.3",
"browser-sync": "^2.8.2",
"browserify": "^11.0.1",
"express": "^4.13.3",
"jquery": "^2.1.4",
"marked": "^0.3.5",
"node-dev": "^2.6.2",
"react": "^0.13.3",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
}
}
サーバー準備
このチュートリアルでは最終的にサーバーに対してJSONを保存・取得します。そのためJSONを処理するだけのサーバーを用意します。
まずは空のJSONを用意しておきます。
touch comments.json
次にサーバーアプリケーションserver.js
を作成します。
var fs = require('fs');
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.set('port', (process.env.PORT || 3000));
app.use('/', express.static(__dirname));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.get('/comments.json', function(req, res) {
fs.readFile('comments.json', function(err, data) {
res.setHeader('Cache-Control', 'no-cache');
res.json(JSON.parse(data));
});
});
app.post('/comments.json', function(req, res) {
fs.readFile('comments.json', function(err, data) {
var comments = JSON.parse(data);
comments.push(req.body);
fs.writeFile('comments.json', JSON.stringify(comments, null, 4), function(err) {
res.setHeader('Cache-Control', 'no-cache');
res.json(comments);
});
});
});
app.listen(app.get('port'), function() {
console.log('Server started: http://localhost:' + app.get('port') + '/');
});
gulpfile.js
Gulpを使ってソースコードに更新があったら自動でビルドしてブラウザに反映させるようにします。
Gulpの設定はgulpfile.js
に書きます。
var babelify = require('babelify');
var browserify = require('browserify');
var browserSync = require('browser-sync');
var buffer = require('vinyl-buffer');
var gulp = require('gulp');
var node = require('node-dev');
var source = require('vinyl-source-stream');
function errorHandler(err) {
console.log('Error: ' + err.message);
}
// 自動ブラウザリロード
gulp.task('browser-sync', function() {
browserSync({
proxy: {
target: 'http://localhost:3000'
},
port: 8080
});
});
// Javascriptへのビルド
// ES6かつJSXなファイル群をbuild/bundle.jsへ変換する
gulp.task('build', function() {
browserify({entries: ['./index.js']})
.transform(babelify)
.bundle()
.on('error', errorHandler)
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(gulp.dest('./build'))
.pipe(browserSync.reload({stream: true}));
});
// ローカルサーバーの起動
gulp.task('server', function() {
node(['./server.js']);
});
// ファイル監視
// ファイルに更新があったらビルドしてブラウザをリロードする
gulp.task('watch', function() {
gulp.watch('./index.js', ['build']);
gulp.watch('./index.html', ['build']);
gulp.watch('./components/*.js', ['build']);
});
// gulpコマンドで起動したときのデフォルトタスク
gulp.task('default', ['server', 'build', 'watch', 'browser-sync']);
Gulpの詳しいことは次の記事などを参照してください。
- Gulp.js入門 – コーディングを10倍速くする環境を作る方法まとめ
- サックっとbrowser-syncを設定
- React.js + Babel + Browserify + gulp の環境を作ってみた
以上で環境の準備は完了です。
Getting Started
index.htmlを作る
まずはブラウザに表示されるindex.html
を作成しましょう。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React Tutorial ES6</title>
</head>
<body>
<header>
<h1>React Tutorial ES6</h1>
</header>
<div id="container"></div>
<script src="build/bundle.js"></script>
</body>
</html>
これは内容がなにもない空HTMLですが、div#container
にReactで生成された仮想DOMが適用されていくことになります。
最初のコンポーネントを作る
Reactではすべてが階層的なコンポーネントから構成されます。これから作るコメントボックスは次のようなコンポーネントの構造を持っています。
- CommentBox
- CommentList
- Comment
- CommentForm
まずはCommentBoxコンポーネントを作ってみましょう。
次のようなindex.js
を作成してください。
import React from 'react'
class CommentBox extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div className='commentBox'>
Hello, world! I am a CommentBox.
</div>
);
}
}
React.render(
<CommentBox />,
document.getElementById('container')
);
index.js
ができたら次のコマンドでgulpを起動してください。
gulp
ブラウザが起動されて"Hello, world! I am a CommentBox."という文が表示されたと思います。以降はファイルを編集して保存すれば自動でJSX/ES6がJavascriptに変換され、ブラウザもリロードされます。新しくファイルを作った時はgulpの再起動が必要です。Ctrl + C
で終了して同じコマンドで起動してください。
index.js
の中身ですが、1行目でReact本体をインポートしています。ReactのコンポーネントはReact.Component
を継承したクラスとして定義します。今回はCommentBox
というコンポーネントを作成しています。
React.render()
メソッドがReactを開始するメソッドです。また、ここで指定するコンポーネントがReactのルート(根)コンポーネントになります。ここではCommentBox
コンポーネントをルートにしています。
CommentBox
クラスにもrender()
メソッドがあり、Reactのコンポーネントを返します。ここではReactのdiv
コンポーネントを返しています。render()
メソッドが返すのはあくまでも実際のDOMノードではなくReactのコンポーネントです。
Reactではこのようにルートコンポーネントからツリー状にコンポーネントをレンダリングしていきます。
constructor(props)
はいわゆるコンストラクタです。ES6のクラスベースでReactコンポーネントを作るときはこのメソッドでコンポーネントを初期化します。明示的にコンストラクタを定義しない場合はスーパークラスのコンストラクタが呼ばれるため、中身がsuper(props)
だけのときは省略することもできます。CommentBox
のコンストラクタには後で初期化処理を追加します。
JSX
このindex.js
はJSXという形式で書かれています。JSXはJavascriptの中にHTMLタグのような形でReactコンポーネントを直接書ける形式です。JSXは最終的にはbabelifyでJavascriptに変換されます。
JSXを使わずに直接Javascriptで書くこともできます。例えば次の2つは同じです。
// JSX
React.render(
<CommentBox />,
document.getElementById('container')
);
// Javascript
React.render(
React.createElement(CommentBox, null),
document.getElementById('container')
);
コンポーネントを組み合わせる
続いてCommentList
コンポーネントとCommentForm
コンポーネントを作成し、コンポーネントを組み合わせます。
index.js
を次のように編集してください。
import React from 'react'
class CommentBox extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div className='commentBox'>
<h2>Comments</h2>
<CommentList />
<CommentForm />
</div>
);
}
}
class CommentList extends React.Component {
render() {
return(
<div className='commentList'>
Hello, world! I am a CommentList.
</div>
);
}
}
class CommentForm extends React.Component {
render() {
return(
<div className='commentForm'>
Hello, world! I am a CommentForm.
</div>
);
}
}
React.render(
<CommentBox />,
document.getElementById('container')
);
少し長くなりましたがやっていることは単純です。CommentList
とCommentForm
という新しいコンポーネントを作成し、CommentBox
コンポーネントの子コンポーネントとしてレンダリングしているだけです。
Reactではこのように階層的にコンポーネントを組み合わせていきます。
ファイルを分割する
コンポーネントが増えてindex.js
の見通しが悪くなってきたのでコンポーネントごとにファイルをわけます。
まずはコンポーネントファイルをまとめるcomponents
ディレクトリを作成します。
mkdir components
次にこれまで作成したコンポーネントをそれぞれひとつのファイルにします。
import React from 'react'
export default class CommentList extends React.Component {
render() {
return(
<div className='commentList'>
Hello, world! I am a CommentList.
</div>
);
}
}
import React from 'react'
export default class CommentForm extends React.Component {
render() {
return(
<div className='commentForm'>
Hello, world! I am a CommentForm.
</div>
);
}
}
import React from 'react'
import CommentList from './CommentList'
import CommentForm from './CommentForm'
export default class CommentBox extends React.Component {
constructor(props) {
super(props)
}
render() {
return(
<div className='commentBox'>
<h2>Comments</h2>
<CommentList />
<CommentForm />
</div>
);
}
}
外のファイルから使えるようにexport default
をつけるのを忘れないで下さい。最後にindex.js
は次のようになります。
import React from 'react'
import CommentBox from './components/CommentBox'
React.render(
<CommentBox />,
document.getElementById('container')
);
だいぶすっきりしました。
プロパティを使う
親コンポーネントは子コンポーネントに値を渡すことができます。Reactではプロパティと呼ばれています。
プロパティを使ったComment
コンポーネントを新しく作成します。
import React from 'react'
export default class Comment extends React.Component {
render() {
return(
<div className='comment'>
<h3 className='commentAuthor'>
{this.props.author}
</h3>
{this.props.children}
</div>
);
}
}
親から渡されたプロパティはthis.props
で参照できます。
また、JSXに変数の値を埋め込むときは{変数名}
を使います。
次にプロパティを渡す側の親コンポーネントを編集します。Comment
の親はCommentList
です。
import React from 'react'
import Comment from './Comment'
export default class CommentList extends React.Component {
render() {
return(
<div className='commentList'>
<Comment author='Pete Hunt'>This is one comment</Comment>
<Comment author='Jordan Walke'>This is *another* comment</Comment>
</div>
);
}
}
このようにHTMLの属性と同じ形式で書くと子コンポーネントにプロパティを渡すことができます。子コンポーネントからはthis.props.author
という形で参照できます。また、JSXの子ノード(この場合This is one comment
など)はthis.props.children
という予約属性で参照することができます。
Markdown機能を追加する
Markdown記法でコメントできるようにするためコメント本文のMarkdownを変換するようにします。
components/Comment.js
を次のように編集してください。
import Marked from 'marked'
import React from 'react'
export default class Comment extends React.Component {
render() {
var rawMarkup = Marked(this.props.children.toString(), {sanitize: true});
return(
<div className='comment'>
<h3 className='commentAuthor'>
{this.props.author}
</h3>
<span dangerouslySetInnerHTML={{__html: rawMarkup}} />
</div>
);
}
}
Markdownの変換にはmarkedライブラリを使っています。{}
を使うとHTMLタグがエスケープされるためdangerouslySetInnerHTML
という属性を使います。
データモデルと連携する
これまではコメントデータを直接ソースコードに埋め込んでいました。最終的な目標はコメントデータをサーバーから取得することです。それを想定したデータモデルからコメントをレンダリングするようにしていきます。
まずはデータモデルを定義してルートコンポーネントにプロパティとして渡します。
import React from 'react'
import CommentBox from './components/CommentBox'
var data = [
{author: 'Pete Hunt', text: 'This is one comment'},
{author: 'Jordan Walke', text: 'This is *another* comment'}
];
React.render(
<CommentBox data={data} />,
document.getElementById('container')
);
CommentBox
ではそれを受けてCommentList
にそのまま渡します。
import React from 'react'
import CommentList from './CommentList'
import CommentForm from './CommentForm'
export default class CommentBox extends React.Component {
constructor(props) {
super(props)
}
render() {
return(
<div className='commentBox'>
<h2>Comments</h2>
<CommentList data={this.props.data} />
<CommentForm />
</div>
);
}
}
CommentList
では受け取ったプロパティからComment
コンポーネントを作成します。
import React from 'react'
import Comment from './Comment'
export default class CommentList extends React.Component {
render() {
var commentNodes = this.props.data.map((comment)=> {
return(<Comment author={comment.author}>{comment.text}</Comment>);
});
return(<div className='commentList'>{commentNodes}</div>);
}
}
これでサーバーからデータを受け取る準備ができました。
サーバーからデータを取得する
ソースコードに直接埋め込んでいたコメントデータをサーバーから取得するようにします。
まずindex.js
のハードコーディング部分を取り除き、CommentBox
コンポーネントにデータ取得用のURLを渡すようにします。
URLはserver.js
で設定してあります。すべて書くとhttp://localhost:8080/comments.js
になります。
import React from 'react'
import CommentBox from './components/CommentBox'
React.render(
<CommentBox url="comments.json" />,
document.getElementById('container')
);
サーバーからデータを取得するとReactのコンポーネントたちは動的に変化することになります。コンポーネントの動的な状態を表現するためReactにはstate
というものがあります。
プロパティは親から渡されるImmutableな値であり親の所有物です。state
は自身の所有物でありMutableに扱うことができます。
サーバーから取得したデータをstate
で扱い、動的にコンポーネントが変化できるようにしていきます。
サーバーがとりあえずJSONを返せるようにするため、まずは静的なJSONファイルを作成してください。
[
{"author": "Pete Hunt", "text": "This is one comment"},
{"author": "Jordan Walke", "text": "This is *another* comment"}
]
http://localhost:8080/comments.json にアクセスしてJSONが返っていればOKです。
次はサーバーからデータを取得するようにします。データ取得はCommentBox
が行います。
import React from 'react'
import CommentList from './CommentList'
import CommentForm from './CommentForm'
import $ from 'jquery'
export default class CommentBox extends React.Component {
constructor(props) {
super(props)
this.state = {
data: []
};
}
componentDidMount() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: (data) => { this.setState({data: data}); },
error: (xhr, status, err) => {
console.error(this.props.url, status, err.toString());
}
});
}
render() {
return(
<div className='commentBox'>
<h2>Comments</h2>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
}
変更点は3つあります。
1つ目はコンストラクタです。state
の初期化を行っています。このようにstate
はthis.state
で参照することができます。
2つ目はcomponentDidMount()
メソッドが新しく定義されていることです。このメソッドはコンポーネントがレンダリングされた時にReactが自動で呼び出すメソッドです。Reactではthis.setState()
メソッドが呼ばれると新しくstate
を設定すると同時に再レンダリングを行います。このcomponentDidMount()
メソッドはサーバーからデータを取得してそのデータを元にコンポーネントの再レンダリングを行っています。
3つ目はCommentList
コンポーネントをレンダリングするときにthis.props.data
ではなくthis.state.data
を渡していることです。
これでサーバーからデータを取得してレンダリングを行えるようになりました。
自動で更新させる
サーバーからデータを取得するようになりましたが、取得するのはindex.html
にアクセスした時の1回のみです。他の人がコメントを投稿した時など自動で画面を更新したいので、定期的にサーバーからデータを取得するようにしましょう。
まずはindex.js
からCommentBox
に何秒ごとにデータを取得するかを伝えます。
import React from 'react'
import CommentBox from './components/CommentBox'
React.render(
<CommentBox url="comments.json" pollInterval={2000} />,
document.getElementById('container')
);
次にCommentBox
でsetInterval
を使って定期的にサーバーからデータを取得するようにします。データ取得部分は新しくloadCommentsFromServer
というメソッドにしました。
import React from 'react'
import CommentList from './CommentList'
import CommentForm from './CommentForm'
import $ from 'jquery'
export default class CommentBox extends React.Component {
constructor(props) {
super(props)
this.state = {
data: []
};
}
loadCommentsFromServer() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: (data) => { this.setState({data: data}); },
error: (xhr, status, err) => {
console.error(this.props.url, status, err.toString());
}
});
}
componentDidMount() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer.bind(this), this.props.pollInterval);
}
render() {
return(
<div className='commentBox'>
<h2>Comments</h2>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
}
これで定期的なポーリングでsetState
を実行してデータの更新があると再レンダリングするようになります。comments.json
を書き換えてみると2秒以内に自動で画面が更新されるはずです。
コメント投稿機能を追加する
フォーム作成
ブラウザからコメントを投稿できるようにしていきます。まずはCommentForm
コンポーネントにフォームを作成しましょう。
import React from 'react'
export default class CommentForm extends React.Component {
render() {
return(
<form className="commentForm">
<input type="text" placeholder="Your name" />
<input type="text" placeholder="Say something..." />
<input type="submit" value="Post" />
</form>
);
}
}
これからこのフォームに「Postボタンがクリックされるとサーバーにデータを送信する」という機能をつけていきます。
onSubmitハンドラ
まずはクリックされるとフォームのデータを読み取ってフォームをクリアするという機能をつけてみます。
import React from 'react'
export default class CommentForm extends React.Component {
handleSubmit(e) {
e.preventDefault();
var author = React.findDOMNode(this.refs.author).value.trim();
var text = React.findDOMNode(this.refs.text).value.trim();
if (!text || !author) return;
// TODO: サーバにデータを送信
React.findDOMNode(this.refs.author).value = '';
React.findDOMNode(this.refs.text).value = '';
}
render() {
return(
<form className="commentForm" onSubmit={this.handleSubmit.bind(this)}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
}
form
コンポーネントにonSubmit
ハンドラとしてhandleSubmit
メソッドを登録しています。これでPostボタンをクリックした時handleSubmit
メソッドが実行されます。
handleSubmit
では次の流れで処理を行います。
- (ブラウザのSubmit処理を無効化する)
- フォームからデータを取得する
- サーバーにデータを送信する
- フォームをクリアする
1つ目の「ブラウザのSubmit処理を無効化する」はe.preventDefault()
の部分です。Submitイベントへのブラウザのデフォルト処理をキャンセルしています。
2つ目の「フォームからデータを取得する」は
var author = React.findDOMNode(this.refs.author).value.trim();
の部分です。Reactコンポーネントに対してref
属性で名前をつけておけばthis.refs
からコンポーネントを参照できるようになります。そしてReact.findDOMNode
を使えば実際のブラウザのDOM要素を取得できます。
3つ目はこれから実装します。
4つ目の「フォームをクリアする」は
React.findDOMNode(this.refs.author).value = '';
の部分です。ここでもthis.refs
を使っています。
バインディング
form
コンポーネントにthis.handleSubmit
を渡すとき.bind(this)
しています。これはその関数内のthis
を固定するものです。これによりhandleSubmit
内のthis
をCommentForm
コンポーネントに固定しているため、関数内でthis.refs
が使えています。
従来のReactでは不要でしたが、ES6のReact.Componentクラスの継承では暗黙的にthis
が継承されないため必要になります。1
データ送信
ではデータ送信部分を実装していきます。が、その前に新しいコメントの画面反映について考えます。
新しいコメントが投稿された時にはコメントデータをリフレッシュして画面に反映する必要があります。コメントデータをStateで保持しているのはCommentBox
コンポーネントなのでなんらかの方法でCommentBox
にデータを送る必要があります。
- CommentBox
- CommentList
- Comment
- CommentForm
これまでは親から子へとpropsでデータを渡してきました。今回は逆方向にデータを渡すことになります。Reactでは親から子へコールバック関数を渡すことでこれを実現します。
まずはCommentBox
でコールバック関数handleCommentSubmit
を定義してprops
でCommentForm
に渡します。
import React from 'react'
import CommentList from './CommentList'
import CommentForm from './CommentForm'
import $ from 'jquery'
export default class CommentBox extends React.Component {
constructor(props) {
super(props)
this.state = {
data: []
};
}
loadCommentsFromServer() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: (data) => { this.setState({data: data}); },
error: (xhr, status, err) => {
console.error(this.props.url, status, err.toString());
}
});
}
handleCommentSubmit(comment) {
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: (data) => { this.setState({data: data}); },
error: (xhr, status, err) => {
console.error(this.props.url, status, err.toString());
}
});
}
componentDidMount() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer.bind(this), this.props.pollInterval);
}
render() {
return(
<div className='commentBox'>
<h2>Comments</h2>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)} />
</div>
);
}
}
CommentForm
では受け取ったコールバック関数を実行します。
import React from 'react'
export default class CommentForm extends React.Component {
handleSubmit(e) {
e.preventDefault();
var author = React.findDOMNode(this.refs.author).value.trim();
var text = React.findDOMNode(this.refs.text).value.trim();
if (!text || !author) return;
this.props.onCommentSubmit({author: author, text: text});
React.findDOMNode(this.refs.author).value = '';
React.findDOMNode(this.refs.text).value = '';
}
render() {
return(
<form className="commentForm" onSubmit={this.handleSubmit.bind(this)}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
}
これでPostボタンをクリックするとサーバーにデータを送信して画面を更新するようになりました。ブラウザで試してみてください。
投稿時の画面表示を最適化する
アプリケーションに必要な機能はひと通り揃いました。しかし現在の実装ではPostをクリックしてサーバーのレスポンスがあるまで画面が更新されません。そのためサーバーが遅い時はアプリケーション自体も遅く感じます。
アプリの体感速度を向上させるためPostをクリックした瞬間画面が更新されるように変更します。handleCommentSubmit
に3行追加します。
...
handleCommentSubmit(comment) {
var comments = this.state.data;
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: (data) => { this.setState({data: data}); },
error: (xhr, status, err) => {
console.error(this.props.url, status, err.toString());
}
});
}
...
おわりに
おわりです。公式のチュートリアルをちょっとES6っぽくしてみました。
Learn more:
とのことです。