Reactは、Facebookの超すごいプログラマーたちが生み出した超すごいフレームワークです。仮想DOMを実装し、DOM方式で必要なコンポーネントを瞬時に追加し、不要なものを瞬時に削除します。ReactはMVC構造におけるVの役割を担いますが、Fluxと組み合わせて使うと、MとVを簡単に同期できる超すごいフレームワークになります。Fluxの話はまた今度〜

コンポーネントたち

Reactでは、HTML要素ではどこを探しても見つからないような特別な機能を持つコンポーネントを作成できます。例えば、このチュートリアルにあるドロップダウンナビゲーションなどです。各コンポーネントには独自のスコープがあるため、コンポーネントを定義すれば、何度も何度も繰り返し使うことができ、他のコンポーネントとやり取りする必要がまったくありません!

各コンポーネントにはrenderという機能があり、ブラウザに描画するHTMLを効率的に返します。また、Reactの他のコンポーネントを呼び出すこともできます。つまり、Reactに一度入ると深い海のようなもの、いや違う、思いつかないことはあっても、できないことはない、ということです。

JSX

Reactをよく見ていると、JSXというものに気づくかもしれません。JSXは、HTMLでJavaScriptを含めるのではなく、JavaScriptの中にHTMLを書くことを可能にします。文字列や改行などを気にする必要がないため、素早い開発を支援します。ブラウザでJSXを実行することもできますが、ページの速度が遅くなるためお勧めできません。gulpとgruntは前処理タスク用のJSXインタープリターを提供しているので、JSXを使いたい場合はこの機能を有効にすることをお勧めします。

JSXを使う

前述のように、JSXの各コンポーネントにはrenderメソッドがあります。このメソッドは「ViewModel(ビューモデル)」を生成します。コンポーネントにHTMLを返す前に、このモデル(ViewModel)の情報をビューに組み込むことができます。つまり、HTMLはこのモデルに基づいて動的に変化します(動的なリストなど)。

これをやり終えたら、レンダリングしたいものを返すことができます。JSXを使えばとても簡単です。

var ExampleComponent = React.createClass({
    render: function () {
        return (
            <div className="navigation">
                Hello World!
            </div>
            );
    }
});

このコードをブラウザで実行しようとすると、構文エラーのメッセージが表示されるだけです。JavaScriptでは<と>の文字を引用符で囲む必要があるからです。コードにJSXインタープリターを実行すると、次のように変換されます。

var ExampleComponent = React.createClass({
    render: function () {
        return (
            React.createElement('div', {className: 'navigation'}, 'Hello World!')
            );
    }
});

ここをクリックしてサンプルをテストできます- ブラウザのJSXインタープリターを使用しています(推奨されませんが、JSFiddleのためです)。

実行できました!JSXは、React.createElementで生成したものを解釈しましたすべてのDOMノード、ノードタイプ、引数、内容が生成されます。JSXを使わなくても構いませんが、それはReact.createElement以外のすべてのDOMノードを手動で書かなければならないことを意味します。数え切れないほどの例が、JSXを使うべきだと教えてくれました。

なぜDOM上でclassの代わりにclassNameを使うのか疑問に思っているでしょう。これはclassがJavaScriptの予約語だからです。JSXが解釈するときあなたのコードを解釈する際、ノード上のすべての属性をオブジェクトに変換しますが、オブジェクトを属性として使うことはできません!

属性に変数を使う

コンポーネントのスタイルクラス(または他の属性値)を動的に変更したい場合は、変数を使用できます。ただし、変数名をそのまま渡すだけではだめで、波括弧({})で囲む必要があります。そうすればJSXはそれが外部の変数であると認識できます。

var ExampleComponent = React.createClass({
render: function () {
    var navigationClass = 'navigation';
    return (
        <div className={ navigationClass }>
            Hello World!
        </div>
        );
}
});

ここでこの機能を見ることができます。

初期レンダラー

最初にReactコンポーネントをレンダリングするときは、Reactにどのコンポーネントをレンダリングするのかを伝え、既存のDOMノードを指定して、そのコンポーネントをどこにレンダリングするかを示す必要があります。そのために、React.render関数を使用します。

var ExampleComponent = React.createClass({
render: function () {
    return (
        <div className="navigation">
            Hello World!
        </div>
        );
}
});
React.render(<ExampleContent />, document.body);

