SIerからWeb系に転職したやつの日記

タイトルの通り大手SIerからWeb系に転職した、世間一般的には変な奴のブログです。技術的な成長記録を書いていこうと思います。筆者とともに成長するブログです。今は初心者のような内容しか書いてませんが、そのうち上級者のようなブログになってるはずです。

create-reacte-appとMaterial-UIで簡単にいけてそうなReactアプリケーションを作る

f:id:regponpon:20190109222904j:plain

今日は今流行りに流行ってるReactについての記事を書こうと思います。 かなり初歩的な内容ですが、初心者の方にはうってつけかと思います。

目標

ReactでHelloWorldよりも少し凝ったWebサイトを作れるようになる

create-react-appを使おう

ReactでWebアプリケーションを作成するためのとっかかりがめんどくさいので簡単にBoilerPlateを作成する必殺技があります。 それが今回ご紹介するcreate-react-appです。

インストール

今回はyarnを使って行きたいと思いますが、別にnpmでも構いません。適宜読み替えてください。 まずはnodeとyarnのバージョンを確認します。

$ node -v
v8.14.0
$ yarn -v
1.12.3

続いて今回使うcreate-react-appをglobalにインストールします。

 $ yarn add -global create-react-app

今回作成するアプリケーションはreact-exampleとします。 自分の作業したいディレクトリで以下のコマンドを実行します。

$ create-react-app react-example

すると

$ create-react-app react-example

Creating a new React app in /Users/regpon/test/react-example.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts...

yarn add v1.12.3
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 📃  Building fresh packages...
success Saved lockfile.
success Saved 22 new dependencies.
info Direct dependencies
├─ react-dom@16.7.0
├─ react-scripts@2.1.3
└─ react@16.7.0
info All dependencies
├─ @babel/runtime@7.1.5
├─ autoprefixer@9.4.4
├─ babel-plugin-transform-react-remove-prop-types@0.4.20
├─ babel-preset-react-app@7.0.0
├─ cssdb@4.3.0
├─ detect-node@2.0.4
├─ eslint-config-react-app@3.0.6
├─ handle-thing@2.0.0
├─ postcss-color-gray@5.0.0
├─ postcss-custom-media@7.0.7
├─ postcss-custom-properties@8.0.9
├─ postcss-double-position-gradients@1.0.0
├─ postcss-preset-env@6.3.1
├─ react-app-polyfill@0.2.0
├─ react-dev-utils@7.0.1
├─ react-dom@16.7.0
├─ react-error-overlay@5.1.2
├─ react-scripts@2.1.3
├─ react@16.7.0
├─ spdy-transport@3.0.0
├─ spdy@4.0.0
└─ webpack-dev-server@3.1.14
✨  Done in 16.93s.

Initialized a git repository.

Success! Created react-example at /Users/regpon/test/react-example
Inside that directory, you can run several commands:

 yarn start
   Starts the development server.

 yarn build
   Bundles the app into static files for production.

 yarn test
   Starts the test runner.

 yarn eject
   Removes this tool and copies build dependencies, configuration files
   and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

 cd react-example
 yarn start

Happy hacking!

こんな具合にインストールが始まります。 作成されたreact-exampleディレクトリをVisualStudioCodeで開いてみると、

f:id:regponpon:20190109220943p:plain

こんな感じでファイルが作成されていると思います。 これでインストールは完了です。

実行してみる

react-exampleディレクトリ に移動して以下のコマンドを実行します。

$ cd react-example
$ yarn start

上記のコマンドを実行すると、自動で以下が表示されたブラウザが立ち上がります。

f:id:regponpon:20190109221032p:plain

また、コンソールには以下が表示されていると思います。

Compiled successfully!

You can now view react-example in the browser.

  Local:            http://localhost:3000/
  On Your Network:  http://192.168.11.63:3000/

Note that the development build is not optimized.
To create a production build, use yarn build.

これでReactによるWebアプリケーションの起動の確認まで完了です。

ちょっと変更してみる前にApp.jsを確認

ブラウザの指示通りに、src/App.jsを少しいじってみましょう。

