4 Common Patterns You Can Easily Focus On In Your React Code Reviews
October 18, 2021 - 7 minutes
In React development, there are a lot of different design patterns that you can use. Part of developing yourself into a better React developer is learning about them, understanding their use cases, and being able to apply them in the appropriate situations. For this reason, there are a lot of resources in the field that will teach you how to implement them.
However, your involvement with these design patterns as a React developer is not only limited to implementing them. If you’re working in an engineering team, your responsibilities will more often than not also include a lot of reviews. Having to go through the React work of your colleagues, understanding it, and providing feedback on it.
This means you’ll have to understand patterns that were implemented by your colleagues rather than yourself or provide feedback using the knowledge that you have regarding those patterns. However, being able to do this to provide proper reviews on React pull requests requires a totally different skillset and understanding of those same patterns.
To apply a React pattern, you mostly need to know how to implement them properly and when it makes sense. But to apply React patterns in your reviews, you’ll first need to identify the current pattern or construction implemented by your colleague. Then, you’ll have to identify whether the current implementation has limitations or flaws. If so, then you’ll have to determine whether there’s an alternative pattern to fix those issues and make sure it can be applied in the current situation. Lastly, you’ll need to either explain to your colleagues how to implement that pattern or help them with it.
This whole process is not trivial, but can tremendously increase the quality of your reviews when done properly. To help you, this article will cover common React patterns that you can easily use when reviewing React pull requests. This information will help you become a better React reviewer and provide more meaningful reviews to your colleagues.
Prop Drilling
One of the most commonly discussed React patterns is prop drilling. This is when a specific component is nested in a larger components tree and needs certain prop values, but the prop information is only available several levels higher in the tree.
A common (anti-)pattern to solve this issue is to pass the value from the parent component all the way to the child component that needs it. While this works, it means that all the components in between also have to accept that prop. Not because they need it themselves, but because they need to pass it down the components chain, which is suboptimal props API design.
When reviewing, it’s relatively easy to spot occurrences of prop drilling. Most of the time, reviewing is performed on platforms in the browsers, like Github and Gitlab. However, reviewing in the browsers only has very limited IDE support. This means that navigating between files requires even more effort, which is exactly what prop drilling enforces you to do.
If you’re constantly going between files and being navigated all over the place while trying to understand the code, then it’s very likely that you’re dealing with an occurrence of prop drilling. When that’s the case, there are several different methods that you can propose to solve the prop drilling issues. Based on the scenario, these include but aren’t limited to using the composition pattern, React context, or compound components.
Multiple Boolean States
With the move from class components to functional components with hooks in React, the way we handled state had also shifted. Instead of having one object where all the state properties are tracked, every piece of state is now tracked individually in their own useState
hook.
While this leads to a better separation of state flows, it also has its pitfalls. One of those pitfalls is always tending towards useState
whenever a new piece of information is added to the component without any second thoughts. This can lead to unnecessary states, additional complexity, and states that are unreachable. These issues are especially prominent when dealing with boolean states.
const Component = () => {
const [isInitialized, setIsInitialized] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
// ...
}
A common example is a component that fetches some data and needs to handle the logic for retrieving that data. This almost always goes together with one variable to track whether the data is done loading and one variable to track whether the data request has failed. In certain scenarios, it’s also relevant to track whether the data request has already been initialized.
Due to the way this information is phrased and thought about, it’s very common to implement a useState
for each of them and capture it in a boolean state variable. The problem with these boolean states is that handling the data logic becomes increasingly more difficult.
Based on the state variables, the data request could be waiting for initialisation, already be loading, but also have errored. If you reason about these states from a realistic point of view, this scenario is impossible to occur. However, due to the way the state is implemented, these impossible and unreachable situations still need to be accounted for, implemented, and maintained in the code. This introduces additional code to the codebase that raises the complexity and increases the maintenance burden.
During reviews, this pattern comes to light more quickly. It’s indicated by multiple numbers of boolean states that are part of the same data flow or the logic flow. This is paired with a lot of combined null checking to verify them individually before they can be used in any meaningful way.
The most straightforward way to solving this problem is changing the implementation of the states to use a different data structure. Instead of several boolean variables, use one enumeration variable. Combining this with TypeScript will remove any unreachable state from having to be handled during development.
Component, Hook, And Props Naming
One of the most difficult tasks in software development is naming things. Unfortunately, in React development we have a lot of additional entities to be named. Think of the usual variables and functions, but also components, hooks, props, and much more. Proper naming of these entities leads to easier maintenance and better readability of the React code, while bad naming will make the code difficult to read, understand, and maintain.
On top of the additional entities, the reusable and composable nature of React development adds even more difficulty to this matter. In a normal React codebase, there are usually a lot of small components, each with its own hooks, variables, and props API. Keeping all the names consistent and clear across all those entities is a difficult task on its own.
Compared to development, it’s easier to have a proper bird’s-eye overview of the entire structure during a review. When reviewing the code, you’re reading all the code fresh and not already invested in it. Due to this, you can more easily grasp the contexts, connections, and consistency. This information allows you to more properly judge whether the naming of entities is done properly.
The most common mistake regarding naming components, hooks, and props that I’ve experienced is sacrificing clarity for length. Shorter names are easier to read and result in less code but aren’t always more clear. Especially with the enormous amounts of entities to name, it’s important to be as clear as possible. Therefore, it never hurts to include more information in the names, like elaborating on the context in which it should be used, what type it is, what it expects and returns, and more.
God Component
A very common anti-pattern in software development is the god entity. This is an entity, like a function, class, or object, that knows too much, does too much, and is responsible for too much. The problem with such a god entity is that different code flows become intertwined, their code becomes very tightly coupled, maintenance becomes more complex, and making changes to a certain part of the god entity can have unexpected consequences for other flows.
In React development, the equivalent would be a component that either receives a lot of data, is responsible for a lot of logic, renders most of the UI, or a combination of these. The same consequences that we discussed also apply to such a god component. Therefore, it’s better to avoid having such a component in the React codebase.
Several symptoms indicate that a particular component is a god component. Either it’s that a component receives several props that are out of proportion with the rest of the components in the codebase. In particular, these props are related to data, which indicates that a lot of data flows have to bypass this single component. Or the component implements a lot of React hooks, either custom or React ones, and a lot of the logic is handled inside that component. Or a very large portion of the resulting HTML structure is rendered in a single place.
When reviewing React code, these symptoms become significantly more apparent due to the lack of IDE support in the browser. You’re forced to read through the code in a different way than you’re used to, which makes you focus on different aspects. The size of the component and the size of the component’s props API are part of those aspects and are indicative of a god component.
Final Thoughts
Design patterns are very common in React development. Part of being a React developer is knowing, understanding, and being able to apply them in the appropriate situations. Besides development, React patterns can also be applied during code reviews. This will tremendously increase the quality of those reviews. However, doing so isn’t a trivial task and requires a different understanding of those patterns.
To help you with that, this article covered several common React patterns that you can easily integrate into your React pull requests reviews. These involve prop drilling, multiple boolean states, the naming of components, hooks, and props, and a god component. The provided information will help you identify them, integrate them into your reviews, and elevate the quality of your React reviews.