D3 v6 アロー関数使用時の移行ガイド

[D3 v6](https://github.com/d3/d3/releases/tag/v6.0.0) が2020年8月26日にリリースされました。D3 v6 では `selection.on` によるイベント処理の方法が変わりました。この記事では**公式の移行ガイドに載っていないアロー関数使用時**に限った移行の方法を記していきます。

移行ガイドというタイトルですが、実質的には以前書いた[D3.jsでアロー関数を使う時の注意点](https://qiita.com/cieloazul310/items/b42465a538a9419471e9) の v6 版です。

[デモページ](https://observablehq.com/@cieloazul310/d3-6-0-arrow-function-event)

[D3 v6 リリースノート](https://github.com/d3/d3/releases/tag/v6.0.0)

> - Adopt ES2015, including support for iterables and collections (Map and Set).
> - Remove d3.event; selection.on now passes events directly to listeners.
> - Remove d3.mouse, d3.touch, d3.touches, d3.clientPoint; add d3.pointer and d3.pointers.
> - Remove d3.nest; add d3.group and d3.rollup.
> - Remove d3-collection; see d3-array.
> - Remove d3-voronoi; add d3-delaunay.
> - Remove support for Bower.

D3 v6 の変更内容は大まかに言うと使う場面が少ない機能の削除という印象です。したがって、ほとんどの人はこれから記すイベント処理の部分だけを書き換えればすんなりと移行できるはずです。詳しくは[公式の移行ガイド](https://observablehq.com/d/f91cccf0cad5e9cb) を参照してください。

## v6 イベント処理の変更

D3 では `selection.on` で DOM にイベントを実装できます。データをバインドした D3 版 `addEventListener` です。これは、マウスオーバーで色を変更、クリックで選択、などインタラクティブな動作を実装したい場面で使います。

### 今までの書き方 (無名関数式版)

```js
selection.on('click', function(d, i, nodes) {
  d3.select(this)
    .style('fill', 'red');
});
```

`selection.on` の第一引数は[イベントのタイプ](https://developer.mozilla.org/en-US/docs/Web/Events#Standard_events)、第二引数にリスナを取ります。ここまでは v6 も同様です。

第二引数のリスナは、v5 以前は `selection.attr`、`selection.style` と同様に、datum (`d`), index (`i`), `nodes` を引数にとっていました。

### v6 の書き方 (無名関数式版)

```js
selection.on('click', function(event, d) {
  d3.select(this)
    .style('fill', 'red');
});
```

リスナの引数が イベントオブジェクト `event` と datum (`d`) に変わりました。この変更により `selection.attr`, `selection.style` との統一感はなくなりましたが、代わりに イベントオブジェクトを利用できるようになりました。 `d` が第一引数から第二引数に変わったので、v5 以前のコードでリスナ内でデータを利用している場合はここを書き換えなければなりません。

さて、D3 の公式ガイドでは、リスナには *function* で始まる無名関数式を使い DOM の選択には `this` を使用しています。自分としては、無名関数には極力アロー関数式を使いたい、`this` は使いたくないと思っているため、以前 [D3.jsでアロー関数を使う時の注意点](https://qiita.com/cieloazul310/items/b42465a538a9419471e9) という記事を書きました。v6 の移行ガイドには、アロー関数を使った方法は載っていないので、アロー関数を使う場合の移行方法をメモしておきます。

## アロー関数式を使う移行方法

### 今までの書き方 (アロー関数版)

```js
selection.on('click', (d, i, nodes) => {
  d3.select(nodes[i])
    .style('fill', 'red');
});
```

無名関数式とアロー関数式では `this` の内容が異なります。したがって、アロー関数を使うと D3 公式が紹介している `this` を使ったコードは動きません。無名関数式で `this` で書いていた場所は v5 以前のコードでは `nodes[i]` で書くことができました。

では、`i` と `nodes` が消えた v6 のリスナではどのように書き換えればよいでしょうか。

### v6 の書き方 (アロー関数版)

```js
selection.on('click', (event, d) => {
  d3.select(event.currentTarget)
    .style('fill', 'red');
  // const nodes = selection.nodes();
  // const i = nodes.indexOf(event.currentTarget);
});
```

`event.currentTarget` で `this` と同じように自身の DOM を選択することができます。  
v5 以前のリスナで引数に取られていた `nodes` は `selection.nodes()`、 `i` は `nodes.indexOf(event.currentTarget)` で取得できます。

## 参考

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Thanks. No immediate plans there, but you can use event.currentTarget for event listeners.</p>&mdash; Mike Bostock (@mbostock) <a href="https://twitter.com/mbostock/status/1299092990875500544?ref_src=twsrc%5Etfw">August 27, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

- [D3 6.0 Release Note](https://github.com/d3/d3/releases/tag/v6.0.0)  
- [D3 6.0 migration guide](https://observablehq.com/d/f91cccf0cad5e9cb)