1. はじめに
マテリアル コンポーネント(MDC)は、デベロッパーがマテリアル デザインを実装する際に役立ちます。Google のエンジニアと UX デザイナーのチームが作成した MDC には、美しく機能的な UI コンポーネントが多数含まれており、Android、iOS、ウェブ、Flutter.material.io/develop に利用可能です。 |
MDC Web は、マテリアル デザインの原則を維持しながら、任意のフロントエンド フレームワークに統合できるように設計されています。次の Codelab では、MDC Web を基盤として使用する React コンポーネントの作成手順について説明します。この Codelab で学んだ原則は、あらゆる JavaScript フレームワークに適用できます。
MDC ウェブの構築
MDC ウェブの JavaScript レイヤは、コンポーネントごとに Component、Foundation、Adapter の 3 つのクラスで構成されます。このパターンにより、MDC Web をフロントエンド フレームワークと柔軟に統合できます。
基盤には、マテリアル デザインを実装するビジネス ロジックが含まれています。基盤は HTML 要素を参照しません。これにより、HTML インタラクション ロジックをアダプタに抽象化できます。Foundation にはアダプタがあります。
Adapter はインターフェースです。アダプタ インターフェースは、マテリアル デザインのビジネス ロジックを実装するために Foundation によって参照されます。アダプターは、Angular や React などのさまざまなフレームワークで実装できます。アダプタの実装は DOM 構造を操作します。
コンポーネントには基盤があり、その役割は
- フレームワーク以外の JavaScript を使用して Adapter を実装します。
- Foundation のメソッドにプロキシする公開メソッドを指定します。
MDC Web が提供する機能
MDC Web のすべてのパッケージには、コンポーネント、基盤、アダプターが付属しています。コンポーネントをインスタンス化するには、ルート 要素をコンポーネントのコンストラクタ メソッドに渡す必要があります。コンポーネントは、DOM 要素と HTML 要素を操作するアダプターを実装します。次に、Component が Foundation をインスタンス化し、Adapter メソッドを呼び出します。
MDC Web をフレームワークに統合するには、そのフレームワークの言語/構文で独自のコンポーネントを作成する必要があります。フレームワークのコンポーネントは MDC Web のアダプターを実装し、MDC Web の基盤を使用します。
作成するアプリの概要
この Codelab では、カスタム アダプターを構築して Foundation ロジックを使用してマテリアル デザインの React コンポーネントを実現する方法について説明します。ここでは、フレームワークへの統合で説明されている上級者向けトピックについて説明します。この 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 のテキストの壁、[コントロール] ボックス(右下)、未完成のトップアプリバーが表示されます。
コードとプロジェクトを確認します。
コードエディタを開くと、プロジェクト ディレクトリは次のようになります。
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 つの section 要素があります。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 コンポーネントの部分は次のとおりです。
- 初期化された基盤
- 基盤に渡すアダプタ メソッド
- JSX マークアップ
- パターン管理(固定、短い、常に閉じている、目立つ)
アプローチ
- Adapter メソッドを実装します。
componentDidMount
で Foundation を初期化します。componentWillUnmount
で Foundation.destroy メソッドを呼び出します。- 適切なクラス名を組み合わせるゲッター メソッドを介して、バリアント管理を確立します。
4. アダプタ メソッドを実装する
フレームワーク以外の JS TopAppBar
コンポーネントは、次のアダプタメソッドを実装します(詳細はこちらをご覧ください)。
hasClass()
addClass()
removeClass()
registerNavigationIconInteractionHandler()
deregisterNavigationIconInteractionHandler()
notifyNavigationIconClicked()
setStyle()
getTopAppBarHeight()
registerScrollHandler()
deregisterScrollHandler()
registerResizeHandler()
deregisterResizeHandler()
getViewportScrollY()
getTotalActionItems()
React には合成イベントと異なるコーディングのベスト プラクティスとパターンがあるため、アダプター メソッドを再実装する必要があります。
アダプタのゲッター メソッド
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,
};
}
スクロールとサイズ変更のイベント登録用のアダプター API は、フレームワーク以外の JS バージョンと同じように実装されています。これは、React にはスクロールやサイズ変更用の合成イベントがなく、ネイティブ DOM イベント システムに委任するためです。また、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});
}
これで、アダプタを実装できました。なお、この時点では実装が完了していないため、コンソールにエラーが表示されることがあります。次のセクションでは、CSS クラスの追加と削除の方法について説明します。
5. コンポーネント メソッドを実装する
バリエーションとクラスの管理
React には、クラスを管理する API がありません。ネイティブ JavaScript の CSS クラスの追加/削除メソッドを模倣するには、classList
状態変数を追加します。TopAppBar
には、CSS クラスを操作するコードが 3 つあります。
<TopAppBar />
コンポーネントにclassName
プロパティを渡します。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 を操作できます。
これで、アダプタが正常に実装されました。次のセクションでは、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,
};
これで、トップ アプリバーの React コンポーネントの実装が完了しました。http://localhost:8080 に移動すると、デモページを操作できます。このデモページは、MDC ウェブのデモページと同じように機能します。デモページは次のようになります。
6. まとめ
このチュートリアルでは、MDC Web の Foundation をラップして React アプリケーションで使用できるようにする方法について説明しました。フレームワークへの統合で説明されているように、GitHub と npm には MDC Web Components をラップするライブラリがいくつかあります。こちらのリストを使用することをおすすめします。このリストには、React 以外のフレームワーク(Angular、Vue など)も含まれています。
このチュートリアルでは、MDC ウェブコードを 3 つの部分(基盤、アダプター、コンポーネント)に分割するという Google の決定について説明します。このアーキテクチャにより、コンポーネントはすべてのフレームワークで動作しながら共通のコードを共有できます。Material Components React をお試しいただきありがとうございます。新しいライブラリ MDC React もぜひご確認ください。この Codelab がお役に立ちましたら幸いです。