SJ
Kelp
Business Search Web App Tool
"Search for your favorite coffee shop in real-time with an interactive map view."
Project Brief:
Codecademy Pro Capstone
React Project
Role / Skills:
Lead Developer
Technologies Used:
- React
- Redux
- JavaScript
- SCSS
- CSS keyframe animations
- Yelp API
- Mapbox API
- CORS Anywhere
- Single Page Application
Summary:
This application started off as a Codecademy Pro React course project that I further developed to have an interactive map experience as well as a responsive, mobile experience.
The biggest challenges I faced dealt with connecting the map markers to behave according to my vision. I only had access to the Yelp API data of each search result, which gave me the address and coordinates for each business found. I then hooked up that data to Mapbox, and made sure the results and the map markers communicated bi-directionally whenever clicking or pressing on either or.
How I did it:- In my Map component, on the map markers side, whenever you click or press any map marker, I used React-Redux to dispatch an action type of
“MARKER_CLICKED”
with a payload ofmarker.properties.id
that stored that info in my global Redux state. Using theuseEffect()
hook in the parent component, this event would trigger the actual business from the search results list to be scrolled into view automatically using thescrollIntoView()
method and the DOM manipulationclassList.add()
method to add highlighting styling to the selected result. - In my individual Business component, on the BusinessList side, whenever you click on an image of a business from the search results, it extracts the Yelp coordinates data from that business, sends it back up to the parent component, and then sets the current state of the
clickedOnBusiness
, and finally sends that back down to the Map component to fly into the actual map marker with the matching coordinates using the Mapbox API'sflyTo()
method.
Another challenge I faced was making sure to clear the map markers from previous search results whenever querying for new searches so they wouldn't pile up (the previous map markers did not clear automatically after running a new search). I used the useRef()
hook and the .current
property to keep track of the current search results data in an array, and compared that array against the next, new search results. If they didn't match, I looped through the current array of existing map markers and removed them in order to empty the array and make room for the new incoming search results.
Challenges Faced:
Creating a Custom, Interactive Map
Using the React Hook, useEffect()
, I created a function to toggle an "active" CSS class, adding or removing highlighting styling whenever users click on any map markers. Concurrently, I used scrollIntoView
with behavior: "smooth"
to effortlessly glide to the business listing of the selected map marker.
Clearing existing markers from previous searches
Using the React Hook, useRef()
and the .current
property, I was able to retrieve the existing array of map markers from the previous search and compare it against the new array of businesses populated by Yelp from running a new search.
If those two arrays weren't exactly alike, I used the built-in MapBox marker.remove()
function to clear all markers from the map.
if (currentMarkers.current !== businesses) {
currentMarkers.current.forEach(marker => marker.remove());
}
Map resizing bug: Required automatic triggering of window-resizing event
For my map's display/hide toggle, I came across a bug where the Mapbox map displayed inconveniently at 1/4th the container's size when toggling to display. The only way to set the size back to normal was by resizing the window manually.
In my map toggle button's handleClick
function, I included this setTimeout()
function to dispatch a window resize trigger event to automatically cause a resize AFTER the map displayed from invisible to visible.
setTimeout(() => {
window.dispatchEvent(new Event('resize'))
}, 0)
Loading JS script into the React project on mount
In order for the Mapbox API to create and load the map on mount, I had to figure out how to load the Javascript <script>
file from a CDN, (as you would normally insert the <script>
tag right before the closing <body>
tag of an HTML file).
I used this custom loadScript
function that takes in any CDN URL and injects it into the React-rendered HTML page on mount, similarly to placing the <script>
tag in the HTML file manually.
function loadScript(url) {
const index = window.document.getElementsByTagName("script")[0];
const script = window.document.createElement('script');
script.src = url;
index.parentNode.insertBefore(script, index);
}