This week, I aimed to solve the age old question: Where should I eat? Here’s what I learned from it.
This week, I aimed to solve the age old question: Where should I eat?
I’ll first talk about the app I made to give you some context, and then I’ll dive into problems I was presented and what I learned by fixing them.
You can try out the site yourself at whereshouldieat.app and as always all of the code can be found on GitHub.
Where Should I Eat?
Of course, solutions for this already exist. You can pull up Google search and fire out a quick search for “sushi near me”, or look up an article about “top restaurants in Manhattan.” However, both of these solutions answer the question by forcing you to make an even harder decision between many choices, some of which you may have never heard of before.
My site alleviates this by only giving you one decision at a time: whether you want to eat at this restaurant or not. If you don’t want to eat there, you shouldn’t need to think about it anymore. If you do want to eat there, you should be be immediately taken to directions on how to get there.
These choices are encoded into gestures. A swipe left means to ignore that restaurant and no longer consider it. A swipe right leads to the Google Maps page for that restaurant where you can easily find directions. You only ever have to make one decision at time, making the whole process as streamlined for someone as indecisive as me.
1. Slowing down your app can speed up the user experience
Responsive inputs can feel amazing. As soon as you start typing in an input field, autosuggestions can pop up and other queries can start happening. However, some things just shouldn’t happen on every key stroke.
For my app, I initially had the text input set up so that every key stroke would fetch from the Yelp API, find and set locations and businesses, and change the location on the map. This caused the map to jostle around and load several towns based off partial inputs. Trying to search for “midtown Manhattan” would lead to a search for “m” and “mi” and “mid” and every other possible substring starting from the beginning.
My solution to this was a concept called debouncing. This makes it so that it is physically impossible for the function to be called several times within a certain time interval, effectively throttling the relationship between the input and its effects. Now the search only fires once the user has cooled off on typing in their input, giving the expected results.
In terms of implementation, I found this article very helpful with ways to do it with React primitives. Of course, someone has made a library for debouncing input fields called react-debounce-input, so I used that instead. Implementation is as easy as replacing inputs with DebounceInputs.
export default function MyDebounceSearch() const [state, setState] = useState("") return ( <div> <DebounceInput minLength=2 value=state debounceTimeout=300 onChange=event => setState(event.target.value) /> <p>Value: state</p> </div> )
2. Your first user shouldn’t come with deployment
This may sound obvious, but you are the first person to use your site. And according to Jakob’s law, you have spent MUCH more time on sites other than the one you just started developing yesterday. You’re a good judge of how sites work and how your new site should behave. So, use it!
Even as you’re developing, be the user and interact with your site constantly. And if something is confusing to your user-brain, put on your developer hat and fix it!
In the case of Where Should I Eat, I found this most evident with how I handled the URL. I used the barely-developed version of the site whenever I was out with friends, and I found myself confused whenever I tried to press the back button on my browser and it completely navigated me away from the site. The internet has taught me that if I want to undo, I can navigate back and expect the change to be undone.
To fix this, I lifted my state into the URL. Now, whenever I major change was made to the location of search or the food that the user was craving, this would be reflected as a URL parameter. So, if a user decided they weren’t feeling tacos and they wanted to go back to their previous choice of sushi, they could naturally do that by navigating back on their browser.
Quick aside: If you’re using Next.js and it’s router to handle putting state into the URL, I would recommend using shallow routing and setting the scroll option to false if you’re finding issues with state changes jerking your site’s scroll or data around.
import useEffect from 'react' import useRouter from 'next/router' // Current URL is "https://dev.to/" function Page() const router = useRouter() useEffect(() => // Always do navigations after the first render router.push('/?counter=10', undefined, shallow: true, scroll: false ) , ) useEffect(() => // The counter changed! , [router.query.counter]) export default Page
3. Compromise with cunning
Originally, I wanted to use the Google Maps API. I was quickly met with a credit card form and a pricing scheme that scares the part of me that hopes my app is an overnight success. And this isn’t even including using their map tiling for generating the dynamic map. (They do provide $200/month of requests free but I learned that after I started my site.)
Instead, I reached for free alternatives. The Yelp Fusion API provides me with place information such as reviews, locations, and hours of business with a solid 5,000 requests/day without upgrading to an enterprise plan. Additionally, the pigeon-maps package leverages free tiling solutions such as OpenStreetMap to provide dynamic and well-tooled maps for React.
Of course, I still wanted to use Google Maps at the end of the day. In order to still get that integration, I simply used the queried latitude, longitude, and place name from Yelp to construct a Google Maps URL that would take me right to the correct place. There is a helpful guide I found for doing that.
Now I’m able to get similar functionality without breaking the bank and with no noticeable hits to the user experience. I’ve comprised on not using the Google Maps API but have lost no major features to my app!
I hope to level-up my development so that the only issue I have is coming up with the next unicorn startup idea! And I hope to share some of the wisdom I learn along the way with all of my readers!
If you want to follow along with my programming journey, follow me here on Medium and check out my personal site at jordantwells.com.
Thanks for reading!