React でカスタム ウィジェットを作成

2479
0
11-12-2020 04:44 PM

React でカスタム ウィジェットを作成

はじめに

ArcGIS API for JavaScript は React, Angular, Vue などの JavaScript のフレームワークと統合して利用することができ、その解説はAPI リファレンスの Using the ArcGIS API for JavaScript with Frameworks に記載されています。

前回は、そのフレームワークのひとつでもある Vue を使用した「Vue でカスタム ウィジェットを作成」の記事で、サンプルの解説を紹介しました。

今回は、React を使用した Using widgets with React のサンプルの解説を翻訳して紹介します。Web アプリの開発環境をお持ちでない場合でも、Using widgets with React の sandbox を利用して、該当箇所のコードを少し書換えすることでWeb ブラウザー上で動作を試すことが可能です。

 

サンプルの解説

このサンプルでは、React ViewModels を使ってカスタム ウィジェットを作成する方法を紹介します。具体的には、ZoomViewModel を使用してカスタムの Zoom ボタンを作成する方法を示しています。

1. ReactをAMDモジュールとして設定

React と ReactDOM を AMD モジュールとして dojoConfig に追加します。

 

window.dojoConfig = {
  async: true,
  packages: [
    {
      name: "react",
      location: "https://unpkg.com/react@16/umd/",
      main: "react.production.min"
    },
    {
      name: "react-dom",
      location: "https://unpkg.com/react-dom@16/umd/",
      main: "react-dom.production.min"
    }
  ]

 

これで AMD モジュールとして使えるようになります。

2. シンプルなマップとビューを作成

簡単な Map を作成し、MapView もしくは SceneView に追加します。もし、ビューや基本的なマップの作成方法に慣れていない場合は、次のリソースを参照してください。

 

var map = new Map({
  basemap: "topo-vector"
});

var view = new MapView({
  container: "viewDiv",
  map: map,
  center: [-100.33, 25.69],
  zoom: 10,
  ui: {
    components: ["attribution"] // 帰属 (attribution) 以外のUIを空にします。 
  }
});

 

3. React コンポーネントを作成

初期状態 (state) と定義済みのプロパティを使用して React コンポーネントを作成します。これについての詳細は React のドキュメントを参照してください。次に、React コンポーネントのアクションを ZoomViewModel のメソッドにバインドします。React コンポーネントのスタイルを View のプロパティにバインドして、現在の最小/最大ズームレベルを決定することも可能です。

 

class Zoom extends React.Component {
  state = {
    vm: new ZoomViewModel(),
    maxZoomed: false,
    minZoomed: false
  };

  componentDidMount() {
    this.props.view.when(this.onViewLoaded);
  }

  onViewLoaded = (view) => {
    this.state.vm.view = view;
    watchUtils.init(view, "zoom", this.onZoomChange);
  };

  onZoomChange = (value) => {
    this.setState({
      maxZoomed: value === view.constraints.maxZoom,
      minZoomed: value === view.constraints.minZoom
    });
  };

  zoomIn = () => {
    if (!this.state.maxZoomed) {
      this.state.vm.zoomIn();
    }
  };

  zoomOut = () => {
    if (!this.state.minZoomed) {
      this.state.vm.zoomOut();
    }
  };

  render() {
    const maxstate = this.state.maxZoomed ? "button circle raised disable" : "button circle raised";
    const minstate = this.state.minZoomed ? "button circle raised disable" : "button circle raised";
    return (
      <div className="zoom-btns">
        <div className={maxstate} onClick={this.zoomIn}>
          <div className="center">
            <i className="material-icons">add</i>
          </div>
        </div>
        <div className={minstate} onClick={this.zoomOut}>
          <div className="center">
            <i className="material-icons">remove</i>
          </div>
        </div>
      </div>
    );
  }
};

 

4. React コンポーネントのレンダリング

次に、React コンポーネント用の DOM 要素を作成し、view UI レイアウトに追加します。完了したら、React コンポーネントをレンダリングします。

完成版のサンプルコード

 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1, maximum-scale=1,user-scalable=no"
    />
    <title>
      Using widgets with React | Sample | ArcGIS API for JavaScript 4.17
    </title>
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
    />
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
      .zoom-btns {
        float: left;
        transition: opacity 0.25s ease-in-out;
        -moz-transition: opacity 0.25s ease-in-out;
        -webkit-transition: opacity 0.25s ease-in-out;
      }
      .button {
        display: inline-block;
        position: relative;
        margin: 5px;
        width: 50px;
        height: 50px;
        line-height: 50px;
        font-size: 2em;
        background-color: #fff;
        color: #646464;
        cursor: pointer;
        text-align: center;
        vertical-align: middle;
      }
      .button.circle {
        border-radius: 50%;
      }
      .button:hover {
        opacity: 0.8;
        color: rgba(0, 0, 0, 0.25);
      }
      .button.raised {
        transition: box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
        transition-delay: 0.2s;
        box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.25);
      }
      .button.raised:active {
        box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2);
        transition-delay: 0s;
      }
      .disable {
        opacity: 0.8;
        color: rgba(0, 0, 0, 0.25);
      }
      .disable:hover {
        opacity: 0.8;
        color: rgba(0, 0, 0, 0.25);
      }
      .material-icons {
        font-size: 1.2em;
        line-height: 50px;
      }
    </style>
    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.17/esri/themes/light/main.css"
    />
    <script>
      window.dojoConfig = {
        async: true,
        packages: [
          {
            name: "react",
            location: "https://unpkg.com/react@16/umd/",
            main: "react.production.min"
          },
          {
            name: "react-dom",
            location: "https://unpkg.com/react-dom@16/umd/",
            main: "react-dom.production.min"
          }
        ]
      };
    </script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script src="https://js.arcgis.com/4.17/"></script>
    <script type="text/babel">
      require([
        "react",
        "react-dom",
        "esri/Map",
        "esri/views/MapView",
        "esri/core/watchUtils",
        "esri/widgets/Zoom/ZoomViewModel"
      ], function (React, ReactDOM, Map, MapView, watchUtils, ZoomViewModel) {
        const map = new Map({
          basemap: "topo-vector"
        });

        const view = new MapView({
          container: "viewDiv",
          map: map,
          center: [-100.33, 25.69],
          zoom: 10,
          ui: {
            components: ["attribution"] // empty the UI, except for attribution
          }
        });

        class Zoom extends React.Component {
          state = {
            vm: new ZoomViewModel(),
            maxZoomed: false,
            minZoomed: false
          };

          componentDidMount() {
            this.props.view.when(this.onViewLoaded);
          }

          onViewLoaded = (view) => {
            this.state.vm.view = view;
            watchUtils.init(view, "zoom", this.onZoomChange);
          };

          onZoomChange = (value) => {
            this.setState({
              maxZoomed: value === view.constraints.maxZoom,
              minZoomed: value === view.constraints.minZoom
            });
          };

          zoomIn = () => {
            if (!this.state.maxZoomed) {
              this.state.vm.zoomIn();
            }
          };

          zoomOut = () => {
            if (!this.state.minZoomed) {
              this.state.vm.zoomOut();
            }
          };

          render() {
            const maxstate = this.state.maxZoomed
              ? "button circle raised disable"
              : "button circle raised";
            const minstate = this.state.minZoomed
              ? "button circle raised disable"
              : "button circle raised";
            return (
              <div className="zoom-btns">
                <div className={maxstate} onClick={this.zoomIn}>
                  <div className="center">
                    <i className="material-icons">add</i>
                  </div>
                </div>
                <div className={minstate} onClick={this.zoomOut}>
                  <div className="center">
                    <i className="material-icons">remove</i>
                  </div>
                </div>
              </div>
            );
          }
        }
        const node = document.createElement("div");
        view.ui.add(node, "bottom-left");
        ReactDOM.render(<Zoom view={view} />, node);
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
  </body>
</html>

 

 

まとめ

今回は、React を使用した Using widgets with React のサンプルを翻訳して紹介しました。
ArcGIS API for JavaScript で React を使用したガイドは、Using the ArcGIS API for JavaScript with React でも紹介しています。また、EsriのDeveloper Summit(動画英語:ArcGIS API for Javascript :Using React and Webpack)でも紹介していますので、興味のある方は参照していただければと思います。
その他 React は、ArcGIS Experience BuilderDeveloper Edition (開発者向けエディション) で使用しています。Developer Edition (開発者向けエディション)でカスタム ウィジェットを作成する際に React を使用しています。
ArcGIS Experience Builder の Developer Edition (開発者向けエディション) の国内サポートは、11月末を予定しています。
これを機会に React を使用した開発をお試しください。

 

関連リンク

Tags (1)
Version history
Last update:
‎11-12-2020 04:50 PM
Updated by:
Contributors