Not another COVID app!
Yes, I'm sorry, this is a COVID tracker. I know the market is pretty saturated on these. Some of my friends expected this to be my Silicon-Valley-Pied-Piper moment, but sadly, all I have in common with Pied Piper is a weird name...
Racoon
I'm proud of this, and willing to take the risk that people think this is some kind of dating app for raccoons. Racoon, with one 'C', is an anagram (and supposedly, the only anagram) of 'Corona.' It's a valid alternate spelling - don't listen to that red dotted line.
[EDIT] I also just finally got around to getting a custom domain. I wanted to be as cheap as possible about it though, so allow me to present... racoon.digital!
My goal here is just to show off something I'm proud of, not to try to win anything or start a real company. So if you're thinking, This idea is not original! or, This name would really confuse users and be hard to sell! - it's hopefully not that important, as long as I don't get any hiring managers who were attacked by raccoons in childhood.
Core Features and Lessons Learned
- Dynamic Google Map
- Official, up-to-date worldwide COVID-19 data
- Custom markers to display cases by users' locations (anonymously)
- Symptom checker component
- Health profile for saving/editing symptoms and diagnoses
Here are some highlights of what I feel were my biggest challenges/achievements/takeaways.
Authorization and Authentication
I wanted the two main components - the map and the symptom checker - to be available without an account. You need an account to post to the map, to see comments, and to save anything, such as bookmarks or diagnosis results.
But, I now see why it is easier to make the whole app either login required or none at all. I ran into many difficulties with ternaries on whether or not a user was 'logged in,' especially with react-router
and Redux. In general, I absolutely loved Redux, but this is also the first time I've really used it, so some of my problems may have come from a faulty implementation of Redux/Thunk. The page loads as if there is no current user every time, and then cycles through several re-renders once the fetch to my backend has retrieved the current user. This caused a ton of problems with undefined
errors on the initial load.
I should have used localStorage.token ?
for my ternaries instead of essentially currentUser ?
or loggedIn ?
based on a successful fetch to my backend auth route. Those return false on initial load, whereas if a user is logged in, localStorage.token
would return true on page load. I thought it made sense to set it up the second way just in case there were a token without a user, somehow. But, if your auth is solid, then a user would only ever have a token if they are logged_in
, and it makes more sense to base any auth conditionals on the frontend using localStorage.token ? (showpage) : null
.
google-maps-react
I used the library google-maps-react
for easily using the Google Maps JavaScript API in React. At first, this package was truly amazing, and I never realized how difficult it can be trying to use pure JS inside a React component without some kind of library. I only ran into problems with it once I wanted more customization, as you might expect. There were some small hiccups that they should definitely fix - such as forgetting to include the <Circle />
component in the default version. But even that taught me the wonders of simply changing a version number in package.json
from 2.0.2
to 2.0.3
. Love it when that works.
The deeper issues I had with google-maps-react
, and why I would almost recommend not using a wrapper package at all if you really wanted to get serious about your Google Maps React app, was all the hidden styling and mysterious <div>
s that popped up. You can specify map styling - again, huge benefit - but only properties for the whole container/map, such as position, height, and width.
As for the mystery <div>
s - I was proud of myself for going back to vanilla JS roots and using the following solution:
const div = document.querySelector("#root > div")
div.className = "map-container"
Seems small, but for the longest time I had basically:
<div id="root">
<div>
<div class="nav"></div>
<div class="map"></div>
</div>
</div>
with no idea how that 2nd, empty, classless <div>
got there!
I triple checked all my JSX, so without getting into the google-maps-react
source code, I thought it must have been from something the package does to wrap the map component.
There was an easy solution for this particular example, but there are many more such struggles of unforeseen styling here. Still love google-maps-react
, but something to be careful of when using imports. Side note - there also exists a google-map-react
.
Symptom Checker API
I used the Infermedica API for the diagnosis component. I wanted to make as few requests as possible (the API is free but limited) and that made things harder because it's designed for many requests. You're supposed to send a POST request with a patient's basic info (sex, age), then the response is a question, and then you go through an interview flow of follow-ups that add to the previous request body, and eventually returns a response of no further questions/diagnosis.
I ended up with a big, ugly, repetitive function, and a less-than-ideal user experience form submission, but! it! works!!!! It's one of my biggest refactor goals to set it up in the intended interview, or even chatbot, style. But I now deeply understand this meme.
I'm also looking into ways to replace the symptom-checker form with something cooler like a legitimate chatbot, and/or rewrite my own medical algorithm. This would be way more as an exercise in writing my own algorithms though - there have to be so many complex medical-knowledge things from real doctors that went into the Infermedica API.
Rails Serializers
I got wrecked by serializers on this project. I'll write another blog post on it, because I have a lot to say on the subject now, and I didn't find a lot of helpful blog posts along this sub-journey.
But for now I'll say that I severely underestimated serializers, started off with Active Model, then tried to switch to Fast JSON API, then saw my users' (luckily, fake) password digests exposed, then my life flash before my eyes, and then finally came back to a point of semi-understanding, security, and efficiency with AMS. Until my longer blog post - do not underestimate your Rails serializers. Make them as solid as possible, as early on as possible.
Speed!!!
I've seen this shared a couple times and always think of it now - 40% of people abandon a website that takes more than 3 seconds to load. Load times/query optimization aren't a big focus in bootcamps. It's maybe understandably too-much-too-soon for beginners. But I think I learned some useful things about Active Record optimization, like avoiding .all
, and instead using .includes()
or .where()
, so that's a small victory. But I want to learn more about caching data, using localStorage
, minimizing fetches on the frontend, and of course optimizing the backend.
TensorFlow and Machine Learning
I wanted a basic audio component because I recently read some amazing project ideas for using audio recognition to diagnose patients through a recording of a cough. Pro: I used react-mic
and it worked as expected! Yay! Con: I didn't do much else with it, or anything at all with TensorFlow in the final version of the audio component here.
I never claimed to be good enough with AI/ML to use TensorFlow here in a meaningful/diagnostic way, but I thought I had a decent enough understanding to tell the user whether or not their recording sounded like a cough. I still might. But the deadline came knocking and I abandoned that for simple audio that records, shows a frequency visualization, and lets you download your recording. But focusing on the positives here, I used a new react package, and I set up a general component for building upon later.
I struggled with the basics of audio, JS, and HTML more than expected. I had never used any of the getUserMedia()
API, and was overwhelmed by all the new AudioContext()
and new MediaRecorder(stream, options)
so I want (need?) to understand all of this better before anything else re: audio/AI/TensorFlow.
But I haven't given up! I got TensorFlow to tell me whether or not I was coughing into a microphone in a separate exercise, I just need to figure out making the 'cough training' persist.
Deploying
A final note on deploying both the frontend and backend to Heroku - this post is my favorite article to follow. It's pretty standard, but this one is concise and direct. I didn't use yarn
, and I had no problems. I had tried Github pages and React before this, and did not like it, so I was surprised at how easy this was. If you are just looking for the fastest, easiest way to get your Rails backend, React frontend app online, I strongly recommend Heroku!
[EDIT] I now strongly recommend deploying the frontend to Netlify!! I still have my backend on Heroku, but my frontend/new racoon.digital domain is on Netlify. I just generally love how easy it is to use continuous deployment with Netlify and React. I've only had minor setbacks with it, either from some dumb, easily fixed mistake I had in the build, and/or from forgetting to add a _redirects
file for react-router
. See here for more in depth guide, but it really is straightforward. And so far, it definitely seems faster than Heroku.
Conclusion
I hope to add a video demo soon. That seems like a very key post-bootcamp thing to do for projects, but I hate the sound of my own voice so here we are. I hope that racoon.digital is good enough for now. This domain was surprisingly cheap - please let me know if I am missing something that makes this a terrible domain name lol! I just wanted something memorable and catchy for my LinkedIn, and honestly I like it, but I'm always open to feedback here.
Thanks for reading!