Disclaimer: This article is an astute attempt to hide that I'm a ReactJS noob, so bear with me if I fumble something unconventional and help me correct myself.
We are going to discuss a few trivial things in ReactJS, the rationale behind writing this article is to collate an ever-growing list of things/temptations/misconceptions that I myself experience while I grow in ReactJS as it's what I've been coding primarily for a few months already. That's why we have "N" in the title because I am myself unaware of how many temptations there could be.
Note: While the ideas here are represented from React's point of view, a few of them may apply across frameworks like VueJS, etc.
Misconception No. 1: Keys in lists should be globally unique.
The story: At work, we had a bunch of lists rendering on the same page, due to my inexperience with what I was working I thought that a key needs to be unique globally at least for a page and to cope up with this notion I used to append some unique string at the end of every list item. At all places in the application. While appending is a good convention, it's not necessary and things get boring pretty quickly.
The Truth: The key only has to be unique among its siblings, not globally unique. Read More
Which compiles down to the below image ๐
So worry not about using the same keys inside lists across your application, just make sure that no sibling list item has a duplicate key, that's it.
Temptation No. 2: Usage of Math.random() to generate a key for list item.
The story: When I had to show up to a lot of lists on our NextJS application, the singular idea of what key should I use haunted me. And soon enough I found myself using something like
key={ Math.random() }
to avoid some decision-making overhead. But doing this in a production app may cause a lot of additional computation and bugs that would become difficult to debug.
The Truth: Every time the component re-renders, Math.random()
will generate a new random number as the key for our list item. And thus we lose on the performance side(precisely react's celebrated "Diffing Algorithm"), as react will have to remove the previous items and add new items after every (re)render.
Temptation No. 3: Usage of item index as the key for list items.
The story: It's very dominant in the react world that we make use of someArray.map((item, index) => <li key={index}>{item}</li>)
to generate some JSX recursively that we'll then render at someplace in our application. But very often is the case where we want to filter/sort or do some other computation on our list which can thus cause the index of items to change, which may then cause certain side effects. I personally had this buggy nuisance going around in an application where I was paginating through a bunch of items, which led to serious code malfunction.
Temptation No. 4: Mutating local state directly in the most non-obvious manner.
Yeah I know, this is the first thing that you learn how to overcome in react, but there's a way to look at this issue that could help you avoid bugs down the line.
Let's actualize the issue by putting together an example:
/* Some data fetched from an API and stored as a local state to out react component */
[
{ id: 1,
shopName: 'Go Bananas',
open: true
},
{ id: 2,
shopName: 'Valenta Pucci',
open: false
}
]
Nothing fancy here, just a list of objects representing the state of a shop.
Based on whether the shop is open or closed, which is determined by true/false flag we'll want to render the shop's list.
Essentially at some point, we want the allow the shop-owner/site-admin to toggle the status of the shop, which will want us to iterate through the current list find the shop we're interested in, and then update it. We don't want to mutate the previous state or its keys accidentally, as it can be problematic and leave the application indeterministic.
Conventionally, I started doing this ๐ (which is problematic to my astonishment, but looks otherwise)
You may assume that the method below is attached to a switch through an event handler so that each time it is toggled the state updates.
onStatusChange = (shopId) => {
const newStatus = this.state.shops.map((shop) => {
if(shop.id === shopId) {
shop.status = !shop.status /* Problematic line of code */
}
});
this.setState({shops: newStatus})
}
You may sigh in relief after doing this as you should, but wait for a moment. It's true that the .map()
higher-order function does not mutate the original array of shops thus our state.
But internally the values we get to access inside of the .map()
function are directly referenced to the values inside our state. As a consequence, we end up mutating values which can lead to a lot of issues as the app grows.
The remedy?
Make use of Object.assign() method, Read More.
Basically, what Object.assign()
does here is, properties in the target object are overwritten by properties in the sources if they have the same key. And Later sources' properties overwrite earlier ones.
This trickery saves us from some future headaches.
onStatusChange = (shopId) => {
const newStatus = this.state.shops.map((shop) => {
if(shop.id === shopId) {
return Object.assign({}, shop, {open: !shop.open})
}
});
this.setState({shops: newStatus})
}
Thanks for strolling your way down, this article will keep growing as long as I work with ReactJS and serve as a single point of reference. As far as updates are concerned latest additions to this list will have a separate article of their own.
If there's something you'll want to add, you're welcome to do so in the comments section. I'll make sure that I verify suggestions and add them to this list with proper credits.