1. はじめに
マテリアル コンポーネント(MDC)は、デベロッパーがマテリアル デザインを実装する際に役立ちます。Google のエンジニアと UX デザイナーのチームが作成した MDC には、美しく機能的な UI コンポーネントが多数含まれており、Android、iOS、ウェブ、Flutter.material.io/develop に利用可能です。 |
MDC Web は、マテリアル デザインの原則を維持しながら、あらゆるフロントエンド フレームワークに統合できるように設計されています。次の Codelab では、MDC Web を基盤として使用する React コンポーネントを構築する手順を説明します。この Codelab で学んだ原則は、あらゆる JavaScript フレームワークに適用できます。
MDC Web の構築方法
MDC Web の JavaScript レイヤは、コンポーネントごとに 3 つのクラス(Component、Foundation、Adapter)で構成されています。このパターンにより、MDC Web はフロントエンド フレームワークと柔軟に統合できます。
Foundation には、マテリアル デザインを実装するビジネス ロジックが含まれています。Foundation は HTML 要素を参照しません。これにより、HTML 操作ロジックを Adapter に抽象化できます。Foundation には Adapter があります。
Adapter はインターフェースです。Adapter インターフェースは、マテリアル デザインのビジネス ロジックを実装するために Foundation によって参照されます。アダプターは、Angular や React などのさまざまなフレームワークで実装できます。Adapter の実装は DOM 構造とやり取りします。
コンポーネントには基盤があり、その役割は
- フレームワーク以外の JavaScript を使用して Adapter を実装します。
- Foundation のメソッドにプロキシするパブリック メソッドを提供します。
MDC Web が提供するもの
MDC Web のすべてのパッケージには、コンポーネント、基盤、アダプターが付属しています。Component をインスタンス化するには、ルート element を Component のコンストラクタ メソッドに渡す必要があります。Component は Adapter を実装し、DOM と HTML 要素を操作します。Component は Foundation をインスタンス化し、Adapter メソッドを呼び出します。
MDC Web をフレームワークに統合するには、そのフレームワークの言語/構文で独自のコンポーネントを作成する必要があります。フレームワークのコンポーネントは、MDC Web のアダプタを実装し、MDC Web の基盤を使用します。
作成するアプリの概要
この Codelab では、Foundation ロジックを使用してマテリアル デザインの React コンポーネントを実現するカスタム Adapter をビルドする方法について説明します。フレームワークへの統合にある高度なトピックを扱います。この Codelab ではフレームワークの例として React を使用していますが、このアプローチは他のフレームワークにも適用できます。
この Codelab では、上部アプリバーを作成し、上部アプリバーのデモページを再現します。デモページのレイアウトはすでに設定されているため、上部のアプリバーの作業を開始できます。トップ アプリバーには、次のものが含まれます。
- ナビゲーション アイコン
- アクション アイテム
- 短い、常に折りたたまれている、固定、目立つの 4 つのバリエーションがあります。
必要なもの:
- Node.js の最新バージョン(JavaScript パッケージ マネージャーである npm がバンドルされています)
- サンプルコード(次の手順でダウンロード)
- HTML、CSS、JavaScript、React に関する基礎的な知識
ウェブ開発の経験についてお答えください。
2. 開発環境をセットアップする
Codelab のスターター アプリをダウンロードする
スターター アプリは material-components-web-codelabs-master/mdc-112/starter ディレクトリ内にあります。
GitHub からクローンを作成する
GitHub からこの Codelab のクローンを作成するには、次のコマンドを実行します。
git clone https://github.com/material-components/material-components-web-codelabs
cd material-components-web-codelabs/mdc-112/starter
プロジェクトの依存関係をインストールする
スターター ディレクトリ material-components-web-codelabs/mdc-112/starter から、次のコマンドを実行します。
npm install
アクティビティが多数表示され、最後にターミナルにインストールが成功したことが表示されます。

スターター アプリを実行する
同じディレクトリで、次のコマンドを実行します。
npm start
webpack-dev-server が開始されます。ブラウザで http://localhost:8080/ にアクセスして、ページを表示します。

完了しました。Top App Bar React Demo ページのスターター コードがブラウザで動作しているはずです。lorem ipsum のテキストの壁、[Controls] ボックス(右下)、未完成のトップアプリバーが表示されます。

コードとプロジェクトを確認する
コードエディタを開くと、プロジェクト ディレクトリは次のようになります。

