REQLY開発ブログ

プロの料理レシピ専門検索エンジンREQLY(レクリー)の開発ブログです。

material-uiによる検索モード搭載のAppBarの実装

はじめに

REQLYではGoogleの提唱するマテリアルデザインに基づいた実装が行われています。material-uiは、マテリアルデザインを構成するUIパーツのReactライブラリであり、REQLYではスマホ版のAppBarなどの実装に取り入れられています。material-uiの1つ1つのコンポーネントの使い方は、公式のDemoページにサンプルコードと共に丁寧に書かれていますが、どのようにして複数のコンポーネントを組み合わせて実用的なデザインを組み上げていくのか、という実践的な内容についての情報は多くありません。今回は、REQLYのAppBarを例にmaterial-uiの実践的な使い方をしっかり説明していきたいと思います。


完成品

Dockerによって再現可能なコードをGitHub - ReqlyTokyo/appbarにて公開しています。

 

f:id:reqly-tokyo:20180616201005g:plain



実装詳細

1. DockerによるReact開発環境の作成

まずは、Dockerを用いて逐次実行可能なポータブルなReact環境を作成するところから始めよう。facebookが公開しているcreate-react-appというコマンドを使えば、開発のベースとなるReactアプリケーションを簡単に作成することができる。create-react-appコマンドのインストール方法は公式ページを参照されたい。

create-react-app myapp
cd myapp
npm start

これによりhttp://localhost:3000/にデフォルトページが立ち上がるはずだ。

次にこのアプリケーションをDockerfileとdocker-compose.ymlを使ってコンテナ化しよう。

Dockerfile
FROM node:9
WORKDIR /myapp

COPY ./myapp/package.json .
RUN npm install
CMD ["npm", "start"]

./myapp/package.jsonはReactプロジェクトに必要なライブラリの依存関係等が記述されたファイルで、npm installをRUN命令で実行することで、必要なライブラリがインストールされたDockerイメージを作成できる。

docker-compose.yml
myapp:
    build: .
    ports:
     - "3000:3000"
    volumes:
     - ./myapp/src:/myapp/src
     - ./myapp/public:/myapp/public

3000番ポートの公開のほか、volumes命令によって./myapp/src./myapp/publicをそれぞれマウントしている。これにより、Dockerコンテナを立ち上げた状態でホスト側で./myapp/src./myapp/public以下のファイルを編集したときに、リアルタイムで更新が走るようにできる。



2. material-uiによる必要なUIコンポーネントの実装

material-uiの導入

./myapp/package.jsonに新たに@material-ui/core - npm@material-ui/icons - npmへの依存関係を定義する。coreだけでなくiconsも使えば、マテリアルデザイン公式アイコン集を簡単に実装に組み込むことが可能になる。

"dependencies": {
    "@material-ui/core": "^1.2.0",
    "@material-ui/icons": "^1.1.0",
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
}

上の2行をdependenciesに追記した状態でdocker-compose buildを実行すれば、無事に必要なライブラリが揃ったDockerイメージが作られる。

次に通常モードと検索モードを表現するクラスをそれぞれ独立に実装していこう。

通常モードの実装(appbar/DefaultBar.js at master · ReqlyTokyo/appbar · GitHub

イメージに非常に近いものがDemoページの3番目に既に実装されている。MenuIconを廃止し、SearchIconを新たに導入すれば、ほとんどイメージに近いものが得られる。この段階ではSearchIconにonClickが定義されていないので遷移自体は起こらないが、マテリアルデザインに基づいたクリック時のエフェクトなどが既に自動的に付与されていることが分かるだろう。

検索モードの実装(appbar/SearchBar.js at master · ReqlyTokyo/appbar · GitHub

検索ウィンドウ自体はmaterial-uiに実装されていないが、material-uiを用いて実装された検索ウィンドウがMITライセンスで公開されている。これについても、一番左にBackIconを追加するだけで、ほぼイメージ通りの検索バーを実装することができる。



3. AppBarの出し分けの実装

Default.jsとSearchBar.jsが定義されたので、あとはこの2つを出し分ける親クラス(ReqlyAppBar.js)を実装すれば目的は達成できる。

親クラスの実装(appbar/ReqlyAppBar.js at master · ReqlyTokyo/appbar · GitHub

class ReqlyAppBar extends React.Component{
  state = {
    search: false,
  };

  handleChange = event => {
    console.log("handleChange called!");
    this.setState({ ['search']: !this.state.search });
  };

  render () {
    if (this.state.search) {
      return (
        <SearchBar
          onChange={() => console.log('onChange')}
          onRequestSearch={() => console.log('onRequestSearch')}
          onHandleChange={this.handleChange}
        />
        );
    } else {
      return (
        <DefaultBar
          onHandleChange={this.handleChange}
        />
      );
    }
  }
}

export default ReqlyAppBar;

ReqlyAppBarは、searchというbooleanのstateを持ち、それがTrueだった場合は検索モード、Falseだった場合は通常モードを描画するクラスだ。searchを反転させる関数であるhandleChangeをコールバック関数としてSearchBar, DefaultBarの呼び出し時に渡していることが分かるだろう。あとは、検索アイコンまたは戻るボタンがクリックされた場合に、このコールバック関数を発火するようにすれば、ReqlyAppBarクラスのstateが変更されてReqlyAppBarが再描画され、2つのモードが切り替わることとなる。クリック時の発火はそれぞれのIconButtonのonClickパラメタにて登録できる。

SearchBar.js

<IconButton
  onClick={this.props.onHandleChange}
>
  <BackIcon />
</IconButton>

DefaultBar.js

<IconButton
  onClick={this.props.onHandleChange}
>
  <Search />
</IconButton>

以上により、サーチモードに切り替え可能なAppBarが無事実装できた。



おわりに

REQLYではAppBarの他にもレシピカードなどにmaterial-uiが使用されています。是非使ってみてください! reqly.tokyo