My Experiences Working in Production
Eight Practices in React That Will Crash Your App in the Future
Take care of your users, and they will take care of you
Edit: Point #2 of this post has been revised to be more understandable (and creepier) in a reader’s perspective. Thank you to the user on dev.to who emailed me about the previous confusion!
A lot of us have fallen in love with the React library for several reasons. It can be incredibly painless to create complex interactive user interfaces. The greatest feature is being able to compose components right on top of one another without breaking other composed components.
If you’re currently building an application using React or thinking of its use for upcoming projects, then this tutorial is for you. I hope to help you on your journey, too, in making great react applications by exposing a few code implementations that you ought to think twice about.
Without further ado, here are Eight Practices in React That Will Crash Your App in the Future:
1. Declaring default parameters over null
I mentioned this topic in an earlier article, but this is one of those creepy gotchas that can fool a careless developer on a gloomy Friday! After all, apps crashing is not a joke — any type of crash can result in money loss at any point if not handled correctly.
I was once guilty of spending excess time debugging something similar to this:
Inside our app component, if dates end up being
falsey, it will be initialized with null.
If you’re like me, instincts tell us that
items should be initialized to an empty array by default when it was a
falsey value. But our app will crash when
items is null. What?
Default function parameters allow named parameters to become initialized with default values if no value or undefined is passed!
In our case, even though null is
falsey, it’s still a value!
So the next time you set a default value to null, just make sure to think twice beforehand. You can just initialize a value to an empty array when that is the expected type of the value.
2. Grabbing properties with square brackets
Sometimes the way properties are being grabbed may influence the behavior of the app. If you’re wondering what that behavior is, it’s the app crashing. Here is an example of performing object look ups with square brackets:
These are actually 100% valid use cases and there’s nothing really wrong with them besides being slower than object key look ups.
Anyhow, the real problem starts to creep up on your app when an unintentional issue occurs, like a tiny typo:
If you or one of your teammates were implementing some enhancement to this snippet and made a minor mistake (such as capitalizing the J in joe), the result will immediately return undefined, and a crash will occur:
The creepy part is, the app will not crash until a part of your code attempts to do a property lookup with that undefined value!
So in the mean time, joesProfile (undefined in disguise) will be passed around your app and no one will be able to know that this silent undefined value is creeping around until a piece of a code performs some property lookup, like joesProfile.age, because joesProfile is
What some developers do to avoid a crash is to initialize some default valid return value if a lookup ends up becoming unsuccessful:
At least now the app won’t crash. The moral of the story is, always handle an invalid lookup case when you’re applying look ups with square bracket notation!
For some, it might be a little hard to explain the severity of this practice without a real world example. So I’m going to bring up a real world example. The code example I am about to show you was taken from a repository that dates 8 months back from today. To protect some of the privacy that this code originated from, I renamed almost every variable but the code design, syntax and architecture stayed exactly the same:
fixVideoTypeNaming is a function that will extract the video type based on the value passed in as arguments. If the argument is a video object, it will extract the video type from the
.videoType property. If it is a string, then the caller passed in the
videoType so we can skip the first step. Someone has found that the
videoType .mp4property had been misspelled in several areas of the app. For a quick temporary fix around the issue, fixVideoTypeNaming was used to patch that typo.
Now as some of you might have guessed, the app was built with Redux — hence the syntax.
And to use these selectors, you would import them to use in a connect higher order component to attach a component to listen to that slice of the state.
The component receives all of the props that the HOC passes to it and displays information following the conditions adapting from the data given from the props. In a perfect world, this would be fine. In a non-perfect world, this would temporarily be fine.
If we go back to the container and look at the way the selectors are selecting their values, we actually might have planted a ticking timebomb waiting for an opportunity to explode:
When developing any sort of application, common practices to ensure higher level of confidence and diminishing bugs during the development flow include implementing tests in-between to ensure that the application works as intended.
In the case of these code snippets, however, if they aren’t tested, and they aren’t handled early, the app will crash in the future.
state.app.media.video.videoType is four levels deep in the chain. What if another developer accidentally made a mistake when he was asked to fix another part of the app and
state.app.media.video becomes undefined? The app will crash because it can’t read the property
videoType of undefined.
In addition, if there was another typo issue with a
fixVideoTypeNaming isn’t updated to accommodate that along with the mp3 issue, the app risks another unintentional crash that no one would have been able to detect unless a real user comes across the issue. And by that time, it would be too late.
And it’s never a good practice to assume that the app will never ever come across bugs like these. Please be careful!
3. Carelessly checking empty objects when rendering
Something I used to do long ago in the golden days when conditionally rendering components was to check if data had been populated in objects using
Object.keys. And if there were data, then the component would continue to render if the condition passes:
Let's pretend that we called some API and received
items as an object somewhere in the response. With that said, this may seem perfectly fine at first. The expected type of
items is an object, so it would be perfectly fine to use
Object.keys with it. After all, we did initialize
items to an empty object as a defense mechanism if a bug to ever appeared that turned it into a
But we shouldn’t trust the server to always return the same structure. What if
items became an array in the future?
Object.keys(items) would not crash but would return a weird output like
["0", "1", "2"]. How do you think the components being rendered with that data will react?
But that’s not even the worst part. The worst part in the snippet is that if
items is received as a null value in the props, then
items will not even be initiated to the default value you provided!
And then your app will crash before it begins to do anything else:
Again, please be careful!
4. Carelessly checking if arrays exist before rendering
This can be a very similar situation to #3, but arrays and objects are quite often used interchangeably, so they deserve their own sections.
If you have a habit of doing this:
Then make sure you at least have unit tests to keep your eyes on that code at all times or handle
arr correctly early on before passing it to the render method, or else the app will crash if
arr becomes an object literal. Of course, the
&& operator will consider it as
truthy and attempt to
.map the object literal which will end up crashing the entire app.
So please keep this in mind. Save your energy and frustrations for bigger problems that deserve more of your special attention! ;)
5. Not using a linter
If you aren’t using any type of linter while you’re developing apps, or you simply don’t know what they are, allow me to elaborate a little about why they are useful in development.
This tool is so useful that it can act as your semi-mentor as it helps correct your mistakes in real time — as if someone is guiding you. It even describes why your code can be bad and suggests what you should do instead!
Here’s an example:
The coolest thing about ESLint is that if you don’t like certain rules or don’t agree with some of them, you can simply disable certain ones so that they no longer show up as linting warnings/errors as you’re developing. Whatever makes you happy, right?
6. Destructuring when rendering lists
I’ve seen this happen to several people in the past, and it isn’t always an easy bug to detect. Basically, when you have a list of items, and you’re going to render a bunch of components for each one in the list, the bug that can creep up on your app in the future happens when one of the items in the list is not a value you expect it to be. Your app may crash if it doesn’t know how to handle the value type.
Here’s an example:
The code would work perfectly fine. Now, let’s look at the API call. And instead of returning this —
— what if somehow there was an issue with how the data flow was handled when an unexpected condition occurred in the API client and returned this array instead?
Your app will crash because it doesn’t know how to handle that:
Uncaught TypeError: Cannot read property 'name' of undefined
at eval (DataList.js? [sm]:65)
at Array.map (<anonymous>)
at DataList (DataList.js? [sm]:64)
at renderWithHooks (react-dom.development.js:12938)
at updateFunctionComponent (react-dom.development.js:14627)
So to prevent your app from crashing, instead, you can set a default object on each iteration:
And now your users won’t have to make judgments about your technology and expertise because they won’t see a page crashing in front of them:
However, even though the app no longer crashes, I recommend going further and handling the missing values like returning null for entire items that have similar issues instead, since there isn’t any data in them anyway.
7. Not researching enough about what you’re going to implement
In the past, I’ve made the crucial mistake of being overly confident with a search input I had implemented, trusting my opinions too early in the game.
What do I mean by this? Well, it’s not the search input component that I was overly confident with. The component should have been an easy task — and it was.
The real culprit of the issue occurring with the whole search functionality was including characters in the queries.
When we’re sending keywords as queries to a search API, it’s not always sufficient to think that every key the user types is valid, even though they’re on the keyboard for that reason.
Just be 100% sure that a regex like this works exactly as intended and avoids leaving out any invalid characters that can crash your app:
That example is the most up-to-date, established regular expression for a search API.
Here is what it was before:
As you can see the slash
/ is missing, and that was causing the app to crash! if that character ends up being sent to an API over the wire, guess what the API thinks the URL is going to be?
Also, I wouldn’t put 100% of my trust in the examples you find on the internet. A lot of them aren’t fully tested solutions, and there isn’t really a standard for the majority of use cases when it comes to regular expressions.
8. Not restricting the sizes of file inputs
Restricting the sizes of files that users select is a good practice because most of the time you don’t really need a ridiculously large file when it can be compressed in some way without losing any noticeable signs of quality loss.
But there’s a more important reason why restricting sizes to a certain limit is a good practice. At my company, we’ve noticed users in the past occasionally get “frozen” while their images are being uploaded. Not everyone owns an Alienware 17 R5, so you must take certain circumstances of your users into consideration.
Here’s an example of restricting files to a limit of 5 MB (5,000,000 bytes):
You wouldn’t want users to be uploading video games when they’re supposed to be uploading documents!
And that concludes the end of this post!
There will be a part two as I’ve only gotten through half of my list — yikes!
Thank you for reading and make sure to follow me for future updates! Happy 4th of July!