App.js ファイルを開き、<TopAppBar> コンポーネントを含む render メソッドを確認します。
App.js
render() {
const {isFixed, isShort, isRtl, isProminent, isAlwaysCollapsed, shouldReinit} = this.state;
return (
<section
dir={isRtl ? 'rtl' : 'ltr'}
className='mdc-typography'>
{
shouldReinit ? null :
<TopAppBar
navIcon={this.renderNavIcon()}
short={isShort}
prominent={isProminent}
fixed={isFixed}
alwaysCollapsed={isAlwaysCollapsed}
title='Mountain View, CA'
actionItems={this.actionItems}
/>
}
<div className={classnames('mdc-top-app-bar--fixed-adjust', {
'mdc-top-app-bar--short-fixed-adjust': isShort || isAlwaysCollapsed,
'mdc-top-app-bar--prominent-fixed-adjust': isProminent,
})}>
{this.renderDemoParagraphs()}
</div>
{this.renderControls()}
</section>
);
}
これは、アプリケーションの TopAppBar のエントリ ポイントです。
render メソッドを含む React の Component クラスである TopAppBar.js ファイルを開きます。
TopAppBar.js
import React from 'react';
export default class TopAppBar extends React.Component {
render() {
return (
<header>
TOP APP BAR
</header>
);
}
}
3. コンポーネントの構成
React では、render メソッドはコンポーネントの HTML を出力します。上部のアプリバー コンポーネントは <header /> タグをレンダリングし、次の 2 つの主要なセクションで構成されます。
- ナビゲーション アイコンとタイトルのセクション
- アクション アイコン セクション
トップアプリバーを構成する要素についてご不明な点がある場合は、GitHub のドキュメントをご覧ください。
TopAppBar.js の render() メソッドを次のように変更します。
render() {
const {
title,
navIcon,
} = this.props;
return (
<header
className={this.classes}
style={this.getMergedStyles()}
ref={this.topAppBarElement}
>
<div className='mdc-top-app-bar__row'>
<section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-start'>
{navIcon ? navIcon : null}
<span className="mdc-top-app-bar__title">
{title}
</span>
</section>
{this.renderActionItems()}
</div>
</header>
);
}
この HTML には 2 つのセクション要素があります。1 つ目はナビゲーション アイコンとタイトルを含みます。2 つ目はアクション アイコンを含みます。
次に、renderActionItems メソッドを追加します。
renderActionItems() {
const {actionItems} = this.props;
if (!actionItems) {
return;
}
return (
<section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-end' role='toolbar'>
{/* need to clone element to set key */}
{actionItems.map((item, key) => React.cloneElement(item, {key}))}
</section>
);
}
デベロッパーは TopAppBar を React アプリケーションにインポートし、アクション アイコンを TopAppBar 要素に渡します。App.js で TopAppBar を初期化するサンプルコードをご覧ください。
render メソッドで使用される getMergedStyles メソッドがありません。次の JavaScript メソッドを TopAppBar クラスに追加してください。
getMergedStyles = () => {
const {style} = this.props;
const {style: internalStyle} = this.state;
return Object.assign({}, internalStyle, style);
}
this.classes は render メソッドにもありませんが、後のセクションで説明します。欠落しているゲッター メソッド this.classes の他に、トップ アプリバーが正しくレンダリングされる前に実装する必要がある TopAppBar の部分がまだあります。
トップ アプリバーにまだ含まれていない React コンポーネントは次のとおりです。
- 初期化された基盤
- 基盤に渡すアダプタ メソッド
- JSX マークアップ
- バリエーション管理(固定、短縮、常に折りたたまれている、目立つ)
アプローチ
- Adapter メソッドを実装します。
componentDidMountで Foundation を初期化します。componentWillUnmountで Foundation.destroy メソッドを呼び出します。- 適切なクラス名を組み合わせたゲッター メソッドを使用して、バリアント管理を確立します。
4. Adapter メソッドを実装する
フレームワーク以外の JS TopAppBar コンポーネントは、次のアダプタ メソッドを実装します(詳細についてはこちらを参照)。
hasClass()addClass()removeClass()registerNavigationIconInteractionHandler()deregisterNavigationIconInteractionHandler()notifyNavigationIconClicked()setStyle()getTopAppBarHeight()registerScrollHandler()deregisterScrollHandler()registerResizeHandler()deregisterResizeHandler()getViewportScrollY()getTotalActionItems()
React には合成イベントがあり、コーディングのベスト プラクティスとパターンが異なるため、Adapter メソッドを再実装する必要があります。
アダプタのゲッター メソッド
TopAppBar.js ファイルで、次の JavaScript メソッドを TopAppBar に追加します。
get adapter() {
const {actionItems} = this.props;
return {
hasClass: (className) => this.classes.split(' ').includes(className),
addClass: (className) => this.setState({classList: this.state.classList.add(className)}),
removeClass: (className) => {
const {classList} = this.state;
classList.delete(className);
this.setState({classList});
},
setStyle: this.setStyle,
getTopAppBarHeight: () => this.topAppBarElement.current.clientHeight,
registerScrollHandler: (handler) => window.addEventListener('scroll', handler),
deregisterScrollHandler: (handler) => window.removeEventListener('scroll', handler),
registerResizeHandler: (handler) => window.addEventListener('resize', handler),
deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
getViewportScrollY: () => window.pageYOffset,
getTotalActionItems: () => actionItems && actionItems.length,
};
}
React にはスクロールやサイズ変更の合成イベントがなく、ネイティブ DOM イベント システムに委任されるため、スクロール イベントとサイズ変更イベントの登録用アダプタ API は、フレームワーク以外の JS バージョンと同じように実装されます。getViewPortScrollY は、React の API にない window オブジェクトの関数であるため、ネイティブ DOM にも委任する必要があります。アダプタの実装はフレームワークごとに異なります。
this.setStyle が欠落していることに気づくかもしれません。これは get adapter メソッドによって呼び出されます。TopAppBar.js ファイルで、不足している JavaScript メソッドを TopAppBar クラスに追加します。
setStyle = (varName, value) => {
const updatedStyle = Object.assign({}, this.state.style);
updatedStyle[varName] = value;
this.setState({style: updatedStyle});
}
これで Adapter を実装しました。この時点では、実装がまだ完了していないため、コンソールにエラーが表示されることがあります。次のセクションでは、CSS クラスを追加および削除する方法について説明します。
5. Component メソッドを実装する
バリアントとクラスの管理
React にはクラスを管理する API がありません。ネイティブ JavaScript の CSS クラスの追加/削除メソッドを模倣するには、classList 状態変数を追加します。TopAppBar には、CSS クラスとやり取りする 3 つのコードがあります。
classNameプロパティを介して<TopAppBar />コンポーネントを渡します。addClassまたはremoveClassを介した Adapter メソッド。<TopAppBar />React コンポーネント内でハードコードされています。
まず、TopAppBar.js の先頭で、既存のインポートの下に次のインポートを追加します。
import classnames from 'classnames';
次に、TopAppBar コンポーネントのクラス宣言内に次のコードを追加します。
export default class TopAppBar extends React.Component {
constructor(props) {
super(props);
this.topAppBarElement = React.createRef();
}
state = {
classList: new Set(),
style: {},
};
get classes() {
const {classList} = this.state;
const {
alwaysCollapsed,
className,
short,
fixed,
prominent,
} = this.props;
return classnames('mdc-top-app-bar', Array.from(classList), className, {
'mdc-top-app-bar--fixed': fixed,
'mdc-top-app-bar--short': short,
'mdc-top-app-bar--short-collapsed': alwaysCollapsed,
'mdc-top-app-bar--prominent': prominent,
});
}
...
}
http://localhost:8080 にアクセスすると、[Controls] チェックボックスで DOM のクラス名をオン/オフできるようになっています。
このコードにより、多くのデベロッパーが TopAppBar を使用できるようになります。デベロッパーは、CSS クラスの実装の詳細を気にすることなく、TopAppBar API を操作できます。
これで、アダプタの実装が完了しました。次のセクションでは、Foundation をインスタンス化する手順について説明します。
コンポーネントのマウントとアンマウント
Foundation のインスタンス化は componentDidMount メソッドで行われます。
まず、TopAppBar.js の既存のインポートの後に次のインポートを追加して、MDC トップアプリバーの基盤をインポートします。
import {MDCTopAppBarFoundation, MDCFixedTopAppBarFoundation, MDCShortTopAppBarFoundation} from '@material/top-app-bar';
次に、次の JavaScript コードを TopAppBar クラスに追加します。
export default class TopAppBar extends React.Component {
...
foundation_ = null;
componentDidMount() {
this.initializeFoundation();
}
componentWillUnmount() {
this.foundation_.destroy();
}
initializeFoundation = () => {
if (this.props.short) {
this.foundation_ = new MDCShortTopAppBarFoundation(this.adapter);
} else if (this.props.fixed) {
this.foundation_ = new MDCFixedTopAppBarFoundation(this.adapter);
} else {
this.foundation_ = new MDCTopAppBarFoundation(this.adapter);
}
this.foundation_.init();
}
...
}
React の適切なコーディング方法の 1 つは、propTypes と defaultProps を定義することです。TopAppBar.js の既存のインポートの後に、次のインポートを追加します。
import PropTypes from 'prop-types';
次に、TopAppBar.js の一番下(Component クラスの後)に次のコードを追加します。
import PropTypes from 'prop-types';
TopAppBar.propTypes = {
alwaysCollapsed: PropTypes.bool,
short: PropTypes.bool,
fixed: PropTypes.bool,
prominent: PropTypes.bool,
title: PropTypes.string,
actionItems: PropTypes.arrayOf(PropTypes.element),
navIcon: PropTypes.element,
};
TopAppBar.defaultProps = {
alwaysCollapsed: false,
short: false,
fixed: false,
prominent: false,
title: '',
actionItems: null,
navIcon: null,
};
これで、Top App Bar React コンポーネントの実装が完了しました。http://localhost:8080 に移動すると、デモページを操作できます。デモページは MDC Web のデモページと同じように動作します。デモページは次のようになります。

6. まとめ
このチュートリアルでは、React アプリケーションで使用するために MDC Web の Foundation をラップする方法について説明しました。フレームワークへの統合で説明されているように、Github と npm には MDC Web コンポーネントをラップするライブラリがいくつかあります。こちらにあるリストを使用することをおすすめします。このリストには、React 以外のフレームワーク(Angular や Vue など)も含まれています。
このチュートリアルでは、MDC Web コードを 3 つの部分(Foundation、Adapter、Component)に分割するという決定について説明します。このアーキテクチャにより、コンポーネントはすべてのフレームワークで動作しながら、共通のコードを共有できます。Material Components React をお試しいただきありがとうございます。新しいライブラリ MDC React をぜひお試しください。この Codelab がお役に立ちましたら幸いです。