はじめに
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 Builder の Developer Edition (開発者向けエディション) で使用しています。Developer Edition (開発者向けエディション)でカスタム ウィジェットを作成する際に React を使用しています。
ArcGIS Experience Builder の Developer Edition (開発者向けエディション) の国内サポートは、11月末を予定しています。
これを機会に React を使用した開発をお試しください。
関連リンク