デフォルトだと、

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

export default App;

こんな感じです。ざっくり説明すると、このApp.jsにはReactComponentクラスを拡張したAppクラスというクラスがあり、そのクラスにはrender()という関数が定義されています。

Componentとは一言で言うと部品のことで、Reactはフロントサイドのフレームワークなのでここで言う部品とは、DOM上に表示される意味を持った塊のようなものです。

例えば、このWikiであれば、上部のメニューバーや編集画面の編集フィールド、プレビューフィールドなどが部品になっています。 また、その部品をさらに細かく管理することもできますが、細かすぎると意味不明になるので、よく言われるのは3回程度他のページでも使い回される部品はComponent化するといいそうです。

そして、このrender()関数はComponentが呼び出されて、サーバーサイドでレンダリングする際に呼ばれます。 ReactはNode.jsのフレームワークなのでサーバーサイドでレンダリングし、クライアントにはHTMLを返します。つまりJavascriptからHTMLを生成する工程をサーバーサイドで行うと言うことです。

ここではsrc/index.jsApp.jsコンポーネントとして呼び出しているので、そいつが実行されることでこれらの処理が作動します。index.jsについては説明は端折りますが、最初に呼ばれるjsファイルなんだな。こいつに他のjsファイルを実行させるんだなと思っといてください。

コンポーネントをサーバーサイドレンダリングする際に実行されるのがrender()関数で、みてみるとすぐにreturn()をして、なにやらHTMLを返しています。 これは厳密言うとHTMLではなくJSXと呼ばれる構文のなのですが、Javascriptとして扱うHTMLのようなものです。

JSXは必ず一つのタグでなければならないと言う制約があります。一つといっても階層構造が一つであればいいので、上記のように子のタグを持つ分には問題ないです。

class App extends Component {
  render() {
    return (
      <div className="App">
        <p>aaa</p>
      </div>
      <p>bbb</p>
    );
  }
}

上述のように<div></div>と同じ階層に<p></p>がいるような状態がダメということですね。 シンタックスをみても変な感じになってますよね。ちゃんとJSXとして認識されていない証拠です。

App.jsをいじる

まぁここまで説明したらあとは好きにいじってもらえればと思います。

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            SIerからWeb系に転職したやつの日記
          </p>
          <p>
            SIerやめてよかった
          </p>
        </header>
      </div>
    );
  }
}

export default App;

くだらないですがちょっと上記のように修正しました。 この状態で保存するだけで即反映されます。

f:id:regponpon:20190109221702p:plain

ちょっといい感じのUIコンポーネントつけてみる

ここではMaterial-UIを使っていい感じの見た目のコンポーネントを付け加えてみようと思います。 Material-UIってのはbootstrapみたいな感じでいい感じの見た目にしてくれるやつで、Googleが提唱しているマテリアルデザインをReactで実現できるようにしたものです。

こいつをまずインストールします。

$ yarn add material-ui

続いて、Material-UIを使ってメニューバーをつけてみましょう。 そのためにまずsrcディレクトリにコンポーネント用のディレクトリを作り、そこに作成して行きたいと思います。 作成するファイル:src/component/MenuBar.js

※コマンドはだるいのでVisualStudioCodeで作業しましょう。

import React, { Component } from 'react';
import AppBar from 'material-ui/AppBar';

class MenuBar extends Component {
  render() {
    return (
      <AppBar
        title="React Example"
        iconClassNameRight="muidocs-icon-navigation-expand-more"
      />
    )
  };
}

export default MenuBar;

次に、このメニューバーを読み込みために、index.jsに呼び出させます。 index.jsはデフォルトだとこんな感じだと思います。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

これを以下のように変更してください

import React from 'react';
import ReactDOM from 'react-dom';

import MenuBar from './component/MenuBar';
import App from './App';
import * as serviceWorker from './serviceWorker';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';

