You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在props变化时强制react-map-gl的Map组件重新渲染

问题:react-map-gl上传GeoJSON后需平移地图才显示

我在项目中使用react-map-gl的Map组件,通过props传入props.displayUploadedFiles(一个GeoJSON对象数组),想借助Source和Layer组件在地图上展示这些GeoJSON数据。但上传文件后,必须平移地图触发重渲染才能看到数据。

已经确认文件上传成功且是标准GeoJSON格式,传入Map组件的props也正常更新,但依赖该props的钩子要等到平移地图才会触发。

代码示例

// App.tsx
export default function App(): JSX.Element {
  const [files, setFiles] = useState<any[]>([]);
  const setFilesState = (filesUploaded: File[]) => {
    const jsons = kmlToGeoJson(filesUploaded);
    setFiles(jsons);
  };

  return (
    <>
      <MyMap displayUploadedFiles={files} ... />
      <MyFileUploader fileStateUpdater={setFilesState} />
    </>
  );
}

// MyMap.tsx
function MapboxMap(props: MapboxMapProps): JSX.Element {
  const geoJsons = useMemo(() => (props.displayUploadedFiles.map((file) => {
    const id = JSON.stringify(file.features);
    return (
      <Source key={id} type="geojson" data={file}>
        <Layer key={id} {...pointLayer} />
      </Source>
    );
  })), [JSON.stringify(props.displayUploadedFiles)]); // 仅在平移地图后才会执行上传后的更新

  return (
    <Map mapboxAccessToken={mapboxToken} ...>
      {geoJsons}
      ...
    </Map>
  );
}

function MyMap(props: StyledMapProps): JSX.Element {
  return (
    <div id={sx.styledMap}>
      <MapboxMap ... displayUploadedFiles={props.displayUploadedFiles} />
      ...
  );
}

export default MyMap;

已尝试的方法

  • 参照类组件的解决方案使用useEffect,问题依旧
  • 用useMemo并将依赖设为JSON.stringify(props.displayUploadedFiles),试图解决数组引用未变导致不重渲染的问题,但无效
  • 调整MyFileUploader的位置,将逻辑移入MyMap或MapboxMap内,props更新正常,但钩子仍需平移地图才触发

解决方案

1. 修复useMemo依赖项,确保引用更新

不要用JSON.stringify作为依赖,直接使用props.displayUploadedFiles。如果kmlToGeoJson返回的对象存在引用复用的情况,转换后生成新对象确保引用更新:

// App.tsx中修改setFilesState
const setFilesState = (filesUploaded: File[]) => {
  const jsons = kmlToGeoJson(filesUploaded).map(item => ({...item})); // 生成新对象,强制更新引用
  setFiles(jsons);
};

// MapboxMap中修改useMemo依赖
const geoJsons = useMemo(() => props.displayUploadedFiles.map((file) => {
  const id = file.features?.[0]?.properties?.id || `${Date.now()}-${Math.random()}`; // 用更稳定的key
  return (
    <Source key={id} type="geojson" data={file}>
      <Layer key={id} {...pointLayer} />
    </Source>
  );
}), [props.displayUploadedFiles]);

2. 手动触发地图刷新

通过ref获取Map实例,在displayUploadedFiles变化时调用refresh方法强制刷新:

function MapboxMap(props: MapboxMapProps): JSX.Element {
  const mapRef = useRef<Map>(null);

  // 当props变化时强制刷新地图
  useEffect(() => {
    mapRef.current?.refresh();
  }, [props.displayUploadedFiles]);

  const geoJsons = useMemo(() => props.displayUploadedFiles.map((file) => {
    const id = file.features?.[0]?.properties?.id || `${Date.now()}-${Math.random()}`;
    return (
      <Source key={id} type="geojson" data={file}>
        <Layer key={id} {...pointLayer} />
      </Source>
    );
  }), [props.displayUploadedFiles]);

  return (
    <Map ref={mapRef} mapboxAccessToken={mapboxToken} ...>
      {geoJsons}
      ...
    </Map>
  );
}

3. 单独包裹GeoJSON数据确保可追踪

如果GeoJSON数据结构复杂,react-map-gl可能无法检测到data的变化,可将每个GeoJSON对象用useMemo单独包裹:

// MapboxMap中修改渲染逻辑
const geoJsons = props.displayUploadedFiles.map((file) => {
  // 单独包裹data,确保变化时Source能感知
  const geoData = useMemo(() => file, [JSON.stringify(file)]);
  const id = useMemo(() => JSON.stringify(file.features), [file.features]);
  return (
    <Source key={id} type="geojson" data={geoData}>
      <Layer key={id} {...pointLayer} />
    </Source>
  );
});

内容的提问来源于stack exchange,提问作者artificialask

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:05:40