React で WebComponents を使う



Wrote: 2019/2/6

<img src="https://qiita-image-store.s3.amazonaws.com/0/49910/d7ae10c8-f33b-643a-6478-5ba72697aac9.gif" height="350px" />



# 概要

create-react-appで作ったプロジェクトにて、WebComponentsを使う手順を説明します。


## create-react-app

```shell
$ npx create-react-app webcomponents
$ cd webcomponents/
$ npm start
```

webcomponentsjsとvendor-copyのインストール

```shell
npm install --save @webcomponents/webcomponentsjs vendor-copy
```


## vendor-copy

### scriptsブロック

package.json のscriptsブロックに以下を挿入

` "postinstall": "vendor-copy"`


```js:package.json
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "postinstall": "vendor-copy"
  },
```


### add vendorCopy path

```js:package.json
  "vendorCopy": [
    {
      "from": "node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js",
      "to": "public/vendor/custom-elements-es5-adapter.js"
    },
    {
      "from": "node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js",
      "to": "public/vendor/webcomponents-bundle.js"
    }
  ]
```

npm installの度に、vendorCopyで指定したファイルが`public`にコピーされます。



### public/index.html

public/index.htmlにwebcomponents-bundle参照の追加を行う

```html:index.html
<script src="%PUBLIC_URL%/vendor/webcomponents-bundle.js"></script>
<script>if (!window.customElements) { document.write("<!--"); }</script>
<script src="%PUBLIC_URL%/vendor/custom-elements-es5-adapter.js"></script>
<!--! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKER -->
```

※ `<!--! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKER -->`は必要なので消さないように注意。

customElementsをサポートしていないブラウザでは`custom-elements-es5-adapter.js`をロードしないようにする。


※%PUBLIC_URL%に任意のURLを指定したい場合には、以下のように`homepage`にURLを設定する

```js
{
  // ...
  "scripts": {
    // ...
  },
  "homepage": "https://example.com"
}
```



## テスト

`npm install` or `npm i` or `yarn install`を実行して、public/配下にwebcomponents がコピーされるかテストを行う

↓のようにファイルがコピーされてればOK


![image](https://qiita-image-store.s3.amazonaws.com/0/49910/1e70ed67-cc2d-83a3-2084-aaa8773fd3a7.png)

`yarn start` でブラウザで起動、Networkのデバッグで２つのファイルがロードされてればOK

![image](https://qiita-image-store.s3.amazonaws.com/0/49910/e7496177-f88d-ae57-0f0e-46c83fe106b8.png)


## web componentの追加

今回は、[Vaadinの component set](https://vaadin.com/components)を使ってみます。

* vaadin-date-picker

```shell
yarn add @vaadin/vaadin-date-picker
```


### src/App.js

importを追加
```js
import "@vaadin/vaadin-date-picker";
```

renderを以下のように変更

```js
  render() {
    return (
      <div className="App">
        <vaadin-date-picker label="When were you born?"></vaadin-date-picker>
      </div>
    );
  }
```

## 実行

以下のようにカレンダーコンポーネントが表示されれば成功です。


<img src="https://qiita-image-store.s3.amazonaws.com/0/49910/eae3bf95-fe28-ecf5-229f-fb0d510caa9c.gif" height="350px" />



### イベントハンドリング

フォームを作成し、テキストBOXを２つ配置
ボタンを押してリストに追加するデモを作成します。

以下のコンポーネント使います。
* vaadin-text-field
* vaadin-button
* vaadin-grid


componentのインストール

```shell
yanr add @vaadin/vaadin-text-field @vaadin/vaadin-button @vaadin/vaadin-grid
```


importを追加
```js
import '@vaadin/vaadin-button';
import '@vaadin/vaadin-grid';
import '@vaadin/vaadin-text-field';
```

renderを以下のように変更

```js:App.js
render() {
  return (
    <div className="App">
        <div className="form">
          <vaadin-text-field label="First Name" ref="firstName" />
          <vaadin-text-field label="Last Name" ref="lastName" />
          <vaadin-button ref="addButton"> Add </vaadin-button>
        </div>
        <vaadin-grid ref="grid">
          <vaadin-grid-column path="firstName" header="First name" /> 
          <vaadin-grid-column path="lastName" header="Last name" />
        </vaadin-grid>
      </div>
  );
}
```



### componentDidMount()

```js:App.js
componentDidMount() {
  let people = []; 
  this.refs.addButton.addEventListener('click', e => { 
    people = [ 
      ...people,
      {
        firstName: this.refs.firstName.value,
        lastName: this.refs.lastName.value
      }
    ];
    this.refs.grid.items = people; 
    this.refs.firstName.value = ''; 
    this.refs.lastName.value = '';
  });
}
```


App.cssを以下のように変更

```css:App.css
.App {
  padding: 4px;
}
.App .form * {
  margin-right: 4px;
}
```


## 結果


![snow.gif](https://qiita-image-store.s3.amazonaws.com/0/49910/aec15b8f-f139-d778-3d3c-e883605a096b.gif)



# ソース

ソースは以下に置きました
[sugasaki/using-web-components-in-react](https://github.com/sugasaki/using-web-components-in-react)



# さらに深く

さらなる続きは以下へダイブ

[Custom Elements Everywhere](https://custom-elements-everywhere.com/)


# 参考

[How to use Web Components in React](https://vaadin.com/tutorials/using-web-components-in-react)

[Start using web components in React – Jens Jansson – Medium](https://medium.com/jens-jansson/start-using-web-components-in-react-6ccca2ca21f9)