ReactDOM.render(
  <React.Fragment>
    <MuiThemeProvider>
      <MenuBar />
    </MuiThemeProvider>
    <App />
  </React.Fragment>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

これで保存して実行すればメニューバーが上部に出ると思います。

f:id:regponpon:20190109221800p:plain

左上のハンバーガーメニューをクリックしても今はなにも出ませんが、メニューをつけるようにすることも可能です。

ちょっと解説

コンポーネント

まずMenuBarコンポーネントから簡単に説明します。

まぁめちゃくちゃ単純です

import React, { Component } from 'react';
import AppBar from 'material-ui/AppBar';

class MenuBar extends Component {
  render() {
    return (
      <AppBar
        title="React Example"
        iconClassNameRight="muidocs-icon-navigation-expand-more"
      />
    )
  };
}

export default MenuBar;

まず、先ほどyarn addしたmaterial-uiAppBarというものをimportしています。これはMaterial-UIにおける画面上部に設置するメニューバーはこれ使っとけばええ感じにするで。ってやつで、オプションでタイトルとハンバーガーメニューの表示を指定してます。

これをApp.js同様、Componentクラスを拡張させてrender()関数に記載しました。

index.js編

次にindex.jsですが、

import React from 'react';
import ReactDOM from 'react-dom';

import MenuBar from './component/MenuBar';
import App from './App';
import * as serviceWorker from './serviceWorker';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';

ReactDOM.render(
  <React.Fragment>
    <MuiThemeProvider>
      <MenuBar />
    </MuiThemeProvider>
    <App />
  </React.Fragment>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

基本的には追加をしただけですね。 import MenuBar from './component/MenuBar';は作成したコンポーネントindex.jsで使えるようにimportしているだけ。 import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';はMaterial-UIを動かすためのコンポーネントで、これもまたimportしています。

続いてReactDOM.render()ですが、結構大きく変わったように見えますが、改行して、<App />以外を追加しただけです。 まず、JSXのタグは一つの原則に基づいて、複数コンポーネントを表示するために親のタグを<App>から変える必要があったので、<React.Fragment></React.Fragment>でくくりました。これは慣例のようなものですね。<div></dix>でくくって一つのタグとしてもいいのですが、<div>を使うとブラウザ側にも<div>が返ってしまい、無駄なDOMツリーができてしまいます。JSXの制約のために一つにまとめたいだけの時は、<React.Fragment>で括ることで、ブラウザ側には無駄な階層が表示されません。

試しにやってみると、

ReactDOM.render(
  <div>
    <MuiThemeProvider>
      <MenuBar />
    </MuiThemeProvider>
    <App />
  </div>,
  document.getElementById('root')
);

の場合は、

f:id:regponpon:20190109222519p:plain

ReactDOM.render(
  <React.Fragment>
    <MuiThemeProvider>
      <MenuBar />
    </MuiThemeProvider>
    <App />
  </React.Fragment>,
  document.getElementById('root')
);

の場合は、

f:id:regponpon:20190109222505p:plain

のように、<div id="root">/div>の一個下の階層にあった余分な<div></div>が消えているのがわかります。 細かいところではありますが、不必要にタグを増やすとレンダリングコストや表示コストの増加に繋がりますし、何より見にくくなるので覚えておきましょう。

あとは新たに作成したMenuBarコンポーネントを呼び出すために、MuiThemeProviderコンポーネントを呼び出し、MenuBarコンポーネントの親階層にしています。Material-UIで作成したコンポーネントは皆、MuiThemeProviderコンポーネントの子階層として呼び出さないと動かないので、このように書きます。

まとめ

いかがだったでしょうか。create-react-appとMaterial-UIを使えばあっちゅーまにいい感じの見た目のWebアプリが作れちゃいます。いろいろ試しながらやってみてはいかがでしょうか。

Material-UIのリファレンスに使い方はまとまっていて、サンプルも充実しているのでよかったらコピペしてコンポーネント作ってみてはどうですか? Material-UIのCardとかいけてるSNSのUIっぽくて結構好き。

Card使ってアフロのSNSっぽいページ作ってみたのがこれ。ほぼコピペ。

f:id:regponpon:20190109222037p:plain

これであなたもReactエンジニアです。