React x TypeScript x Webpackの開発環境でjQuery (Bootstrap)を使うには


TypeScriptで記述しているReactアプリケーションをWebpackでビルドしている開発環境で、jQueryライブラリをあわせて使う構成について調べたことをまとめてみました。

ここでは、前回の記事 「TypeScript,WebPackを利用したReactJS開発環境セットアップまとめ」で構築したプロジェクトに、jQuery Bootstrapを追加してみます。

jQueryは、TypeScriptのコンパイルはimportで通りますが、そのままでは、jQueryがbundle.jsに含まれません。
jQueryをbundle.jsに含めるためには、ProvidePluginを利用することで解決できます。
また、jQuery本体については、CDNからjsを含めても依存の問題は発生しにくいので、scriptタグで指定してしまうのが良い気もします。

ReactでBootstrapを使いたい場合は、React-Bootstrapを使うほうが筋が良いと思いますが、ここではあえて本家のBoostrapを利用してみます。

パッケージのインストール

jqueryとbootstrap、その型定義をインストールします
また、bootstrapのPopoverはpopper.jsに依存しているのでpopper.jsも追加しておきます

$ npm install --save jquery @types/jquery bootstrap @types/bootstrap popper.js

ProvidePluginを設定

ProvidePluginを参考に、jQueryの設定をwebpack.config.jsに追加します。

var webpack = require('webpack');
module.exports = {
    // ...
    plugins: [
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery',
        Popper: ['popper.js', 'default']
      })
    ]
}

tsconfig.jsonの設定

このままwebpackを実行すると、TypeScriptのコンパイルで以下のエラーが発生します。

ERROR in [at-loader] ./node_modules/@types/jquery/index.d.ts:2957:63
    TS2304: Cannot find name 'Iterable'.

以下のようにlibオプションを追加します

// tsconfig.json
{
    "compilerOptions": {
        // ...
        "lib" : ["es2015", "es2015.iterable", "dom"]
    },
    // ...
}

参考: Cannot find name ‘Iterable’. · Issue #14595 · angular/angular

サンプル

Bootstrapのpopoverを試すコンポーネントを追加してみます。

import * as React from 'react';

export class Popover extends React.Component<{}, {}> {
  popoverbutton: HTMLElement;

  componentDidMount() {
    $(this.popoverbutton).popover();
  }

  render() {
    return (
      <div>
        <button ref={(button) => { this.popoverbutton = button; }} type="button"
        className="btn btn-secondary" data-container="body" data-toggle="popover"
        data-placement="right" data-content="Vivamus sagittis lacus vel augue laoreet rutrum faucibus.">
          Popover on right
        </button>
      </div>
    );
  }
};

data-xxxxの属性は、そのまま使えるので、表示のためだけのstateを管理しなくて良いのでこれはこれで便利です。

以下は、コンポーネントを利用するindex.tsxです。

import * as React from "react";
import * as ReactDOM from "react-dom";
import 'bootstrap';

import { Hello } from "./components/Hello";
import { Popover } from "./components/Popover";

ReactDOM.render(
    <div>
        <Hello compiler="TypeScript" framework="React" />
        <Popover />
    </div>

    ,
    document.getElementById("example")
);

bootstrapのみimportする必要があります。

How to use Bootstrap 4 and Sass (and jQuery) · AngularClass/angular-starter Wiki あたりを見ると、bootstrapのpopoverもwebpack.config.jsの設定でimportなしでも使えるようになりそうですが、要調査です。

ここまででは、SCSSを使ってないのでスタイルシートはCDNから読み込むようにしておきます。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    </head>
    <body>
        <div id="example"></div>

        <!-- Dependencies -->
        <script src="./node_modules/react/dist/react.js"></script>
        <script src="./node_modules/react-dom/dist/react-dom.js"></script>

        <!-- Main -->
        <script src="./dist/bundle.js"></script>
    </body>
</html>

ビルドして動作確認

$ ./node_modules/.bin/webpack
$ open index.html

以下のように、ボタンをクリックするとPopoverが表示されました