注意
- 本投稿は一種のネタであり、Afterのコードは動作しません。
- 本投稿の著者はReactを使ったことはありません。
概要
本投稿では、JSX記法でも読みにくい(と私は感じている)Reactのサンプルコードを、
直感的に理解できるよう、分かりにくい箇所を場合によってはJavaScriptの文法・動作を無視したコードに書き直してみます。
書きなおしたコードは私の主観で書かれているので、人によっては読みにくいかもしれません。
サンプル
Reactのサンプルコードは、公式サイトより引用します。
A JavaScript library for building user interfaces | React
A Stateful Component
Before
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
React.render(<Timer />, mountNode);
↓
After
/**
* Timer要素を作成する
*/
Timer = React.createClass(
function getInitialState() {
/**
* this.state に初期値を設定する
*/
this.state.secondsElapsed = 0;
}
function this.tick() {
/**
* this.state.secondsElapsed の値を1増やす
*/
this.state.secondsElapsed = this.state.secondsElapsed + 1;
}
function componentDidMount() {
/**
* Timer要素がDOMに追加されたら、
* タイマーを起動し、
* 1秒毎に this.tick 関数を呼ぶ
*/
this.interval = setInterval(this.tick, 1000);
}
function componentWillUnmount() {
/**
* Timer要素がDOMから削除されたら、
* タイマーを停止する
*/
clearInterval(this.interval);
}
return <div>Seconds Elapsed: {this.state.secondsElapsed}</div>;
);
React.render(<Timer />, mountNode);
コメントを追加し、
this.setState
関数を変数代入に変えました。
あとrender
関数は省略しています。
これだけで随分と見やすくなったと思いますがいかがでしょう?
少なくとも私は読みやすいです。
以下もこの調子でいきます。
An Application
Before
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.render(<TodoApp />, mountNode);
↓
After
/**
* TodoList要素を作成する
*/
TodoList = React.createClass(
/**
* items属性の値を取得する
* items属性には配列が指定される
*/
items = this.props.items;
function createItem(itemText) {
return <li>{itemText}</li>;
}
/**
* items属性に指定した配列の各値を含むli要素を含む
* ul要素を作成する
*/
return <ul>{items.map(createItem)}</ul>;
);
/**
* TodoApp要素を作成する
*/
TodoApp = React.createClass({
function getInitialState() {
/**
* this.state に初期値を設定する
*/
this.state.items = [];
this.state.text = '';
}
function this.onChange(e) {
/**
* input要素の入力値が変更されたら、
* this.state.text に入力値を代入する
*/
this.state.text = e.target.value;
}
function this.handleSubmit(e) {
/**
* 項目を追加しようとしたら(フォームが送信されそうになったら)、
* 配列 this.state.items に文字列 this.state.text を追加する
*/
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.state.items = nextItems;
this.state.text = nextText;
}
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>Add #{this.state.items.length + 1}</button>
</form>
</div>
);
});
React.render(<TodoApp />, mountNode);
A Component Using External Plugins
Before
var converter = new Showdown.converter();
var MarkdownEditor = React.createClass({
getInitialState: function() {
return {value: 'Type some *markdown* here!'};
},
handleChange: function() {
this.setState({value: this.refs.textarea.getDOMNode().value});
},
render: function() {
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<textarea
onChange={this.handleChange}
ref="textarea"
defaultValue={this.state.value} />
<h3>Output</h3>
<div
className="content"
dangerouslySetInnerHTML={{
__html: converter.makeHtml(this.state.value)
}}
/>
</div>
);
}
});
React.render(<MarkdownEditor />, mountNode);
↓
After
converter = new Showdown.converter();
/**
* MarkdownEditor要素を作成する
*/
MarkdownEditor = React.createComponent({
function getInitialState() {
/**
* this.state に初期値を設定する
*/
this.state.value = 'Type some *markdown* here!';
}
function this.handleChange() {
/**
* textarea要素の入力値が変更されたら、
* this.state.value に入力値を代入する
*/
textarea = this.refs.textarea.getDOMNode();
this.state.value = textarea.value;
}
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<textarea onChange={this.handleChange} ref="textarea" defaultValue={this.state.value}>
<h3>Output</h3>
<div className="content">{html: converter.makeHtml(this.state.value)}</div>
</div>
);
});
React.render(<MarkdownEditor />, mountNode);
dangerouslySetInnerHTML
って分かりにくいので、省略してみます。
動作は前提としないので、多少矛盾があっても問題ないよね?