ハトネコエ Web がくしゅうちょう

プログラミングやサーバー関連、チームマネジメントなど得た技術のまとめ

materialize-css のJSライブラリを TypeScript + React 環境で読み込む

Materialize 、モダンっぽい Web サイトを作れるので、
個人的には Bootstrap よりは最近使うようになりつつあります。

「Bootstrap より軽いし……」と書こうとして実際にいま測ってみたらそうでもなかったですが(笑)

1. Bootstrap vs. Materialize (ファイルサイズ比較)

1-1. Bootstrap

  • bootstrap.min.js (v4.1.3) : 20.56KB
  • bootstrap.min.js (v4.1.3) : 13.76KB
  • jquery-3.3.1.slim.min.js : 23.47KB
  • popper.min.js (v1.14.3) : 6.8KB

Total: 64.59KB

1-2. Materialize

Total: 59.53KB

Materialize の JavaScriptjQuery を内包しているので、そのぶん大きくなっていますね。
読み込むファイル数は少ないので、そのぶんコストは低いですが、思ったより容量に差はなかったです。

閑話休題。本題にいきましょう。

2. Materialize の JavaScript ライブラリを TypeScript で使いたい

例えば materialize-cssNavbar Component の Dropdown を使いたいときには、
Dropdown のドキュメントにある通り、

// ドキュメント通りに dropdown-trigger class を設定し、以下の script を末尾などに記述
document.addEventListener('DOMContentLoaded', function() {
  var elems = document.querySelectorAll('.dropdown-trigger');
  M.Dropdown.init(elems, {});
});

// jQuery を呼び出している場合は以下の 1 行で済む
$('.dropdown-trigger').dropdown();

上のどちらかの方法でドロップダウンをアクティブにしなくてはいけません。

HTML の最後に script タグを使って、

<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    var elems = document.querySelectorAll('.dropdown-trigger');
    M.Dropdown.init(elems, options);
  });
</script>

こんな感じで書いてあげてもいいわけですが、
せっかくなら ts ファイルや tsx ファイル内で呼び出してあげたい。その方法です。

3. 答え

3-1. npm install -D @types/materialize-css

前置きが長くなりましたが、以下のようにライブラリをダウンロードすればOKです。

npm i -S materialize-css
npm i -D @types/materialize-css

materialize-css だけでなく、
@types/materialize-css を入れているのが一番のポイントです。
これがないと TypeScript でのコンパイルに失敗してしまいます。

なお、古い記事だと npm i -S materialize-css@next で入れている記事がありますが、
2019年5月現在では、 npm i -S materialize-css で入るのは 1.0.0 、
npm i -S materialize-css@next で入るのは 1.0.0-rc.2 と、
next を付けるほうがむしろ古いバージョンが入ってしまうのでお気をつけください。

f:id:nekonenene:20190506005825p:plain
https://www.npmjs.com/package/materialize-css?activeTab=versions より

3-2. import M from 'materialize-css'

あとはいつも通りに、 tsx ファイルや ts ファイルの先頭で import してあげれば OK です。

import M from 'materialize-css';

2. の項で書いた例のように、

var elems = document.querySelectorAll('.dropdown-trigger');
M.Dropdown.init(elems, {});

を適当な場所で書くことにより Dropdown の初期化をおこなうこともできますが、
ドキュメントの例にあるようなクラス名(dropdown-trigger)をそのまま使う場合でしたら、

M.AutoInit();

でカンタンに済ませることも可能です。(参考: https://materializecss.com/auto-init.html

どのコンポーネント、そして class 名だと初期化されるかは元のコードを読むといいでしょう。

https://github.com/Dogfalo/materialize/blob/efa0aee/js/global.js#L106-L135

3-3. React で扱う場合の Tips

M.AutoInit();componentDidMount 内で呼び出すと良いでしょう。
(※ constructor 内で呼び出すと失敗します。constructor は HTML render がおこなわれる前に走るためです)

public componentDidMount(): void {
  M.AutoInit();
}

M.AutoInit(); は便利ですが、すべてのコンポーネントに関する init がおこなわれるわけではありません。
例えば M.updateTextFields(); もその一つです。(参考: 『Text Inputs』 の『Prefilling Text Inputs』の項)
これは、Text Input Form にすでに文字が入力されている場合には、ラベルを上に移動しておいてくれるメソッドです。

https://github.com/Dogfalo/materialize/blob/efa0aee/js/forms.js#L3-L21

使用しないとこうなる:

f:id:nekonenene:20190506021047p:plain

使用するとページ表示時にこうなってくれる:

f:id:nekonenene:20190506021123p:plain

これは M.AutoInit(); から呼び出されないことがコードからわかりますので
入力フォームのあるページでは M.updateTextFields(); を使ってあげるといいです。

私の書いているコードでは、input フォームの defaultValue を非同期通信で取ってくるために
render のされ直しが発生してしまうため、
componentDidMount 内ではなく componentDidUpdate 内で呼び出してあげています。

public componentDidUpdate(): void {
  M.updateTextFields();
}

4. おしまい

React を今まで避けて生きてきたんですが、
かんたんな Go (Gin) + React のアプリケーションを作ってみようとさわってみたら
ちょっとずつわかってきました。

Redux には触れていないですが、今のところバケツリレーも大規模になっていないので、
今回は使わないでもいけそうです。

ついでに TypeScript も導入し、数年ぶりにさわっていますが、
慣れていない Go + 慣れていない React + 慣れていない TypeScript の組み合わせでちょこちょこ苦しんでいます(笑)
型チェックのために書く量は増えるし、参考資料そのままで書けない場合もあり大変です。

とても勉強になっているので、もう少しがんばってみます!