ECharts v6 を React で使う
Apache ECharts v6 を React で動かす最小構成。echarts/core + use() の tree-shaking 登録、init / dispose のライフサイクル、setOption の merge / notMerge / replaceMerge の違い(実測)、コンテナ幅に追従しない resize の仕様、Node での SSR(SVG 文字列出力)まで。option JSON を書き換えて試せるプレイグラウンド demo 付き。
検証日: 2026-09-01
使用バージョン:
echarts@6.1.0(React 19・ラッパー無し)対象: ECharts を React に組み込みたい /
setOptionしたのに前の系列が残る・リサイズで崩れる現象に当たった場面
Apache ECharts v6 を ラッパーライブラリ無しで React に組み込む最小構成。option(グラフ定義オブジェクト)を直接書き換えて挙動を確かめられるプレイグラウンドと、setOption の merge 挙動・リサイズの仕様を実測で確認します。
触って試す
左の option JSON がそのまま ECharts への入力。preset を切り替える → data の数値を書き換える → series の type を "bar" から "line" に変える、のように試しながら読み進められる。notMerge チェックは後述の merge 挙動の実験用。
なぜ ECharts か(Chart.js / Recharts との位置取り)
- ECharts: option オブジェクト 1 個にグラフ定義を全部書く宣言スタイル。チャート種類・コンポーネント(dataZoom / visualMap 等)の品揃えが最も広く、フレームワーク非依存
- Chart.js: 同じ canvas + 設定スタイルだが、より軽量・シンプル寄り。定番チャートだけなら十分
- Recharts: JSX でグラフを合成する React 専用スタイル
「地図・ヒートマップ・ズーム連動のような込み入った可視化までいく可能性があるか」が ECharts を選ぶ分かれ目。
最小サンプル(echarts/core + use() 登録)
npm i echarts
| 項目 | 値 |
|---|---|
| 検証バージョン | echarts@6.1.0 |
| React ラッパー | 不要(本記事は素の echarts。echarts-for-react は最終更新が古く v6/React 19 での動作保証がない) |
| TypeScript 型 | 本体同梱 |
| 描画方式 | canvas / SVG 選択式 |
import * as echarts from "echarts" でも動くが、全チャート・全コンポーネントがバンドルに入る。echarts/core から使う部品だけ use() で登録するのが公式推奨の tree-shaking 構成:
import * as echarts from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent, LegendComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
echarts.use([BarChart, GridComponent, TooltipComponent, LegendComponent, CanvasRenderer]);
登録が足りない部品を option で使うと、グラフがエラーではなく無描画または部分欠けになる(tooltip が出ない、軸が出ない等)。Chart.js の register は明示的なエラーで落ちるのと対照的に、ECharts は静かに欠けるので気づきにくい。
React への組み込み: init と dispose のライフサイクル
ECharts は DOM 要素に自分でインスタンスを張り付ける設計なので、React では useRef + useEffect で管理する。次のコードが「マウントで init、アンマウントで dispose」の基本形:
function Chart({ option }: { option: echarts.EChartsCoreOption }) {
const divRef = useRef<HTMLDivElement>(null);
const chartRef = useRef<echarts.ECharts | null>(null);
useEffect(() => {
if (!divRef.current) return;
const chart = echarts.init(divRef.current);
chartRef.current = chart;
return () => chart.dispose(); // cleanup で必ず破棄
}, []);
useEffect(() => {
chartRef.current?.setOption(option); // option 更新は setOption で
}, [option]);
return <div ref={divRef} style={{ width: "100%", height: 300 }} />;
}
cleanup の dispose() を書かないと、React 18+ の StrictMode(開発時に effect を 2 回実行して掃除漏れを炙り出す仕組み)で同じ div に 2 回 init が走り、コンソールに警告が出る:
There is a chart instance already initialized on the dom.
なお dispose() 済みインスタンスへの setOption は throw せず、警告(Instance ec_xxx has been disposed)を出して無視される(実測)。エラーで落ちない分、「更新したのに描画されない」という形で現れる。
setOption の merge 挙動 — 前の系列が残る理由
setOption は既定で merge(差分マージ)。2 系列のグラフに 1 系列だけの option を渡しても、2 本目は消えずに残る。実測:
chart.setOption({ series: [s1, s2] }); // 2 本描画
chart.setOption({ series: [s1改] }); // → series は 2 本のまま(s2 が残る)
chart.setOption({ series: [s1改] }, { notMerge: true }); // → 1 本になる(全置換)
chart.setOption({ series: [s1改] }, { replaceMerge: ["series"] }); // → series だけ置換
使い分けの目安:
| 方法 | 挙動 | 向く場面 |
|---|---|---|
| 既定(merge) | 渡したキーだけ差分更新 | データ値の更新、色の変更 |
notMerge: true | option 全体を破棄して置換 | グラフの種類ごと切り替える |
replaceMerge: ["series"] | 指定キーだけ置換、他は維持 | 系列の増減(軸や tooltip 設定は保持) |
冒頭のプレイグラウンドで再現できる: preset: bar(2 系列)を適用 → JSON の series を 1 本に減らして「setOption を実行」。notMerge OFF なら 2 本目が残り、ON にすると消える。
リサイズはコンテナに追従しない
ECharts の canvas は init 時点のコンテナ寸法で固定され、その後コンテナの幅が変わっても自動では追従しない。chart.resize() を呼んだ瞬間だけ現在の寸法に合わせ直す。スライダーで体感できる:
実務では window の resize イベントか、ResizeObserver(要素自体の寸法変化を監視できるブラウザ API)で resize() を呼ぶ:
const ro = new ResizeObserver(() => chart.resize());
ro.observe(divRef.current);
// cleanup で ro.disconnect()
フレックスレイアウト内でサイドバーの開閉だけで幅が変わるケースは window resize では拾えないので、ResizeObserver の方が確実。
Node での SSR(SVG 文字列出力)
v5.3 以降、DOM の無い Node 環境で SVG 文字列としてレンダリングできる。OG 画像やメール向けの静的グラフ生成に使える(実測: 3.9KB の SVG が返る):
const chart = echarts.init(null, null, {
renderer: "svg", ssr: true, width: 400, height: 300,
});
chart.setOption(option);
const svgStr = chart.renderToSVGString(); // "<svg width=..." の文字列
本記事の merge 挙動の実測も、この SSR モードを使って Node 単体で行った(ブラウザ不要でテストに組み込める)。
つまずいたポイント
- 系列を減らしたのに描画が減らない — 既定 merge の仕様。
notMerge: trueかreplaceMerge: ["series"]を明示する(本記事の表を参照) There is a chart instance already initialized on the dom.— 同じ div への二重init。React では effect cleanup のdispose()漏れが典型原因(StrictMode で顕在化する)- 部品の登録漏れが「エラーにならない」 —
echarts/core構成で tooltip や legend が出ない時は、まずuse([...])に該当 Component があるか確認。エラーメッセージ頼みのデバッグができない dispose()後のsetOptionが黙って無視される — 非同期処理の完了後に更新するコードでは、アンマウント済みかをchart.isDisposed()で確認してから呼ぶ- プレイグラウンドの option を JSON にする制約 — 本物の option は関数(formatter 等)も書けるが、demo は
JSON.parseなので関数は書けない。関数が要る設定を試す時はコードに移す
関連書籍
Python インタラクティブ・データビジュアライゼーション入門 : Plotly/Dashによるデータ可視化とWebアプリ構築 | tech-book.net
Webサイトで公開できる対話的・探索的(読み手が自由に動かせる)可視化をPythonで実践.デー
データ分析者のためのPythonデータビジュアライゼーション入門 コードと連動してわかる可視化手法 | tech-book.net
分析したデータを わかりやすく ビジュアライゼーションしよう! 【データビジュアライゼーションとは】 数値データ・位置のデータ・文章のデータ等
関連記事 / 関連 Topic
- Chart.js v4 を React で使う — 同じ canvas + 設定スタイルの軽量な選択肢
- Recharts v3 の bar chart レシピ — JSX 合成スタイル
- D3 + React で bar chart — 描画を自作する場合
- 体系的に学ぶなら: JavaScript の本 / TypeScript の本(tech-book.net)