MeteorJSとReactを勉強してみる その2: ReactコンポーネントでViewを作成しよう【公式翻訳】
Meteor公式翻訳の第二弾です。
基本的に1レッスン1記事、計12記事で書く予定です。
- ReactコンポーネントでViewを定義する
- 初期のコードを書き換えよう
- 変更結果を確認してみよう
- HTMLファイルには静的コンテンツを書く
- ReactでViewを構成する
- マークアップ(HTML)の内容はJSXのrenderメソッドでレンダリングされる
- CSSを追加する
ReactコンポーネントでViewを定義する
アプリのViewライブラリとしてReactを利用するにあたって、まずはいくつかのNPMパッケージをインストールしましょう。 新しくターミナルの画面を開いて、前回作成したアプリがあるディレクトリまで移動して下記のコマンドを打ってください。
meteor npm install --save react react-dom
Note: meteor npmコマンドはnpmコマンドと同様の挙動をしますが、一部重要な差があります。詳細についてはmeteor npmのドキュメントを参照してください。
初期のコードを書き換えよう
手始めに、前回作成したデフォルトアプリのコードを書き換えましょう。 まず、client/main.htmlを下記のように書き換えます。
before
<head> <title>react-meteor-app</title> </head> <body> <h1>Welcome to Meteor!</h1> {{> hello}} {{> info}} </body> <template name="hello"> <button>Click Me</button> <p>You've pressed the button {{counter}} times.</p> </template> <template name="info"> <h2>Learn Meteor!</h2> <ul> <li><a href="https://www.meteor.com/try" target="_blank">Do the Tutorial</a></li> <li><a href="http://guide.meteor.com" target="_blank">Follow the Guide</a></li> <li><a href="https://docs.meteor.com" target="_blank">Read the Docs</a></li> <li><a href="https://forums.meteor.com" target="_blank">Discussions</a></li> </ul> </template>
after
<head> <title>Todo List</title> </head> <body> <div id="render-target"></div> </body>
次に、client/main.jsも下記に書き換えましょう。
before
import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import './main.html'; Template.hello.onCreated(function helloOnCreated() { // counter starts at 0 this.counter = new ReactiveVar(0); }); Template.hello.helpers({ counter() { return Template.instance().counter.get(); }, }); Template.hello.events({ 'click button'(event, instance) { // increment the counter when button is clicked instance.counter.set(instance.counter.get() + 1); }, });
after
import React from 'react'; import { Meteor } from 'meteor/meteor'; import { render } from 'react-dom'; import App from '../imports/ui/App.js'; Meteor.startup(() => { render(<App />, document.getElementById('render-target')); });
そうしたら、次はimportsという新しいディレクトリを作ります。 Meteorのプロジェクトにおいて、importsという名前のディレクトリは特別で、他のものとは異なる挙動をします。
importsの中にないファイルはMeteorサーバーが立ち上がると自動的にロードされますが、importsだけはimport
宣言がされたときのみにロードされます。
importsディレクトリをアプリのディレクトリ直下に作成したら、その中に新しく2つのファイルを作成しましょう。
imports/ui/App.js
import React, { Component } from 'react'; import Task from './Task.js'; // App component - represents the whole app export default class App extends Component { getTasks() { return [ { _id: 1, text: 'This is task 1' }, { _id: 2, text: 'This is task 2' }, { _id: 3, text: 'This is task 3' }, ]; } renderTasks() { return this.getTasks().map((task) => ( <Task key={task._id} task={task} /> )); } render() { return ( <div className="container"> <header> <h1>Todo List</h1> </header> <ul> {this.renderTasks()} </ul> </div> ); } }
imports/ui/Task.js
import React, { Component } from 'react'; // Task component - represents a single todo item export default class Task extends Component { render() { return ( <li>{this.props.task.text}</li> ); } }
ここまでで3つの新しい要素をアプリに追加しました。
3つめの初期化文には、ページがロードされ表示の準備が整った状態で、どのように他のコードを呼び出すかを書いています。
この初期化文は他のコンポーネントをロードし、#render-target
内のHTMLとして描写します。
importsディレクトリがどのように動作するのか、そして実際にはどのような構成でアプリを作っていけば良いのか、より詳細にはApplication Structure articleを読んでみてください。
チュートリアルを進め、コードを追加・変更していく中でこれらのコンポーネントについても再度解説します。
変更結果を確認してみよう
アプリを立ち上げてブラウザを見てみると、アプリの内容が下記のように書き換わっているのが分かるかと思います。
もしこのようになっていなければ、このページ(訳者注: 元ページ)の各コードスニペットの右上リンクからGitHubをチェックし、自分の書いたコードが確実に例と合致しているか確認してみてください。
HTMLファイルには静的コンテンツを書く
Meteorはアプリのフォルダ内の全てのHTMLファイルを解析し、<head>
、<body>
、<template>
の3つのタグを特定します。
<head>
内の全ての要素はクライアントに送信されるHTMLのheadセクションに、そして<body>
内の全ての要素はbodyセクションに追加されます。これは普通のHTMLファイルと同様です。
ただ、<template>
内の全ては「Meteor template」にコンパイルされます。templateはHTML内では{{> templateName}}
、JavaScript内ではTemplate.templateName
で表現されます。
このチュートリアルでは、View内のコンポーネント定義には全てReactを利用するので、templateは利用しません。
ReactでViewを構成する
Reactでは、ViewコンポーネントはReact.Componentのサブクラスです。
なお、このReact.Componentはimports/ui/App.js
で書いたようにimport { Component } from 'react';
という宣言でインポートできます。
自分で作ったコンポーネントには好きなメソッドを持たせることができますが、render
のように特殊な機能を持つメソッドが既に存在しています。
また、コンポーネントはその親からprop
という属性でデータを受け取ることができます
このチュートリアルではさらにいくつかのReactの一般的な特徴について解説していきます。詳細なReactの解説については、FacebookのReactチュートリアルを参照してください。
マークアップ(HTML)の内容はJSXのrenderメソッドでレンダリングされる
どのReactコンポーネントにおいても最も重要なのが、render()
メソッドです。
そのコンポーネントは表示すべきである、というHTMLの内容を取得するためにReactによって呼び出されるメソッドがrender()
です。
Reactでは、HTMLはJSXというJavaScriptの拡張構文で表現します。イメージとしては、JavaScriptファイルの中にHTMLをそのまま書く感じです。
ただし既に見たとおり、明らかに通常のHTMLと異なる点がいくつかあります。
特に理解すべきなのが、JSXではclassではなくclassName属性を用いるということです。 重要なのは、JSXはSpacebarsやAngularのようなテンプレート言語ではない、ということです。JSXはそのまま正規のJavaScriptコードにコンパイルされます。 より詳しくは、Reactのドキュメントで読んでください。
JSXはecmascriptのAtmosphereJSパッケージを利用していますが、新しいMeteorアプリの中にはデフォルトで上記が含まれています。
CSSを追加する
before
/* CSS declarations go here */
after
/* CSS declarations go here */ body { font-family: sans-serif; background-color: #315481; background-image: linear-gradient(to bottom, #315481, #918e82 100%); background-attachment: fixed; position: absolute; top: 0; bottom: 0; left: 0; right: 0; padding: 0; margin: 0; font-size: 14px; } .container { max-width: 600px; margin: 0 auto; min-height: 100%; background: white; } header { background: #d2edf4; background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%); padding: 20px 15px 15px 15px; position: relative; } #login-buttons { display: block; } h1 { font-size: 1.5em; margin: 0; margin-bottom: 10px; display: inline-block; margin-right: 1em; } form { margin-top: 10px; margin-bottom: -10px; position: relative; } .new-task input { box-sizing: border-box; padding: 10px 0; background: transparent; border: none; width: 100%; padding-right: 80px; font-size: 1em; } .new-task input:focus{ outline: 0; } ul { margin: 0; padding: 0; background: white; } .delete { float: right; font-weight: bold; background: none; font-size: 1em; border: none; position: relative; } li { position: relative; list-style: none; padding: 15px; border-bottom: #eee solid 1px; } li .text { margin-left: 10px; } li.checked { color: #888; } li.checked .text { text-decoration: line-through; } li.private { background: #eee; border-color: #ddd; } header .hide-completed { float: right; } .toggle-private { margin-left: 5px; } @media (max-width: 600px) { li { padding: 12px 15px; } .search { width: 150px; clear: both; } .new-task input { padding-bottom: 5px; } }
CSSを追加したので、アプリの見栄えも良くなったはずです。 ブラウザで上記のスタイルが読み込まれているか確認しましょう。
第二弾はここまでです。 どうでしょうか。僕の翻訳もごく一部自信のない部分があるので、もし間違いに気づいたら教えてくださいm( )m
第三弾は来週中にでも。