1. はじめに
マテリアル コンポーネント(MDC)は、デベロッパーがマテリアル デザインを実装する際に役立ちます。Google のエンジニアと UX デザイナーのチームが作成した MDC には、美しく機能的な UI コンポーネントが多数含まれており、Android、iOS、ウェブ、Flutter.material.io/develop に利用可能です。 |
MDC ウェブは、マテリアル デザインの原則を守りつつ、あらゆるフロントエンド フレームワークに統合できるように設計されています。次の Codelab では、MDC Web を基盤として使用する React コンポーネントの作成方法について説明します。この Codelab で学んだ原則は、あらゆる JavaScript フレームワークに適用できます。
MDC ウェブの構築
MDC ウェブの JavaScript レイヤは、コンポーネントごとに Component、Foundation、Adapter の 3 つのクラスで構成されます。このパターンにより、MDC Web はフロントエンド フレームワークと柔軟に統合できます。
Foundation にはマテリアル デザインを実装するビジネス ロジックが含まれています。本財団は、HTML 要素を参照しません。これにより、HTML インタラクション ロジックを Adapter に抽象化できます。Foundation には Adapter があります。
Adapter はインターフェースです。Adapter インターフェースは、マテリアル デザインのビジネス ロジックを実装するために Foundation から参照されます。Adapter は、Angular や React などのさまざまなフレームワークで実装できます。Adapter の実装は DOM 構造と相互作用します。
コンポーネントには基盤があり、その役割は
- フレームワーク以外の JavaScript を使用して Adapter を実装します。
- 基盤のメソッドにプロキシするパブリック メソッドを提供します。
MDC Web が提供する機能
MDC Web の各パッケージには、コンポーネント、基盤、アダプターが付属しています。Component をインスタンス化するには、ルート element をコンポーネントのコンストラクタ メソッドに渡す必要があります。Component は DOM 要素や HTML 要素とやり取りする Adapter を実装しています。次に、Component が Foundation をインスタンス化し、Adapter メソッドを呼び出す。
MDC Web をフレームワークに統合するには、フレームワークの言語と構文で独自のコンポーネントを作成する必要があります。フレームワーク Component は MDC Web の Adapter を実装し、MDC Web の Foundation を使用します。
作成するアプリの概要
この Codelab では、Foundation ロジックを使用してマテリアル デザインの React コンポーネントを実現するカスタム Adapter の作成方法を示します。ここでは、フレームワークへの統合にある高度なトピックを扱います。この Codelab ではフレームワークの例として React を使用していますが、このアプローチは他のフレームワークにも適用できます。
この Codelab では、トップ アプリバーを作成し、トップ アプリバーのデモページを再作成します。デモページのレイアウトはすでに設定されているため、トップアプリバーの作業を開始できます。トップ アプリバーには次のものが含まれます。
- ナビゲーション アイコン
- アクション アイテム
- ショート動画には、ショート動画、常に折りたたまれた、固定、目立つという 4 つのパターンがあります。
必要なもの:
- Node.js の最新バージョン(JavaScript パッケージ マネージャーである npm にバンドルされています)
- サンプルコード(次のステップでダウンロード)
- HTML、CSS、JavaScript、React に関する基本的な知識
ウェブ開発の経験についてお答えください。
<ph type="x-smartling-placeholder">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/ にアクセスしてページを表示します。

完了しました。トップ アプリバーの React デモページのスターター コードがブラウザで実行されているはずです。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 メソッドでファイル TopAppBar.js を開きます。これはベアの React Component クラスです。
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 要素に渡します。TopAppBar を初期化するサンプルコードを App.js に示します。
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 Component がトップ アプリバーにまだ表示されていない部分は次のとおりです。
- 初期化された基盤
- 基盤に渡すアダプター メソッド
- JSX マークアップ
- パターン管理(固定、短い、常に閉じている、目立つ)
手法
- Adapter メソッドを実装します。
componentDidMountで Foundation を初期化します。componentWillUnmountで Foundation.destroy メソッドを呼び出します。- 適切なクラス名を組み合わせたゲッター メソッドでバリアント管理を確立します。
4. Adapter メソッドを実装する
フレームワーク以外の JS TopAppBar コンポーネントは、次の Adapter メソッドを実装します(詳細についてはこちらをご覧ください)。
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 は window オブジェクトの関数ですが、React の API には存在しません。アダプタの実装はフレームワークごとに異なります。
get adapter メソッドによって呼び出される this.setStyle がないことがわかります。TopAppBar.js ファイルで、不足している JavaScript メソッドを TopAppBar クラスに追加します。
setStyle = (varName, value) => {
const updatedStyle = Object.assign({}, this.state.style);
updatedStyle[varName] = value;
this.setState({style: updatedStyle});
}
Adapter が実装されました。完全な実装はまだ完了していないため、この時点でコンソールにエラーが表示される場合があります。次のセクションでは、CSS クラスを追加、削除する方法を説明します。
5. コンポーネント メソッドを実装する
バリアントとクラスの管理
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 にアクセスすると、[コントロール] チェックボックスで DOM のクラス名のオン/オフが切り替わるはずです。
このコードにより、多くのデベロッパーが TopAppBar を使用できるようになります。デベロッパーは、CSS クラスの実装の詳細を気にせずに、TopAppBar API を操作できます。
これで、Adapter が正常に実装されました。次のセクションでは、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 のコーディング プラクティスとしては、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,
};
これで、トップ アプリバー React コンポーネントの実装が完了しました。http://localhost:8080 に移動したら、デモページで操作できます。デモページは、MDC ウェブのデモページと同じように機能します。デモページは次のようになります。

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