【问题标题】:Best way to update MapBox layer based on API call?基于 API 调用更新 MapBox 图层的最佳方法?
【发布时间】:2020-02-04 19:33:55
【问题描述】:

对于较长的代码示例,我们深表歉意。我在我的 componentDidMount 中进行 API 调用(Mapbox Directions API)以获取坐标点并在多个坐标之间绘制路线线。我目前正在根据我从这个 API 调用获得的坐标在我的地图上画一条线,问题是,API 调用是根据存储在我的 Redux 存储中的数据进行的,如果我更改存储,地图上绘制的线停留在那里(因为 componentDidMount 不再被调用,所以该行的数据保持不变)。我不是 React 专家,我正在寻找一种优雅的方式来刷新在地图上绘制的线条。任何帮助表示赞赏!提前致谢。

戴夫

import React, { Component } from 'react';
import ReactMapboxGl, { Marker } from 'react-mapbox-gl';
import marker from '../../assets/images/marker.png';
import Loading from '../Loading';
import { connect } from 'react-redux';
import ItineraryCard from '../Itinerary/ItineraryCard';
import photo1 from '../../assets/images/nav_tourisme.jpg';

const Map = ReactMapboxGl({
  accessToken:
    '...',
});

class MapWithLine extends Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      data: [],
      isLoaded: false,
      zoom: [13],
    };
  }

  onMapLoad = map => {
    map.addLayer({
      id: 'route',
      type: 'line',
      source: {
        type: 'geojson',
        data: {
          type: 'Feature',
          properties: {},
          geometry: {
            type: 'LineString',
            coordinates: this.state.data,
          },
        },
      },
      layout: {
        'line-join': 'round',
        'line-cap': 'round',
      },
      paint: {
        'line-color': '#888',
        'line-width': 8,
      },
    });
    map.resize();
  };

  removeLastCharacter(str) {
    if (str != null && str.length > 0) {
      str = str.substring(0, str.length - 1);
    }
    return str;
  }

  getItineraryCoords() {
    let itineraryString = '';
    this.props.itineraryReducer.itinerary.map(exp => {
      const coords = exp.lng + ',' + exp.lat + ';';
      itineraryString = itineraryString + coords;
      return itineraryString;
    });
    return this.removeLastCharacter(itineraryString);
  }

  componentDidMount() {
    if (this.props.itineraryReducer.itinerary.length > 1) {
      const url =
        'https://api.mapbox.com/directions/v5/mapbox/' +
        'driving/' +
        this.getItineraryCoords() +
        '?geometries=geojson&' +
        'access_token='...';
      fetch(url)
        .then(response => response.json())
        .then(
          result => {
            result.routes
              ? this.setState({
                  data: result.routes[0].geometry.coordinates,
                  isLoaded: true,
                })
              : this.setState({
                  isLoaded: true,
                });
          },
          error => {
            this.setState({
              isLoaded: true,
              error: error,
            });
          }
        );
    }
  }

  componentWillUnmount() {
    //Resume scrolling on body when drawer is not maximized (component unmounted)
    document.body.style.overflow = 'visible';
  }
  render() {
    const { error, isLoaded } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <Loading />;
    } else {
      return (
        <div className="mapWrapper">
          <Map
            style="mapbox://styles/mapbox/streets-v8"
            className="mapContainer"
            center={this.state.data[0]}
            zoom={this.state.zoom}
            onStyleLoad={this.onMapLoad}
          >
            });
            {this.props.itineraryReducer.itinerary.map((exp, index) => {
              const coords = [exp.lng, exp.lat];
              return (
                <Marker coordinates={coords} anchor="bottom" key={index}>
                  <img src={marker} alt="marker-icon" className="mapMarker" />
                </Marker>
              );
            })}
          </Map>
        </div>
      );
    }
  }
}

const mapStateToProps = state => {
  return {
    itineraryReducer: state.itineraryReducer,
  };
};

export default connect(mapStateToProps)(MapWithLine);

【问题讨论】:

    标签: reactjs react-redux mapbox mapbox-gl-js


    【解决方案1】:

    如果我理解正确,您希望在 props.itineraryReducer 更新时显示来自 mapbox Directions API 的响应,并将其存储在 state.data 中。因此,您可以使用 ComponentDidUpdate - 这是一种 React 生命周期方法,您可以使用它来对组件中的更改(无论是状态还是道具)采取行动 - 每当这些道具和状态变量发生变化时触发地图上的更新。你可以做类似的事情:

    componentDidMount (prevProps, prevState) {
      const { data } = this.state
      const { itineraryReducer } = this.props
    
      if (prevProps.itineraryReducer !== itineraryReducer) {
        if (itineraryReducer.itinerary.length > 1) {
          const url =
            'https://api.mapbox.com/directions/v5/mapbox/' +
            'driving/' +
            this.getItineraryCoords() +
            '?geometries=geojson&' +
            'access_token='...';
    
          fetch(url)
            .then(response => response.json())
            .then(
              result => {
                result.routes
                  ? this.setState({
                      data: result.routes[0].geometry.coordinates,
                      isLoaded: true,
                    })
                  : this.setState({
                      isLoaded: true,
                    });
              },
              error => { // You should probably catch this error on a .catch() in your promise chain
                this.setState({
                  isLoaded: true, 
                  error: error,
                });
              }
            );
        }
      }
    
      if (prevState.data !== data) {
        this.addLineLayer(data) // Here is where we are actually drawing the line map layer
      }
    }
    
    addLineLayer = (coords) => {
      this.map.addLayer({
          id: 'route',
          type: 'line',
          source: {
            type: 'geojson',
            data: {
              type: 'Feature',
              properties: {},
              geometry: {
                type: 'LineString',
                coordinates: this.state.data,
              },
            },
          },
          layout: {
            'line-join': 'round',
            'line-cap': 'round',
          },
          paint: {
            'line-color': '#888',
            'line-width': 8,
          },
        });
        this.map.resize();
    }
    
    // ... (Rest of the Component)
    

    根据您的目标,您可能必须先删除地图上的前一个现有图层,然后才能绘制下一个图层。调用方法可以参考this link

    您可能还注意到在this.map 中访问了地图对象,因此您需要在加载样式时将map 对象保存在您的类实例中。为此,您可以将以下方法作为属性onStyleLoad 传递给&lt;Map&gt;

    onStyleLoad = (map) => {
      this.map = map // This saves the object map in your class instance, so that you can access it later
    }
    
    ...
    
    <Map
      style="mapbox://styles/mapbox/streets-v8"
      className="mapContainer"
      center={this.state.data[0]}
      zoom={this.state.zoom}
      onStyleLoad={this.onStyleLoad}
    >
    
    ...
    

    【讨论】:

      猜你喜欢
      • 2021-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-30
      • 1970-01-01
      • 1970-01-01
      • 2021-03-24
      • 1970-01-01
      相关资源
      最近更新 更多