それはbodyノード上にコンポーネントをレンダリングします——簡単でしょう! これからは、他のコンポーネントを通常どおり呼び出すことができます。また、必要に応じてrender関数を何度も使うこともできます。Reactを使ってページ全体をレンダリングしたくないが、複数のコンポーネントを使いたい場合も同様です。

コンポーネントの基本

コンポーネントは、自身の「状態」を持つことができます。これにより、同じコンポーネントを何度も繰り返し使用しても、それぞれのコンポーネントインスタンスの状態が一意であるため、まったく異なる見た目にすることができます。

コンポーネントに属性(プロパティ)を渡すとき、これらはプロパティ(props)と呼ばれます。HTML属性に限定されることなく、好きなものを渡すことができ、コンポーネント内でthis.propsを通じてアクセスできます。これにより、同じコンポーネントを再利用しつつ、異なるプロパティのセットを渡すことができます。たとえば、コンポーネントの「構成」を渡すことができます。

プロパティ

先ほどの「Hello World!」の例では、HTMLノード上にclassName属性がありました。コンポーネント内部では、this.props.classNameを使ってこの値にアクセスできます。しかし前述のとおり、好きなものを渡すことができます。ドロップダウンの場合、ナビゲーションの設定をオブジェクトとして構成し、コンポーネントがそれをレンダリングするための設定として使う必要があります。始めましょう—

var navigationConfig = [];

