如何避免React中Fusion Maps地图因状态变量变更重渲染
解决React中FusionMaps悬停时地图闪烁的问题
我用React结合FusionMaps做了美国地图,现在碰到个糟心的问题:鼠标悬停到某个州的时候,地图会不停闪烁,体验特别差。查了下原因,是我给地图绑了entityRollover事件,事件里会更新hoveredState这个状态变量,导致整个组件(包括地图)跟着重渲染。我想要的效果是,悬停时只在旁边的文本框里显示州名,地图本身别跟着瞎动。知道useRef、useMemo、useCallback这些Hook能解决,但不会具体用。
两种解决办法
办法一:用useRef直接改DOM(最简单)
不用状态变量,直接用useRef拿到文本框的DOM元素,在悬停事件里直接修改它的内容,这样不会触发任何组件重渲染,自然就不会闪烁了。
代码示例:
import { useRef } from 'react'; import ReactFusioncharts from 'react-fusioncharts'; import FusionCharts from 'fusioncharts'; import Maps from 'fusionmaps'; import USA from 'fusioncharts/maps/fusioncharts.usa'; FusionCharts.addDep(Maps); FusionCharts.addDep(USA); function App() { // 存储文本框的DOM引用 const stateNameRef = useRef(null); const chartConfigs = { type: 'usa', width: '100%', height: '500', dataFormat: 'json', dataSource: { "chart": { "caption": "美国人口分布", "subcaption": "2019年估算值", "numbersuffix": "百万", "entityFillHoverColor": "#FFF9C4", "theme": "fusion" }, "data": [ // 这里放入你原代码中的地图数据即可 ] }, events: { entityRollover: (event, data) => { // 直接更新文本框内容 if (stateNameRef.current) { stateNameRef.current.textContent = data.label; } }, entityRollout: () => { // 鼠标移开时清空文本框 if (stateNameRef.current) { stateNameRef.current.textContent = ''; } } } }; return ( <div> <div ref={stateNameRef} style={{ fontSize: '24px', marginBottom: '20px' }}></div> <ReactFusioncharts {...chartConfigs} /> </div> ); } export default App;
办法二:拆分组件+用useMemo缓存地图
如果必须用状态管理(比如之后还要用到hoveredState做其他逻辑),可以把地图拆成独立的子组件,用useMemo缓存它,让它只在地图配置变化时才重渲染,不受父组件的hoveredState影响。
代码示例:
import { useState, useMemo, useCallback } from 'react'; import ReactFusioncharts from 'react-fusioncharts'; import FusionCharts from 'fusioncharts'; import Maps from 'fusionmaps'; import USA from 'fusioncharts/maps/fusioncharts.usa'; FusionCharts.addDep(Maps); FusionCharts.addDep(USA); // 独立的地图子组件 const USMap = ({ onStateHover }) => { const chartConfigs = { type: 'usa', width: '100%', height: '500', dataFormat: 'json', dataSource: { "chart": { "caption": "美国人口分布", "subcaption": "2019年估算值", "numbersuffix": "百万", "entityFillHoverColor": "#FFF9C4", "theme": "fusion" }, "data": [ // 原地图数据放入此处 ] }, events: { entityRollover: (event, data) => onStateHover(data.label), entityRollout: () => onStateHover('') } }; return <ReactFusioncharts {...chartConfigs} />; }; function App() { const [hoveredState, setHoveredState] = useState(''); // 用useCallback保证回调函数引用稳定 const handleStateHover = useCallback((state) => { setHoveredState(state); }, []); // 用useMemo缓存地图组件,仅当handleStateHover变化时重新生成 const cachedMap = useMemo(() => { return <USMap onStateHover={handleStateHover} />; }, [handleStateHover]); return ( <div> <div style={{ fontSize: '24px', marginBottom: '20px' }}>{hoveredState}</div> {cachedMap} </div> ); } export default App;
问题复现步骤
- 创建React项目:
npx create-react-app map-demo
- 安装依赖包:
npm i fusioncharts react-fusioncharts fusionmaps
- 将上述代码替换到
src/App.js中,补充好原代码中的地图数据即可复现原问题。
内容的提问来源于stack exchange,提问作者cluis92
相关产品推荐
相关产品推荐

