LoginSignup
1
1

More than 5 years have passed since last update.

Falcor+Reactフルスタック(Material-UI)

Last updated at Posted at 2017-08-18

Meteor and Reactによるリアクティブシステム「ラーメン野郎を追いかけろ! @ Twitter」を作ってみた

Falcor+Reactフルスタック(開発環境)
Falcor+Reactフルスタック(react-router)
Falcor+Reactフルスタック(views層とcomponents層)
Falcor+Reactフルスタック(formsy-react)
Falcor+Reactフルスタック(エラー処理)
Falcor+Reactフルスタック(Material-UI)

 今回はMaterial-UIについてです。小池都知事は都民ファーストですが、最近のJavaScriptアプリはモバイルファーストだそうです。ちなみに小沢一郎は「生活が第一」でした。
 従来は、TwitterのBootstrapなどが多くの人に採用されて、モバイルでもPCでもきれいにデザインされたWebアプリが作られてきました。最近はGoogleがMaterial Design Liteというものを公開して、Bootstrapの置き換えに使われてきつつあるようです。それぞれのメリットだけを簡単に言えば、Bootstrapは実績と膨大なリソースがあり、Materialはモダンなデザインが売りなようです。しかし私はBootstrapもMaterialも生で使うには理解が難しいほど複雑で敬遠していました(コピペ文化なんでしょうけど)。しかしReactではどちらも使い易くcomponent化されたライブラリがあり、複雑さが隠蔽され気持ちよく使えるようになっています。その1つがMaterial-UIです。Reactプログラマは、これを使えば複雑で難解な「デザイン」(しかもモバイルファーストです)から解放されるといううわけです。積極的に使いたいものです。
http://www.material-ui.com/

 今回のサンプルプログラムで新しいMaterial-UIのCard componentを導入してみたいと思います。まずはトップのホームページを一見してから、この続きを読まれることをお勧めします。
http://www.mypress.jp:3021/

 前にも述べましたが、トップcomponentのroutes.jsでテーマを設定しています。短いソースなので再度リストします。テーマさえ設定すれば、後は好きな場所で、好きなMaterial-UI componentを使うことが可能になります。

src/routes.js
import React  from 'react';
import {render} from 'react-dom'
import {BrowserRouter} from 'react-router-dom'

//import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'; <==テーマをimport
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AppBar from 'material-ui/AppBar';

import App from './App';
import PageNotFound from './components/PageNotFound'

const target = document.getElementById('appRoot');
const node =(
  <MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}> <==テーマでアプリ全体をラッピング
      <BrowserRouter>
        <App />
      </BrowserRouter>
  </MuiThemeProvider>
);
render(node, target);

 次に、ホームページを改良します。ホームページには以下のような記事(articles)を掲載することとしましょう。この記事表示のデザインにCardを使うつもりです。

initData.js
[
  {
    name: '山田太郎',
    avatar: 'avatar1.png',
    title: '花火大会',
    content: '今日は隅田川の花火大会でした。とても美しかったです。人が大勢いました。(本当は雨で辛かったけど)'
  },
  {
    name: '鈴木花子',
    avatar: 'avatar2.png',
    title: 'プールで遊んだ',
    content: '東武動物公園のプールに行きました。大きなプールでいっぱいありました。とても楽しかったです。(天気悪くて寒かったけど)'
  },
  {
    name: '魔法使いサリー',
    avatar: 'avatar3.png',
    title: '魔法を使えるとしたら',
    content: 'やはり、夏なので天気をよくする魔法を使いたいです。魔法で温暖化や氷河期の到来も防ぎたいです。'
  }
]

 この記事をコマンドラインからMongoDBにimportします

mongoimport --db qiita9db --collection articles --jsonArray initData.js --host=127.0.0.1

 プログラムの修正です。まずはarticlesコレクションを追加しましたのでMongooseのスキーマを追加します。

server/configMongoose.jsに追加
const articleSchema = {
  name:String,
  avatar:String,
  title:String,
  content:String
};

const Article = mongoose.model('Article', articleSchema, 'articles');

 クライアント側のホームページからarticlesを取得しますので、サーバ側のfalcor routerに以下の2つのパスを追加します。

