Tips to help you know if the code is good or not… not so good
I have coached junior developers and got to know firsthand about the challenges they usually face. That’s why I’ve put together a list of common mistakes that can be helpful for juniors or even those with a little more experience who want to advance their knowledge!
1. Failing To Pass a Valid Key to Element Arrays2. Mutating the State3. Ending Up With Huge Components
okay let’s go.
You may have heard about it already: when mapping an array to a single React element, you must assign a unique key to each child.
But why is it so? And is it always like this? To answer this, we must first briefly talk about how React works under the hood.
Whenever a state or props update occurs in your app, React runs its own algorithm, which compares the old DOM tree with the new one. In this way it learns what changed and, ultimately, what needs to be reintroduced.
This means that it goes through each node and checks if a parameter has changed after the update, then the same process iterates over its children. Now let’s say we have those two trees:
As you can see, the update added a node at the bottom of the children: that’s fine! React handles this well because it can understand that the first two nodes did not change, and a third was new. However, let’s take a look at a different scenario:
Here comes the trouble: Update added a new node on top of the children. When React compares each child sequentially, it never finds a match. This means that it will also recreate the two children that did not change.
In this case, the children are just DOM elements, so you could say it’s not a big deal, but it can be too much of a performance issue when there’s a tree under each child, and iterating them all. It takes a lot to make. Or when there are many children to recreate.
Not only this! Since React cannot uniquely identify each child, there are some cases where it can mess things up and mix up the state of the children. here is one Example You can see: first type something in the input and then “Add new to start.”
So this is where the keys come in handy. let’s take a look:
Great. With keys, React can now identify the current children, who are second and third, and know that the first child is late to the party. It will not recreate existing children as it can match them correctly.
But what if I use the index as a key? Well, React does this by default. It will go with the index of the array unless you provide a true unique key. It will still warn you about the issue, so you can handle it right away. So, why is index not a valid key? Let’s look at an example:
As you can see, the first child is not recognized as the new one because the key matches. However, since its contents have changed, React will recreate the node. Same goes for the other kids. In addition, you may face the same state mixing issues we discussed earlier.
There is one exception, however, where using the index should be fine, and that is when you are sure that the children will not be reordered. Still, I suggest you bind it to a unique ID somehow and choose to use the index as a last resort.
Oh, that was a lot. Let’s move on to the next one!
Again, I’ll explain why and how on this as well. The explanation of the previous point gives us a head start.
As stated before, the app’s render props and state are triggered by updates. But how does React know when a state is updated? That’s our job! Every time we set a state via the intended API, React knows. For example:
As long as you need to handle primitive data, things are pretty simple. However, they can be messy when you have to deal with non-primitive data, and this is where I often see mistakes being made. Take a look at this:
What’s wrong here? we know we should use
setSandwich() Tasks for the response to trigger the rerendering. But that’s not enough. We also have to make sure we don’t change the state so that the old and new versions can be compared (remember different?) A React state is completely immutable. This means you have to overwrite its value every time you want to change it.
The reaction would not see the change in this case because the mutation caused the two versions to be identical. Let’s say you display the sandwich content in a div: it will not be updated. That’s why you’ll still see onions rendered (yuck!).
So how can we fix it? Let’s look at the correct form given below:
It’s a very different thing. We set up a new object that brings along the values of the previous state (note how we get this from the setSandwich callback) but also overrides the onion property. Rendering will now work correctly as we instantiate a new object to set, leaving the old position untouched.
Ok, let’s look at an example on arrays:
Same story: Changing the array prevents React from noticing its changes. Now let’s do it the right way:
Same principle: we set up a new instantiated array and leave the old one untouched. We are not done yet! For those of you great people who love TypeScript, I have a trick to help you avoid mistakes in the future.
As you can see, TypeScript comes with a handy
ReadonlyArray type which omits all the mutable properties we want to avoid. writing now
push() Will throw a TSLint error! there is also
ReadonlySet Types that you can use in the same fashion.
You see it all the time. You need to make a small change. You open the target component, and 500 lines of code get in your face. 80% of your effort was spent figuring out what you were looking for while trying not to break anything else.
I’ll give you some tips on how to fix this and organize your components. First, take your time. Think of it as writing an essay. Mind your own business; You write down everything. Then, when you’re done, have you re-read it and check for grammar mistakes and what not.
So when you start writing your component, don’t try to optimize the code right off the bat because you’ll be redoing it a hundred times later.
Once you think the component is pretty much ready, you can take a look at what you have and consider what could be improved. Here’s what you’re looking for:
separation of concerns. This is always a good rule of thumb. A component shouldn’t feel like it’s doing too many things. Does your component use a lot of hooks? Is your JSX more than a few hundred rows? Then you’ll probably have to break things.
Try to visually break it down into smaller parts. You should ask yourself, “Is this from here?” or “should my component care to know that?”. There isn’t one right way to go about it, so do what feels best to you and take it easy. Once that’s done, go ahead and break the code down into smaller components.
Leveraging Hook. When I join a component, my main focus will be on what is returned. ie jsx. This is the main purpose of a component, to render some kind of UI.
Usually, however, to achieve the required UI, we have to manage the data coming from the backend with local storage, state and UI related logic (forms, navigation, styling, animations), and what not.
But we still have to keep our main focus on JSX, right? My next suggestion is to keep everything else inside your component as minimal as possible.
Got any sort of logic that probably involves quite a bit of state management? Extrapolate or give everything related in a custom hook formic a try.
When managing complex state structures, you might want to consider
useState(), This is another way you can extrapolate your state management and give you other benefits.
So you got the idea. I often enjoy making hooks or using something else because you can be creative and do anything, all while keeping your components neat and tidy.
What I’m going to do now is give you a realistic example of this process, applying most of the tips I just gave you:
So this is the page of my fantasy pizzeria. You can choose a pizza and drink through the form, then submit your order. fancy, huh?
Now keep in mind this is just an example. So the component is not that long or complicated, but we will still apply our principles just for demonstration. As you can see, we also have a header and footer coded in with the form, so we could definitely use some refactoring.
Also, both the form logic and the fetch request are written in the component, so we can improve that as well.
Let’s see the result:
Here’s what I did: First, I moved the header, form, and footer into three separate components. Speaking of the form, the props you see allow it to speak with the parent component as before. So this was the easy part. Now let’s take a look at the new hook.
I’ve exported all the arguments to two separate hooks:
useForm() to manage the state and event of the form, and
useApi() to submit the form and post it. Here’s what they look like:
That’s why I’ve put aside logic after separation of concern theory. Other than that, the code is almost the same as before so it wasn’t even an expensive refactoring task.
Alright guys, I guess we can wrap this up. I hope it wasn’t too boring and it taught you something new. Soon I will be writing an article dedicated to React Hooks, should you be interested in that?
Thanks for reading, stay tuned for more. See you.
#Reaction #Deep #Dive #Pitfalls #Developers #Fall #Alan #Azam #August