Как отфильтровать массив в моем состоянии, чтобы он отображал только маркеры карты на основе моего пользовательского ввода - Реагировать - PullRequest
0 голосов
/ 28 августа 2018

Я очень новичок в React и веб-разработке, так что, надеюсь, это имеет смысл. Поэтому я создаю приложение, которое использует API-интерфейсы Google Maps и React для отображения маркеров мест, которые я выбираю из Foursquare.

Я сгенерировал массив мест из Foursquare (ограничен 5 местами, которые я хочу сохранять статичными) и поместил его в состояние моего приложения, чтобы я мог передавать данные на боковую панель списка и на карту для создания маркеров. Я также создал панель поиска «Фильтр», которую хочу использовать для фильтрации списка и маркеров карты, чтобы ТОЛЬКО места в списке и маркеры карты соответствовали поисковым запросам пользователей.

Мне удалось отфильтровать список, но я изо всех сил пытаюсь найти способ фильтрации маркеров карты.

Теперь я думаю, что мне нужно вывести массив мест из состояния и использовать входные данные поиска для фильтрации и создания нового массива, который я сохраняю в этом состоянии, но я не уверен, как это сделать ... Это то, что я сделал до сих пор:

Filter.js

import React, { Component } from 'react';

class Filter extends Component {

  filterUpdate() {
    const val = this.myValue.value
    this.props.filterUpdate(val)
  }

  render() {
    return(
      <header>
        <form>
          <input
            type='text'
            ref={ (value) => { this.myValue = value } }
            placeholder='Type to filter...'
            onChange={this.filterUpdate.bind(this)}
          />
        </form>
      </header>
    )
  }
}

export default Filter;

List.js

import React, { Component } from 'react';

class List extends Component {

  render() {

    const { places, filterText } = this.props;

    const placeList = places
    /*.filter(place => {
      // remove places that do not match current filter text
      return place.venue.name.toLowerCase().indexOf(filterText.toLowerCase()) >= 0
    })
    */
    .map(place => {
      return (
        <li key={place.venue.id}>
          <p>{place.venue.name}</p>
          <p>{place.venue.location.address}</p>
        </li>
      )
    })

    return(
      <div className="list">
        <ul>
          {placeList}
        </ul>
      </div>
    )
  }
}

export default List;

Map.js

import React, { Component } from 'react'

class Map extends Component {

  componentDidMount() {
    this.loadMap();
  }

  loadMap = () => {
      loadScript("https://maps.googleapis.com/maps/api/js?key=<INSERT API HERE>=initMap");
      window.initMap = this.initMap;
      // Shows alert when problem with auth, from: https://developers.google.com/maps/documentation/javascript/events#auth-errors
      window.gm_authFailure = function() {
        alert('Cannot load Google Maps! Please ensure that you have a valid Google Maps API key! Please go to https://developers.google.com/maps/documentation/javascript/get-api-key')
        }
    }

    initMap = () => {
      let map = new window.google.maps.Map(document.getElementById('map'), {
        center: {lat: 52.637106, lng: -1.139771},
        zoom: 15
      });
      this.props.places.map(place => {
        const marker = new window.google.maps.Marker({
          position: {lat: place.venue.location.lat, lng: place.venue.location.lng},
          map: map,
          title: place.venue.name,
          animation: window.google.maps.Animation.DROP,
          id: place.venue.id
        });
      });
    }

  render() {
    return(
      <div id="map"></div>
    )
  }
}

export default Map;

function loadScript(url) {
  // https://stackoverflow.com/questions/34779489/rendering-a-google-map-without-react-google-map
  let ref = window.document.getElementsByTagName('script')[0];
  let script = window.document.createElement('script');

  script.src = url;
  script.async = true;
  script.defer = true;
  ref.parentNode.insertBefore(script, ref);

  script.onerror = function () {
    document.write('Load error: Google Maps')
  };
}

App.js

import React, { Component } from 'react'
import Filter from './components/Filter'
import List from './components/List'
import Map from './components/Map'
import axios from 'axios'


class App extends Component {

  constructor(props) {
    super(props)
    this.state = {
      filterText: '',
      places: []
    }
  }

  filterUpdate(value) {
    this.setState({
      filterText: value
    })
  }

  componentDidMount() {
    this.loadPlaces()
  }

  loadPlaces = () => {
    const endPoint = "https://api.foursquare.com/v2/venues/explore?";
    const parameters = {
      client_id: "<INSERT API HERE>",
      client_secret: "5A3ANB4RL11MWKHBA2W0KYGKDIRHERLL1XRZE3JEH1BUS4V5",
      v: "20180323",
      query: "coffee",
      near: "Leicester, UK",
      limit: 5
    }

    // Get data for coffee places from Foursquare
    axios.get(endPoint + new URLSearchParams(parameters))
      .then(response => {
        this.setState({
          places: response.data.response.groups[0].items
        })
      })
      .catch(error => {
        alert("An error occurred fetching data from Foursquare: " + error);
      })
  }

  render() {

    const { places, filterText } = this.state;

    const filterPlaces = places
      .filter(place => {
        return place.venue.name.toLowerCase().indexOf(filterText.toLowerCase()) >= 0
      })

    return (
      <div className="App">
        <Filter
          filterText={this.state.filterText}
          filterUpdate={this.filterUpdate.bind(this)}
        />
        <main>
          <List
            places={filterPlaces}
            filterText={this.state.filterText}
          />
          <Map
            places={filterPlaces}
          />
        </main>
      </div>
    );
  }
}

export default App;

index.css

body {
  height: 100%;
  margin: 0;
  padding: 0;
  font-family: sans-serif;
}

#map {
  height: 100vh;
}

.list {
  background-color: #fff;
  opacity: 0.9;
  z-index: 1;
  text-align: left;
  margin: 5px;
  width: 25%;
  position: absolute;
  padding: 5px;
  min-width: 120px;
  height: 400px;
  overflow-y: scroll;
  top: 200px;
}

/* ##### Search ##### */

header {
  background: #3498db;
}

input[type='text'] {
  margin: 1rem;
  background: #fff;
  font-size: 1.3rem;
  border: none;
  box-shadow: inset 0 0 3px rgba(0, 0, 0, 0.3);
  border-radius: 3px;
  padding: 0 2rem;
  width: calc(100% - 3rem);
  height: 4rem;
}

Не думаю, что могу обновить состояние из метода рендеринга ... Могу ли я даже отфильтровать массив в состоянии, чтобы изменить отображаемые маркеры? Помогите!

1 Ответ

0 голосов
/ 29 августа 2018

НЕКОТОРЫЕ ИДЕИ

Сохранить ссылку на map (initMap) в экземпляре компонента (this.map=map).

shouldComponentUpdate будет вызываться при смене фильтра (с новыми реквизитами).

При использовании сохраненной ссылки на карту должна быть возможность удалить все существующие маркеры. Из новых реквизитов вы можете создавать новые маркеры. Можно было бы оптимизировать этот процесс, например скрывать только отфильтрованные маркеры.

Конечно (как обычно в sCU) сравните this.props с nextProps, чтобы избежать ненужных операций на карте.

В конце sCU вернуть false для повторного рендеринга блока - без этого div с картой будет очищен при рендеринге.

...