如何将基于jQuery的HighChart地图转换为React组件
Highcharts肯尼亚选举地图 jQuery 转 React 实现方案
项目基础信息
- 原实现为基于jQuery的Highcharts肯尼亚总统选举地图,点击对应郡县可弹出选区两党得票占比饼图
- 现有可用资源:
- 郡县选举统计JSON数据(当前为硬编码格式,后续可直接替换为API接口返回数据)
- 原版本的数据映射、配色、交互逻辑
- Highcharts相关依赖资源
原jQuery核心实现代码
$(function() { var json = [{ "name": "Busia", "registered": "251305", "UDA": "0", "Azimio": "0", "value": "-5" },{ "name": "Wajir", "registered": "118091", "UDA": "8", "Azimio": "7", "value": "-2" }] function init() { function pointClick(json) { var row = this.options.row, $div = $('<div></div>') .dialog({ title: ([this.name]), width: 400, height: 300 }); window.chart = new Highcharts.Chart({ chart: { renderTo: $div[0], type: 'pie', width: 370, height: 240 }, title: { text: null }, series: [{ name: 'Votes', data: [{ name: 'Azimio', color: '#0200D0', y: Number(this.Azimio) }, { name: 'UDA', color: '#C40401', y: Number(this.UDA) }], dataLabels: { format: '<b>{point.name}</b> {point.value:.1f}%' } }] }); } // 初始化地图 $('#presidential').highcharts('Map', { title: { text: 'Presidential Electoral Map <em>(Kenya)</em>' }, legend: { title: { text: 'Political Affiliation' } }, credits: { enabled: false }, tooltip: { valueSuffix: 'Margin' }, mapNavigation: { enabled: true, enableButtons: false }, colorAxis: { dataClasses: [{ from: 0.0000001, to: 100, color: '#C40401', name: 'UDA' }, { from: -100, to: -0.00000001, color: '#0200D0', name: 'Azimio' }, { from: 0, to: 0, color: '#C0C0C0', name: 'Battle Ground(s)' }] }, series: [{ name: 'By County Difference', point: { events: { click: pointClick } }, "type": "map", "joinBy": ['name', 'name'], "data": $.each(json, function() {}), "mapData": [{ "name": "Busia", "path": "M40,-534,43,-533,46,-532L46,-530L44,-528,44,-525C44,-525,41,-520,41,-520L40,-516,40,-513,41,-511C41,-511,44,-512,43,-509,43,-506,44,-504,44,-504L38,-499,38,-497,44,-495,45,-493,41,-489,41,-486L36,-486L34,-487,30,-488,28,-487,25,-484,22,-484,20,-486,18,-483,16,-481,15,-478,14,-476L14,-473L15,-471,14,-469L12,-469L10,-467,9,-464,10,-459C10,-459,9,-458,7,-457,5,-456,5,-455,5,-455L3,-459,0,-462,0,-465,2,-470,2,-474L2,-478L5,-481,8,-486,10,-491,13,-493L13,-495L12,-499,13,-503,15,-506,15,-510,16,-513C16,-513,19,-516,20,-517,21,-517,24,-519,24,-519L27,-519,28,-519,31,-520L31,-524L32,-526,33,-527,34,-531,35,-532z" }, }] }, { "type": "mapline", "data": [{ "name": "path5072", "path": "M443,-449Z" }] }] }); } init() });
之前使用React封装库失败的核心原因
- 未正确注册Highcharts地图模块:直接使用通用Highcharts包没有加载map模块,导致
map类型图表无法识别渲染 - 原jQuery代码存在数据绑定错误:
"data": $.each(json, function() {})写法无效,$.each返回的是遍历对象本身不是选举数据数组,导致地图数据为空 - 直接照搬jQuery UI Dialog的DOM操作逻辑:在React中直接手动创建DOM节点挂载饼图,和React的虚拟DOM渲染逻辑冲突,导致弹窗、饼图渲染异常
React版本正确实现步骤
- 安装对应依赖
npm install highcharts highcharts-react-official
- 组件内注册地图模块,不要遗漏模块加载
- 修正原代码的数据绑定错误,直接传入选举数据数组,不要套无效的
$.each遍历 - 替换jQuery DOM操作弹窗逻辑,用React状态控制弹窗显隐、传递点击的选区数据,弹窗内用HighchartsReact组件渲染饼图
核心实现代码示例
import { useState } from 'react'; import Highcharts from 'highcharts'; import MapModule from 'highcharts/modules/map'; import HighchartsReact from 'highcharts-react-official'; // 导入完整的肯尼亚地图路径数据(替换为本地完整mapData即可) import kenyaMapData from './kenya-map-data'; // 注册地图模块 MapModule(Highcharts); const ElectoralMap = () => { // 选举数据,后续可替换为接口请求获取 const electionData = [ { "name": "Busia", "registered": "251305", "UDA": "0", "Azimio": "0", "value": "-5" },{ "name": "Wajir", "registered": "118091", "UDA": "8", "Azimio": "7", "value": "-2" } ]; // 弹窗状态控制 const [modalOpen, setModalOpen] = useState(false); const [selectedCounty, setSelectedCounty] = useState(null); // 地图配置,绝大多数配置和原jQuery版本完全一致 const mapOptions = { title: { text: 'Presidential Electoral Map <em>(Kenya)</em>' }, legend: { title: { text: 'Political Affiliation' } }, credits: { enabled: false }, tooltip: { valueSuffix: 'Margin' }, mapNavigation: { enabled: true, enableButtons: false }, colorAxis: { dataClasses: [{ from: 0.0000001, to: 100, color: '#C40401', name: 'UDA' }, { from: -100, to: -0.00000001, color: '#0200D0', name: 'Azimio' }, { from: 0, to: 0, color: '#C0C0C0', name: 'Battle Ground(s)' }] }, series: [{ name: 'By County Difference', point: { events: { click: function() { // this指向当前点击的选区点数据 setSelectedCounty(this); setModalOpen(true); } } }, type: "map", joinBy: ['name', 'name'], data: electionData, // 修正原$.each的错误写法,直接传数据数组 mapData: kenyaMapData }, { type: "mapline", data: [{ "name": "path5072", "path": "M443,-449Z" }] }] }; // 饼图配置 const pieOptions = selectedCounty ? { chart: { type: 'pie', width: 370, height: 240 }, title: { text: null }, series: [{ name: 'Votes', data: [{ name: 'Azimio', color: '#0200D0', y: Number(selectedCounty.Azimio) }, { name: 'UDA', color: '#C40401', y: Number(selectedCounty.UDA) }], dataLabels: { format: '<b>{point.name}</b> {point.value:.1f}%' } }] } : {}; return ( <div> <div id="presidential"> <HighchartsReact highcharts={Highcharts} constructorType={'mapChart'} options={mapOptions} /> </div> {/* 自定义弹窗,替换原jQuery UI Dialog */} {modalOpen && selectedCounty && ( <div className="modal-mask" onClick={() => setModalOpen(false)}> <div className="modal-content" onClick={e => e.stopPropagation()}> <div className="modal-header"> <span>{selectedCounty.name}</span> <button onClick={() => setModalOpen(false)}>×</button> </div> <HighchartsReact highcharts={Highcharts} options={pieOptions} /> </div> </div> )} </div> ) } export default ElectoralMap;
注意事项
- 完整的肯尼亚郡县地图路径数据直接替换示例中
kenyaMapData的导入即可,不需要修改其他配置 - 弹窗样式可根据需求自行编写CSS,不需要依赖jQuery UI
- 如果后续需要对接API,只需要把electionData改为useState管理的状态,在useEffect中请求接口赋值即可,其他逻辑不需要改动
内容的提问来源于stack exchange,提问作者Brian Otieno
相关产品推荐
相关产品推荐

