Как да направите просто приложение за карта с помощта на Ruby on Rails, React and Leaflet, part 2: Frontend

Tidal Basin, Washington, D.C., източник: автор

Наскоро създадох приложение, което има интерактивна карта на уличните дървета във Вашингтон, D.C. В предишната публикация писах за предизвикателствата на реорганизацията на данни и създаването на Ruby on Rails API. Този път ще опиша процеса на изграждане на интерфейс с помощта на React.js и съответните библиотеки за картографиране.

Получаване на картата за показване

Първата стъпка беше картата да се покаже. Реших да използвам библиотеката React-Leaflet с основна карта Mapbox. След като инсталирах React-Leaflet, просто импортирах Map, TileLayer, Marker и Popup компоненти. Всичко това и още много неща са част от секцията „Първи стъпки“ на документите React-Leaflet. Ако следвате примерите в документите, можете лесно да получите картата или всеки друг компонент, който да се покаже.

Следващата стъпка беше прилагането на основната карта Mapbox. След като получих маркера за обществен достъп от Mapbox, просто го предадох, заедно с URL адреса на плочките, като реквизит към компонента на TileLayer. По този начин получих желаната статична карта, която да покажа. Моят App.js съдържа следния код:

Добавяне на данни към картата

Сега, когато получих картата да се покаже, беше време да добавя някои данни към нея. Тази стъпка се оказа по-сложна, отколкото изглеждаше. Ако следвате примери от документите, всичко, което трябва да направите, е да импортирате компонентите Marker и Popup и да предадете необходимите реквизити. Много е ясно, но не можах да накарам маркерите да се показват на картата ми. След часове на отстраняване на грешки и проучвания разбрах, че проблемът не е в моя край. Оказа се, че маркерът по подразбиране на Leaflet е свързан със снимка, която е изтрита. За да коригирам този проблем, трябваше да изтрия иконата на персонализиран маркер и да импортирам нова.

За приложения, които работят с по-малко количество данни, маркерите с изскачащи прозорци може да са достатъчни. В моя случай тя само направи картата да изглежда изключително струпвана и почти невъзможна за навигация, тъй като беше покрита с хиляди маркери на дървета. React-Leaflet-Markercluster е библиотека, която решава този проблем в (буквално) само в няколко реда код.

След всички тези промени, App.js съдържа следния код:

Подобряване на производителността

Големите данни, състоящи се от близо 200 000 дървета, вероятно са най-трудната част от този проект. Има много начини да подобрите скоростта и производителността и аз се връщам към този проблем още от деня, в който започнах да работя над това приложение. Една от идеите, които използвах, използва ограничения за заявки (които вече са внедрени в задния ред), за да извлича само дървета, които са в границите на екрана на потребителя. За да реализирам напълно тази функция, трябваше да направя следното:

  • добавете координати към състоянието и дефинирайте първоначалната му стойност, която е равна на границите на картата, представена за първи път на екрана
  • добавете филтър към извличането на API, така че той да връща само дървета, които са в границите на екрана
  • добавете слушател на събитията към картата, който ще актуализира състоянието с нови координати, тъй като те се променят, както и с новоиззетите дървета

Това може да звучи сложно, но много от основните работи вече са свършени. Основната логика, която върши цялата работа за нас, вече е написана в бекенда. В допълнение към това, вградените методи на Leaflet правят тази стъпка много по-лесна. Един от тях е .getBounds () метод, който грабва координатите на ъглите на екрана (редове 58–61). Обхватът на този метод се променя, така че моят съвет е да играете с него в грешката, за да разбера точния синтаксис.

Що се отнася до слушателя на събитието, Leaflet идва с много възможности. React-Leaflet използва малко по-различен синтаксис, който е обяснен в документите. Слушателят на събитията, който подхожда на приложението ми, най-добре се задейства, когато потребителят спре да влачи картата (onMoveEnd, ред 74). Във функцията за обратно извикване задавам новото състояние и получавам дървета. Това прави приложението много по-бързо и създава по-добро потребителско изживяване.

Към този момент App.js съдържа следния код:

Внедряване на филтри

Данните, които използвам, са доста подробни. Всяко дърво идва с информация за неговото състояние, отделение, научно име, род, род и т.н. Исках да използвам цялата тази информация и да позволя на потребителите да филтрират дървета по отделение, състояние, общо име или научно име. За да изградя тази функция, трябваше да добавя два нови компонента, Navbar и Filters. Реших да използвам React-Semantic-UI и неговия компонент на страничната лента, въпреки факта, че може да не е най-елегантното или просто решение (неговите компоненти са в App.js, Navbar.js и Filters.js).

Navbar.js е доста прост компонент, който не съдържа никаква важна логика.

Filters.js съдържа формата и всички необходими обратни повиквания, които грабват стойностите от формата.

След това тези стойности се изпращат до App.js и се прилагат към извикването за извличане.

На теория промяната на състоянието би довела до повторното представяне на картата всеки път, когато потребителят приложи и нулира филтрите. На практика обаче това не беше така. Докато състоянието съдържа текущите стойности на филтъра (отделение, условие, commonName или sciName), данните на дърветата винаги са били с една стъпка назад, съдържащи резултатите от предишното търсене.

След часове или отстраняване на грешки и задълбочено проучване разбрах, че самата природа на Реакта е причината за това. Преди да се сблъскам с този проблем, не бях запознат с факта, че setState е асинхронен. Това означава, че редът на действията беше различен от този, който очаквах. Извикването за извличане ще се извърши преди актуализацията на състоянието, което води до това, че данните за дървото винаги са една стъпка назад. Този проблем беше отстранен чрез прехвърляне на стойностите на филтъра директно към функцията, осъществяваща извикване за извличане (редове 53–59 и 71–84). За алтернативни решения, вижте този блог.

App.js съдържа следния код:

Сега, когато цялата функционалност е добавена към картата, остава само да добавите стайлинг. Много от елементите на картата могат да бъдат персонализирани с помощта на обикновен CSS (за инструкции за стилистични маркери щракнете тук). И картата е готова! Ето как изглежда моята карта:

източник: авторизточник: автор

За повече информация и демонстрация на живо на това приложение, щракнете тук.