我无法让 Mapbox 在我的 NextJs 项目中工作

问题描述 投票:0回答:1
import React, { useEffect, useRef, useState } from 'react';
// eslint-disable-line import/no-webpack-loader-syntax
// @ts-ignore
import mapboxgl from '!mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import Image from 'next/image';
import moment from 'moment';
import { MAP_STYLES, CONFIG_MAP } from '@/components/MapContainer/config';

import styles from './styles.module.scss';

interface MapProps {
  config: any;
  heatmap: any;
}

interface MapInfo {
  sourceId: string;
  layerId: any;
  data: any;
  config: any;
  urlObj: any;
}

export const MapContainer: React.FC<MapProps> = ({ config, heatmap }) => {
  const defaultCenter = [-84.45, 33.76];
  const defaultZoom = 10;
  const center = heatmap?.center || defaultCenter;
  const mapContainer = useRef<any>(null);
  const map = useRef<mapboxgl.Map | any>(null);
  const [lng, setLng] = useState(center[0]);
  const [lat, setLat] = useState(center[1]);
  const [zoom, setZoom] = useState(config?.zoom_level || defaultZoom);
  const [mapStyle, setMapStyle] = useState(MAP_STYLES[0].style);
  const [mapInfo, setMapInfo] = useState<MapInfo[]>([]);
  const [isMenuOpen, setIsMenuOpen] = useState(false);

  useEffect(() => {
    console.log('Map instance:', map.current);

    if (mapContainer.current && !map.current) {
      mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN as string;
      map.current = new mapboxgl.Map({
        container: mapContainer.current,
        style: mapStyle,
        center: center,
        zoom: zoom,
      });

      map.current.on('load', () => {
        heatmap?.data?.forEach((data: any, index: any) => {
          loadGeoJson(data, index);
        });

        map.current?.addControl(
            new mapboxgl.GeolocateControl({
              positionOptions: {
                enableHighAccuracy: true,
              },
              trackUserLocation: true,
              showUserHeading: true,
            }),
        );
        map.current?.addControl(new mapboxgl.NavigationControl());
      });
      map.current.on('move', () => {
        if(map.current) {
        setLng(map.current.getCenter().lng);
        setLat(map.current.getCenter().lat);
        setZoom(map.current.getZoom());
        }
      });
    }

    return () => {
      if (map.current) {
        map.current.remove();
      }
    };
  }, []);

  useEffect(() => {
    if (map.current) {
      map.current.setStyle(mapStyle);
    }
  }, [mapStyle]);

  const toggleMenu = () => {
    setIsMenuOpen(!isMenuOpen);
  };

  const handleStyleChange = (style: string) => {
    setMapStyle(style);
    setIsMenuOpen(false);
  };

  const parseDate = (inputDate: any) => {
    const cleanedDate = inputDate.replace(/<br\/>/g, '').trim();
    return moment(cleanedDate, 'Do MMM YYYY').format('YYYY-MM-DD');
  };

  const parseTime = (inputTime: any) => {
    return moment(inputTime, 'h A').format('HH');
  };


  const loadGeoJson = async (config: any, layerId: any) => {
    try {
      console.log('Loading GeoJSON data:', config?.url);
      const response = await fetch(config?.url);
      if (!response.ok) {
        new Error(`Error loading GeoJSON data: ${response.statusText}`);
      }
      const data = await response.json();
      const {uniqueSourceId} = configureMap(map, data, layerId, config);
      const _mapInfo = [...mapInfo, {
        sourceId: uniqueSourceId,
        layerId: layerId,
        data: data,
        config: config,
        urlObj: config?.url,
      }];
      setMapInfo(_mapInfo);
    } catch (error) {
      console.error('Error loading GeoJSON data:', error);
    }
  };

  const configureMap = (map: mapboxgl.Map, data: any, layerId: any, conf: any) => {
    const uniqueLayerId = `cluster-layer-${layerId}`;
    const uniqueSourceId = `cluster-source-${layerId}`;
    config.functions.forEach((func: any) => {
      const {name, layerType = "clustered", args} = func;
      // @ts-ignore
      CONFIG_MAP[name]({args, map, data, layerType, layerId, uniqueLayerId, uniqueSourceId}, conf);
    });

    return {
      uniqueSourceId,
      uniqueLayerId,
    };
  };

  return (
      <main className={styles['custom-map-container']}>
        <div className='relative w-full h-full'>
          <div ref={mapContainer} className='w-full h-full'/>
          <div className='absolute bottom-4 right-4 flex items-center'>
            <div className='relative'>
              <button
                  className={`bg-[#b0b0b0] text-white p-1  flex items-center space-x-2 flex-col ${
                      isMenuOpen ? 'rounded-tr-md rounded-br-md' : 'rounded'
                  }`}
                  onClick={toggleMenu}
                  style={{fontSize: '10px'}}
              >
              <span>
                <Image
                    src={MAP_STYLES[0].icon}
                    alt={MAP_STYLES[0].name}
                    className='w-10 h-10 cursor-pointer'
                    onClick={() => handleStyleChange(MAP_STYLES[0].style)}
                />
              </span>
                <span className='text-center'>Streets</span>
                {isMenuOpen && (
                    <div
                        className='absolute right-full top-0 mr-2 bg-[#b0b0b0] p-1  flex space-x-2 rounded-tl-md rounded-bl-md'>
                      {MAP_STYLES.slice(1).map(({style, name, icon}) => (
                          <div key={style} className='flex flex-col items-center'>
                      <span className='flex items-center justify-center w-10 h-10'>
                        <Image
                            src={icon}
                            alt={name}
                            className='w-full h-full object-contain cursor-pointer'
                            onClick={() => handleStyleChange(style)}
                        />
                      </span>
                            <span className='text-white text-center text-[10px]'>{name}</span>
                          </div>
                      ))}
                    </div>
                )}
              </button>
            </div>
          </div>
          <div className='ml-auto align-items-center flex text-sm mt-1 mb-3'>
            Longitude: {lng?.toFixed(2)} | Latitude: {lat?.toFixed(2)} | Zoom: {zoom?.toFixed(2)}
          </div>
        </div>
      </main>
  );
};

以上是我的完整代码

但是,控制台中没有错误,在记录 MapInstance 时,它会记录两次,第一次获取 null,然后第二次提供地图实例。

我尝试过不同的事情。但无法解决这个问题。

我尝试修改 webpack 来处理加载工作文件,我认为无论如何这都是不必要的,但这也不起作用。

如果您有任何想法,我们将不胜感激:)

next.js mapbox mapbox-gl-js mapbox-gl
1个回答
0
投票

将 Mapbox 与 NextJS 结合使用没有任何问题。我建议将您的代码简化为基本的“在页面上显示地图”,然后从那里返回。

如果您提供了所看到的错误的重现或带有存储库的codesandbox的链接,那么很可能有人会花时间浏览您的代码。

© www.soinside.com 2019 - 2024. All rights reserved.