One of the most important aspects of code is its readability. Easily readable code is the result of well-written code, and it has a lot of long-term benefits. It will be easier to read, understand, maintain, review, less prone to unexpected errors, and generally make the lives of other developers a lot easier when they have to interact with that code.
The difficulty of code readability is especially prominent in React development due to its composite nature. The resulting code is filled with a lot of code patterns, very fragmented, and generally distributed over multiple places. This further increases the difficulty of writing readable React code.
However, writing readable React code is not an impossible task. Doing so starts with making the entire process a conscious one. For that, it’s important to know what aspects to focus on.
To help you with this, this article will cover multiple topics that you should consider when writing more readable code and its impact on readability, including:
Hopefully, this information will provide you with a solid foundation on how to write more readable React code right now, and in the future.
When discussing code readability, the topic that is most commonly mentioned is the length of the code. Shorter code, in both the vertical and horizontal directions, is often associated with being more readable. The main reason for this is that shorter code equals less code for developers to read through. This results in fewer opportunities that can confuse, which would otherwise make it harder for developers to read through the code.
In reality, however, it’s not that clearly differentiated. While writing less code can contribute a lot to readability, it’s not an ultimate guarantee. There also comes a turning point where shortening the code even further turns it from being beneficial to harmful for the readability.
When pushing for shorter code with the assumption that it’s beneficial for the code readability, the other aspect that is often sacrificed is explicitness.
Take the concept of inline conditional rendering, where it’s often between the AND and the ternary operator.
const Component = ({ linkUrl }) => ( <div> { !!linkUrl && <PrettyLink url={linkUrl} /> } </div> } // -- OR -- const Component = ({ linkUrl }) => { return ( <div> {linkUrl !== undefined && linkUrl !== null ? ( <PrettyLink url={linkUrl} /> ) : null} </div> ); };
The former is considered shorter and more concise, while the latter is considered lengthy and only appropriate when both branches of the condition are necessary.
But using the && operator
means that one branch is not explicitly stated, so it’s up to the reader to figure out what the expected behavior is for the other branch (even if it’s to render nothing), whether it was left out by mistake, and look for information that is not provided to them.
This is a clear sacrifice of explicitness for the sake of saving on code length. Whether this is more readable depends on the scenario, but it isn’t always as straightforward as “the shorter the better”.
One of the reasons we create custom components, Hooks, and functions in React is because it groups related code. Instead of scattering code all over the place, it packages everything in one location under a certain context or purpose.
In the same fashion, the distance at which similar code is grouped also plays a role in the readability.
One of the biggest examples of this occurrence in React development is the introduction of React Hooks. Before Hooks, the only way to include logic with React components was through using class components. To do so, we had to implement lifecycle methods and put pieces of logic in the appropriate places.
Unfortunately, these lifecycle methods were scattered across the component and, in certain cases, were written in a specific order — your logic was broken up and distributed across the component. This increased the distance between related code blocks and often made it hard to see and understand the logic flow.
With the introduction of Hooks, we didn’t only receive a way to reuse logic across multiple components, but also a way to group all the related code closely together. This reduced the distance at which similar code is grouped.
This is an important factor for code readability and maintainability, and thus should be kept in mind whenever possible.
In the end, a major part of React development is JavaScript. Implementing React components, logic, Hooks, and more is all done in JavaScript, which means that all of JavaScript can be used for it. That can be both an advantage and a drawback.
As a programming language, JavaScript is very extensive and allows for a lot of different implementations. But a major drawback to such an extensive language is that not everyone will be similarly familiar with all the language details.
Many language features in JavaScript are based on intrinsic details or implicit behavior, which compounds its complexity when coupled with its dynamic nature. These two factors make certain JavaScript constructions more complicated to understand and can negatively impact the readability of your code based on how familiar your developers are with them.
Let’s discuss a few common example JavaScript constructions that I’ve noticed are more difficult to understand. For all of these constructions, understanding the implicit concepts behind them is crucial for understanding the construction itself. Not having that information can significantly negatively affect readability.
While it’s likely that most React developers will be aware of these constructions, it’s not a given guarantee, and thus something to keep in mind.
Array.reduce
function for data conversionconst idObjects = ids.reduce((prev, curr) => { return { ...prev, [curr]: { id: curr, value: getValueFromId(id), } }; }, {});
The Array.reduce
function is often used to convert an array into a different data structure, like an object. The code is very compact, but it’s also often difficult to understand — there’s a lot of details to keep track of:
reduce
callThe order of this information is also unnatural, like the initial structure being defined last. A different structure that improves upon this is the for-loop. Although it’s considered more ugly and verbose, the resulting code is often more readable due to the more straightforward order of information:
&&
operator for conditional renderingconst Component = ({ hasImage }) => { // ... return ( <div> {hasImage && <Image />} </div> ); }
A very commonly used construction for inline conditional rendering is the &&
operator. Based on the value of the left-hand side operand, the right-hand side operand might be rendered.
However, this construction only works due to the implicit JavaScript behavior called short-circuiting. When the &&
expression is evaluated and the left-hand side operator evaluates to a falsy value, then that operand is returned and the evaluation of the right-hand side operand is entirely skipped.
Any given web application will have to deal with all types of information flowing around. Together with the ever-increasing complexity of web applications, it’s also never about handling just one data or logic flow. Any UI will have a dozen, hundred, or even a thousand smaller pieces. Every single piece will be connected to some kind of information and have multiple flows going through them.
React provides us with a lot of tools to implement data and logic flows. Think of out-of-the-box Hooks like useState
, useReducer
, useEffect
, and useLayoutEffect
, and the ability to reuse logic in the form of custom Hooks. While these tools allow React developers to handle flows very easily and effectively, they also have their drawbacks in certain scenarios.
It’s very easy to entangle a lot of flows in a single location because of how straightforward it is to implement flows into your components. Multiple flows going through a single component or combining pieces of logic from multiple flows into a single useEffect
Hook is not an uncommon occurrence.
const Component = ({ data }) => { // Logic... // Here, we're combining flows for the data request, empty state, filled logic state, and // server error feedback into a single `useEffect`. It feels natural, but is it readable? useEffect(() => { if (!data) { setRequestState("LOADING"); } else if (data.length === 0) { setRequestState("DONE"); triggerEmptyState(); } else { setRequestState("DONE"); if (dataIsValid(data)) { updateOtherLogicWithData(data); } else { informServerDataIsInvalid(); } } }, [data, updateOtherLogicWithData, informServerDataIsInvalid, triggerEmptyState]); // Render... }
The problem with combining piece of logic from multiple flows into a single useEffect
Hook like this is that it can negatively influence code readability. Putting different flows closely together will make them intertwined, difficult to separate, and tightly coupled. The resulting code will thus become more difficult to understand and harder to maintain.
In general, one of the most difficult things in software development is naming things. Proper names can make or break the readability of code. React development is no exception. But due to the composite nature of React, there are a lot of entities to be named. Hooks, components, functions, variables, props, callbacks, contexts — and the list goes on.
Together with the focus on reusability, their names cannot be so specific that they seemingly prevent reusability, but also can’t be too generic because they should reflect their scope and context.
Properly naming them can bring you very far in writing more readable code. Not only does it benefit code readability, but it can also enhance the quality of the code and increase future maintainability. A few examples are:
valid
, consider naming it isValid
; instead of validate
, consider calling it onValidate
isLoading
and an isError
boolean prop
Avatar
, but if it’s specifically meant to be used in a section of a card, then it’s beneficial to make that explicit in the name and call it CardAvatar
onChange
prop, then naming that callback onChange
will not add any useful information to the reader
updateStateValue
increases the readability because it clarifies what the callback does and what will happen when the appropriate change event occurs in the used componentThese are concrete examples of how naming variables differently can change the readability and quality of React code. But it’s not only limited to these examples — the most important thing is to keep this topic in mind when writing, consider the quality and specificity of your naming style, and potentially improve upon it.
There are a lot of different types of code flying around in React development — CSS, JS, HTML (or JSX) — and because of this, a lot of code is located in a single location. Especially in such a UI-centered development field, it means that there will be many scenarios where the code has either minor similarities or differences.
Properly highlighting these occurrences can make a world of difference for the readability of the code. Consider the following:
const SomeSection = ({ isEditable, value }) => { if (isEditable) { return ( <OuterSection> <Header>Edit this content</Header> <Body>{value}</Body> <SectionButton>Clear content</SectionButton> </OuterSection> ); } return ( <OuterSection> <Header>Read this content</Header> <Body>{value}</Body> </OuterSection> ); } // -- OR -- const SomeSection = ({ isEditable, value }) => { return ( <OuterSection> <Header> { isEditable ? "Edit this content" : "Read this content"} </Header> <Body>{value}</Body> { isEditable ? <SectionButton>Clear content</SectionButton> : null } </OuterSection> ); }
When these occurrences are highlighted, you can more easily see how certain flows, branches, or results are related to each other, which parts are connected, which parts are different, and so on.
If the approach you use to do this isn’t thoughtful, it can result in duplicate code, or code that requires a higher cognitive load — meaning more things to keep track of, disconnected flows, and harder to understand code.
Writing readable code is one of the most important aspects of software development, and it isn’t a trivial task. Its difficulty is especially prominent in React development because of its composite, fragmented, and distributed nature. There is a lot more code and factors to consider when dealing with code readability, which can be overwhelming and make it very difficult.
In this article, I went over various React code aspects to consider when writing more readable code. These include the length of the code, how closely related blocks of code are grouped, whether complicated JavaScript constructions are used, how many flows are handled at once, the naming of your variables and entities, and the degree to which similarities or differences are highlighted. For each topic, we went over why they matter, their impact on the code readability, and how their impact can be controlled.
Using this information, you should have a solid foundation on what aspects to consider and how to write more readable React code. Not only will this have an immediate impact on the readability of your code, but also on its reviewability and long-term maintainability.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "How to write more readable React code"
Not sure I agree with a for loop over reduce.
First I don’t think the list for things you have to keep in mind is correct. Why are you thinking about the previous value? If you’re doing that then you’re thinking about things being combined in a linear fashion which isn’t great. You should be thinking with pure functions when using functional methods.
Also with the order – you already have the lengths and the limits. It’s the length of the array. It’s baked in. You get that for free. So the only thing with order is declaring the initial state. That’s also easy, just declare the variable first like you would with a for loop and pass it in if that’s the issue. But there is also a bonus with reduce – it takes a function. That means you can move that function out and just use a name. `ids.reduce(addItemToObject, {})`.
The issue with a for loop is it can do anything. With reduce you are already hinting at what you want to do (i.e. reduce an array). The other factor that comes in is that with reduce you are expected to use a pure function (if you’re not then that’s a whole other issue). That takes a lot of context out of the equation, whereas a for loop can be using context from anywhere. So the only thing that is weird about it is the order.