server/routesSession.jsに追加
  {
    route: 'articles.length',
    get: () => Article.count({}, (err, count) => count)
      .then ((articlesCountInDB) => {
        return {
          path: ['articles', 'length'],
          value: articlesCountInDB
        };
      })
  },

  {
    route: 'articles[{integers}]["id","name","avatar","title","content"]',
    get: (pathSet) => {
      const articlesIndex = pathSet[1];
      return Article.find({}, (err, articlesDocs) => articlesDocs)
        .then ((articlesArrayFromDB) => {
          let results = [];

          articlesIndex.forEach((index) => {
            const singleArticleObject = articlesArrayFromDB[index].toObject();
            const falcorSingleArticleResult = {
              path: ['articles', index],
              value: singleArticleObject
            };

            results.push(falcorSingleArticleResult);
          });

          return results;
        });

    }
  },

 この2つのパスにクライアント側のホームページからアクセスしてarticlesを取得します。falcor的に言ううとVirtual JSONにアクセスします。以下のようになります。

src/views/HomeApp.js
import React from 'react';
import Falcor from 'falcor';
import falcorModel from '../falcorModel.js';
import ArticleCard from '../components/ArticleCard';

export default class HomeApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      articles: null
    };
  }

  componentWillMount() {
    this._fetch(); 
  }

  async _fetch() {
    const articlesLength = await falcorModel
      .getValue('articles.length')
      .then((length) => length);

    const articles = await falcorModel
      .get(['articles', {from: 0, to: articlesLength-1}, ['_id','name','avatar','title', 'content']])
      .then((articles) => articles.json.articles);

      this.setState({articles: articles});
  }

  render () {
    let articlesJSX = [];
    for (let idx in this.state.articles) {
      const article = this.state.articles[idx];
      let currentArticleJSX = (
          <div key={idx}>
            <ArticleCard name={article.name} avatar={article.avatar} title={article.title} content={article.content} />
          </div>
      );
      articlesJSX.push(currentArticleJSX);
    }

    return (
      <div>
        <h1>私のホームページへようこそ !!!</h1>
        <div style={{height: '100%', width: '75%', margin: 'auto'}}>
          {articlesJSX}
        </div>
      </div>
    );
  }
}

 HomeApp.jsの修正には2つのポイントがあります。1つ目はfalcorを使ってarticlesを取得していること。2つ目はArticleCardという下層のcomponentを呼び出して、取得したarticlesを描画していること、です。前々回で述べた言葉で言えば、HomeApp.jsはViews層に属し、下層のComponents層のArticleCardを呼び出している。ArticleCardではMaterial-UIのCard componentを利用して具体的な描画を定義している、ということです。

 Material-UIのCard componentは、関係ある複数のコンテンツをまとめて表示するためのものです。このサンプルでは記事タイトル、内容、アバター画像、名前、背景画像などをカードの枠組のなかに表示しています。ArticleCard componentを確認してください。CSSの記述を除けば短いものです。

src/components/ArticleCard.js
import React from 'react';
import { Card, CardHeader, CardMedia, CardTitle, CardText } from 'material-ui/Card';
import { Paper } from 'material-ui';

class ArticleCard extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    let {name,avatar,title,content} = this.props;
    let avatar1 = "/"+avatar;

    let paperStyle = {
      padding: 10,
      width: '100%',
      height: 300
    };

    let leftDivStyle = {
      width: '30%',
      float: 'left'
      }

    let rightDivStyle = {
      width: '60%',
      float: 'left',
      padding: '10px 10px 10px 10px'
    }

    return (
      <Paper style={paperStyle}>

        <CardHeader title={name} subtitle="サブタイトル(名前)" avatar={avatar1}/>

        <div style={leftDivStyle}>
          <Card >
            <CardMedia overlay={<CardTitle title={title} subtitle="サブタイトル(画像)" />}>
              <img src="/placeholder.jpg" height="140" />
            </CardMedia>
          </Card>
        </div>

        <div style={rightDivStyle}>
          <div dangerouslySetInnerHTML={{__html: content}} />
        </div>

      </Paper>);
  }
};
export default ArticleCard;

 以上で今回の説明は終わりますが、Falcorシリーズはひとまず今回で終わりたいと思います。

 次回は Meteor + React について少し書きたいと思います。

1
1
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
1
1