var Navigation = React.createClass({
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

もし今this.props.configにアクセスできるなら、空の配列(navigationConfigの値)を受け取るでしょう。実際のナビゲーションのコーディングに入る前に、状態について説明しましょう。

状態

先ほど説明したように、各コンポーネントには独自の「状態」があります。状態を使用するときは、初期状態を定義してから、this.setStateを使って状態を更新します。状態が更新されると、コンポーネントは再びrender関数を呼び出し、以前にレンダリングした値を新しい値で置き換えたり変更したりします。これが仮想DOMの真髄です。差分を計算するアルゴリズムはReact内部で実行されるため、DOMの更新に依存する必要はありません(DOMは遅いからです)。Reactは差分を計算し、一連の命令のセット(例えば、navigation__linkにactiveクラスを追加したり、ノードを削除したり)を生成し、それらをDOM上で実行します。

ナビゲーションでは、ドロップダウンメニューが開いているかどうかを状態に保持します。そのために、getInitialState関数をクラス構成オブジェクトに追加し、必要な初期状態を持つオブジェクトを返します。

var navigationConfig = [];

var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

私たちがこれを-1に設定していることに気づくでしょう。ドロップダウンメニューを開こうとするとき、状態内の構成配列にあるナビゲーション項目の位置を使用します。配列のインデックスは0から始まるため、まだどのナビゲーション項目も指していないことを表すのに-1を使います。

this.stateを使用して状態にアクセスできます。したがって、this.state.openDropdownを確認すると、-1が返されるはずです。

コンポーネントのライフサイクル

各コンポーネントには「ライフサイクル」があります。これは、コンポーネント設定内で定義できる一連の関数であり、コンポーネントのライフサイクル中に呼び出されます。getInitialStateは既に見てきました。これは、コンポーネントがマウントされるときに一度だけ呼び出されます。

componentWillMount

この関数は、コンポーネントがマウントされる直前に呼び出されます。つまり、ここでコンポーネントの機能に必要なコードを実行できます。renderはコンポーネントのライフサイクル中に何度も呼び出されるため、通常はXHRリクエストなど、一度だけ実行すればよいコードをここに置きます。

var ExampleComponent = React.createClass({
    componentWillMount: function () {
        // xhr request here to get data
    },
    render: function () {
        // this gets called many times in a components life
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

componentDidMount

コンポーネントがrender関数を実行し、実際にDOMにコンポーネントをレンダリングした後、componentDidMountが呼び出されます。ここでは、必要なDOM操作や、コンポーネントが実際にDOMに存在することに依存する処理(グラフのレンダリングなど)を行うことができます。this.getDOMNodeを呼び出すことで、DOMノードに内部的にアクセスできます。

var ExampleComponent = React.createClass({
    componentDidMount: function () {
        var node = this.getDOMNode();
        // render a chart on the DOM node
    },
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

componentWillUnmount

もし私たちが後始末をしない場合、自分自身の後始末をせずに、それらのイベントの一つがトリガーされると、ロードされていないコンポーネントを計算しようとして、Reactはエラーをスローします。

var ExampleComponent = React.createClass({
    componentWillUnmount: function () {
        // unbind any event listeners specific to this component
    },
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

コンポーネントメソッド

Reactは、私たちの作業をより便利にするためのメソッドも用意しています。これらはコンポーネントの作成中に呼び出されます。例えばgetInitialStateは、デフォルトの状態を定義できるようにしてくれるため、コード内で状態項目が存在するかどうかをさらにチェックする必要がありません。

getDefaultProps

コンポーネントを作成するとき、好きなようにコンポーネントのプロパティにデフォルト値を定義できます。つまり、コンポーネントを呼び出す際にこれらのプロパティに値を設定しない場合、コンポーネントはデフォルトの「構成」を持ちます。そして、次の行でプロパティをチェックするような心配をする必要はありません。

コンポーネントを定義すると、これらのデフォルトのプロパティはキャッシュされるため、各コンポーネントのインスタンスで同じであり、変更できません。ナビゲーションコンポーネントでは、構成を空の配列として指定しているため、構成が渡されなくてもrenderメソッド内でエラーは発生しません。

var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

propTypes

また、各プロパティに任意の型を指定することもできます。これは、プロパティに予期しない値が代入された場合のチェックや処理に非常に役立ちます。以下のドロップダウンのように、

var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        return (
            <div className="navigation">

            </div>
            );
    }
});

React.render(<Navigation config={ navigationConfig } />, document.body);

mixins

コンポーネントに mixins を追加することもできます。これは React から独立した基本的なコンポーネント(単なるオブジェクト型の設定)です。つまり、機能が似ている2つのコンポーネントがあれば、設定を共有できます(初期状態が同じ場合)。mixin にメソッドを抽象化して定義することで、同じコードを2回書く必要がなくなります。

var ExampleMixin = {
    componentDidMount: function () {
        // bind some event listeners here
    },
    componentWillUnmount: function () {
        // unbind those events here!
    }
};

var ExampleComponent = React.createClass({
    mixins: [ExampleMixin],
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

var AnotherComponent = React.createClass({
    mixins: [ExampleMixin],
    render: function () {
        return (
            <div>
                Hello World!
            </div>
            );
    }
});

これで全てのコンポーネントが同じ componentDidMount と componentWillUnmount メソッドを持つことになり、書き直すコードを省けます。ただし、これらのプロパティは override(上書き)できません。mixin 内で設定されたプロパティは、そのコンポーネント内では上書き不可です。

ループ処理

オブジェクトを含む配列があるとき、その配列をループして各オブジェクトをリスト項目にレンダリングするにはどうすればいいでしょうか? JSX を使うと、任意の JavaScript ファイル内で使用でき、配列をマッピングして JSX を返し、React を使ってレンダリングできます。

var navigationConfig = [
    {
        href: 'http://ryanclark.me',
        text: 'My Website'
    }
];
var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        var config = this.props.config;
        var items = config.map(function (item) {
            return (
                <li className="navigation__item">
                    <a className="navigation__link" href={ item.href }>
                        { item.text }
                    </a>
                </li>
                );
        });
        return (
            <div className="navigation">
                { items }
            </div>
            );
    }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

JSFiddle で navigationConfig を自由に使用してください。

ナビゲーション設定は配列とオブジェクトで構成され、ハイパーリンクを指す href プロパティと表示用の text プロパティを含みます。マッピングすると、配列を順にたどってオブジェクトを取得します。href と text にアクセスして HTML 内で使用できます。リストを返すとき、この配列内のリスト項目はすべて置き換えられるため、React に渡して処理すると、React はそれをどうレンダリングするかを認識します!

混在

これまでに、すべてのドロップダウンリストの展開を実現しました。どの項目がドロップダウンされたかを知る必要があります。.children プロパティを使用して navigationConfig 配列を走査します。次に、ループを使ってドロップダウンの子要素アイテムを操作できます。

var navigationConfig = [
    {
        href: 'http://ryanclark.me',
        text: 'My Website',
        children: [
            {
                href: 'http://ryanclark.me/how-angularjs-implements-dirty-checking/',
                text: 'Angular Dirty Checking'
            },
            {
                href: 'http://ryanclark.me/getting-started-with-react/',
                text: 'React'
            }
        ]
    }
];
var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        var config = this.props.config;
        var items = config.map(function (item) {
            var children, dropdown;
            if (item.children) {
                children = item.children.map(function (child) {
                    return (
                        <li className="navigation__dropdown__item">
                            <a className="navigation__dropdown__link" href={ child.href }>
                                { child.text }
                            </a>
                        </li>
                    );
                });
                dropdown = (
                    <ul className="navigation__dropdown">
                        { children }
                    </ul>
                );
            }
            return (
                <li className="navigation__item">
                    <a className="navigation__link" href={ item.href }>
                        { item.text }
                    </a>
                    { dropdown }
                </li>
                );
        });
        return (
            <div className="navigation">
                { items }
            </div>
            );
    }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

サンプルはこちら- しかし、ドロップダウンの項目はまだ見えます。たとえ私たちが設定してもopenDropdown に設定しても-1 。

コンポーネント内でアクセスすることでthis.state、ドロップダウンが開いているかどうかを判断できます。さらに、マウスホバー効果を表示するために新しい CSS スタイルクラスを追加できます。

var navigationConfig = [
    {
        href: 'http://ryanclark.me',
        text: 'My Website',
        children: [
            {
                href: 'http://ryanclark.me/how-angularjs-implements-dirty-checking/',
                text: 'Angular Dirty Checking'
            },
            {
                href: 'http://ryanclark.me/getting-started-with-react/',
                text: 'React'
            }
        ]
    }
];
var Navigation = React.createClass({
    getInitialState: function () {
        return {
            openDropdown: -1
        };
    },
    getDefaultProps: function () {
        return {
            config: []
        }
    },
    openDropdown: function (id) {
        this.setState({
            openDropdown: id
        });
    },
    closeDropdown: function () {
        this.setState({
            openDropdown: -1
        });
    },
    propTypes: {
        config: React.PropTypes.array
    },
    render: function () {
        var config = this.props.config;
        var items = config.map(function (item, index) {
            var children, dropdown;
            if (item.children) {
                children = item.children.map(function (child) {
                    return (
                        <li className="navigation__dropdown__item">
                            <a className="navigation__dropdown__link" href={ child.href }>
                                { child.text }
                            </a>
                        </li>
                    );
                });
                var dropdownClass = 'navigation__dropdown';
                if (this.state.openDropdown === index) {
                    dropdownClass += ' navigation__dropdown--open';
                }
                console.log(this.state.openDropdown, index);
                dropdown = (
                    <ul className={ dropdownClass }>
                        { children }
                    </ul>
                );
            }
            return (
                <li className="navigation__item" onMouseOut={ this.closeDropdown } onMouseOver={ this.openDropdown.bind(this, index) }>
                    <a className="navigation__link" href={ item.href }>
                        { item.text }
                    </a>
                    { dropdown }
                </li>
                );
        }, this);
        return (
            <div className="navigation">
                { items }
            </div>
            );
    }
});
React.render(<Navigation config={ navigationConfig } />, document.body);

サンプルはこちら– マウスを "My Website" に乗せると、ドロップダウンが表示されます。

先ほど、各リスト項目にマウスイベントを追加しました。ご覧のとおり、私は〜を使用しています。.bind(バインド) 呼び出しを使用し、他の方法での呼び出しは使用しません。これは、ユーザーのマウスが要素領域から外れたとき、カーソルがどこにあるかを気にする必要はなく、ドロップダウンを閉じる必要があるだけだからです。そのため、値を -1 に設定できます。しかし、ユーザーのマウスが要素に乗ったときにどの要素がドロップダウンで展開されたかを知る必要があるため、そのパラメータ(要素のインデックス)を知る必要があります。単純な関数(function)で呼び出すのではなくバインドを使って呼び出すのは、React を通じて呼び出す必要があるからです。直接呼び出すと、イベント内で呼び出すのではなく、常に呼び出すことになります。

次に、多くの項目を〜に追加できます。navigationConfig に追加できます。また、ドロップダウン機能にスタイルを追加することもできます。サンプルを見る。

まとめ

React はシンプルで便利で使いやすく、強力なフレームワークです。そのシンプルな API により、複雑な DOM を気にせず、特に繰り返しのリスト項目などで特別に複雑な操作をする必要なく、素早く独自のコンポーネントを作成できます。

出典:http://www.oschina.net/translate/getting-started-with-react