この Codelab について
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 に関する基本的な知識
ウェブ開発の経験についてお答えください。
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 がお役に立ちましたら幸いです。