Logo
blank Skip to main content

Common React Mistakes Developers Make During App Development

React (also called React.js) is a powerful JavaScript library for creating robust web applications. However, just like any other technology, it hides some pitfalls your developers might face. To ensure an efficient development process, it’s vital for developers to be aware of the most common issues when working with React.

In this article, we take a look at four interesting challenges related to coding in React. We show examples of common mistakes in React development that might occur when working with this framework and offer improvements.

This article will be helpful for team leaders whose development teams are working on web solutions and want to know how to avoid React mistakes.

Why choose React for web development?

As of 2022, React took second place on Statista’s list of the most used web frameworks among developers worldwide, with 42.62% of respondents reporting that they used it. (Node.js was used by 47.12% of respondents). Such popularity can be explained by the various benefits React provides, especially for creating web applications.

Organizations choose React to build robust web solutions for three reasons:

  • Development speed. Your team can use individual application components on both the client side and server side. Thus, your developers can create individual components and introduce changes without affecting the application’s logic. Also, React allows you to reuse components, which also saves time.
  • Flexibility. React code is easy to maintain. It has a modular structure that makes it flexible, which in turn saves time and money for businesses.
  • Performance. The core of the React framework offers a virtual DOM and server-side rendering. You can leverage these benefits to make complex applications run fast.
3 key benefits React provides for web development

An additional benefit of React is a strong community that regularly publishes tips and guides on solving various challenges. This framework also has tons of packages and extensions that simplify and accelerate the development process as well as open more opportunities to enrich your products.

However, web development can be tricky, even if the tools you choose are easy to use and have clear documentation. So whatever programming languages and frameworks you use, it’s essential to leverage their potential to the fullest and minimize errors.

Let’s explore four categories of React challenges your developers can encounter when building web applications and discuss ways to address them.

Read also:
Knockout vs Vue vs React: Comparing Features and Performance

Challenge 1. Working with props

Components and properties (props) are key elements of the React framework:

  • Components represent functions that contain logic and return elements such as buttons and input boxes.
  • Props work as arguments that contain information such as usernames. Props help you pass data from one component to another.

Let’s discuss the most common issues that your developers might face when using props and discuss ways to avoid such mistakes during React development.

5 common props-related mistakes

1. Passing props to child components without using them

Sometimes developers need to pass props that include certain logic to child components without actually using props. The reasons vary: they could do it by accident, or they could change their mind about using particular logic and have to move it inside a child component.

No matter the reason, unused props pile up, making the code hard to read and making it challenging for other developers on the team to understand what to do next. Thus, your team will need to spend extra time whenever they analyze the existing code. Also, needless code can cause unpredictable bugs that are hard to get rid of, so it’s best to not write such code in the first place.

Here’s an example of React code with piled up props (left) and without them (right):

Code with too many unused props vs clean code
Screenshot 1. Code with too many unused props vs clean code

How to handle this:

  • Carefully review what props you use in your child components. 
  • Don’t pass to components props that you’re not going to use. To accelerate and automate this process, set up the no-unused-vars rule in ESLint.

2. Passing ref and key to a component as props

Experienced React developers know that words like ref and key are reserved in React and can’t be used as props. Here’s what the official React documentation says:

Most props on a JSX element are passed on to the component, however, there are two special props (ref and key) which are used by React, and are thus not forwarded to the component.”

However, there’s a chance that a developer will forget this rule and cause an error that looks like this:

An error occurs when ref is used as a prop
Screenshot 2. An error occurs when ref is used as a prop

Below, you can see the wrong and right ways of mentioning ref in React code:

Using ref as a prop vs using the forwardRef function
Screenshot 3. Using ref as a prop vs using the forwardRef function

How to handle this:

  • Don’t pass ref or key to a component as a prop.
  • Use forwardRef (as shown in Screenshot 3), or change the name of the prop.

Read also:
SaaS Development Lifecycle

3. Props drilling

Props drilling is a situation when developers pass the same props to every component one level down from its parent to the required component at the end. Thus, components become closely connected with each other and can’t be used without adding a particular prop. The fewer unnecessary interconnections between components in your code, the better.

Say you have a name prop that’s passed to the I component from the A component, as shown in the scheme below:

Name prop being passed through components

As you can see, the only component that needs the name prop is I. However, this prop is passed through the C and G components that don’t use it. It would be better to pass the name prop directly from A to I.

Let’s explore how both prop drilling and a more efficient example of prop passing work in practice. Below, you can see code with props drilling (left) and code without unnecessary dependencies (right):

Props drilling vs passing the prop directly to the component that uses it
Screenshot 4. Props drilling vs passing the prop directly to the component that uses it

How to handle this:

4. Using the spread operator from ES6 to pass props

Another type of mistake developers make when building React applications is related to using the spread operator.

The first thing you need to do to understand how a component works is to look at it and read it. React developers usually start exploring a component from the props it receives. It’s essential to keep the prop names concise and clear. Thus, the code will be easy to read, understand, and work with.

React developers often choose the Rest parameter and the Spread operator that come from ECMAScript 6 (ES6). However, their unreasonable use can make code hard to read and understand.

Below, you can see the difference in code volume when you use (left) or don’t use (right) the Spread operator:

Using the Spread operator vs making code easier to read
Screenshot 5. Using the Spread operator vs making code easier to read

How to handle this:

  • Use fewer Spread operators to pass props.

5. Using props from HOC

When you want to reuse the same logic, you can write Higher-Order Components (HOCs). This is especially convenient if such functionality uses React hooks like useEffect and useState to decrease the amount of copy-pasted code.

However, getting props from HOCs adds complexity to code. For instance, to understand where a certain prop comes from, you’ll have to scroll to the bottom of the file to see what’s wrapped in the HOC.

The more props a component receives from outside, the more difficult it is to track the logic of each prop. If a child component has props from the parent component and is wrapped in one or several HOCs, the code becomes even more challenging to read.

In such situations, you need to decide whether you need an HOC or if it’s better to use a custom hook or import this function from a different file.

For example, the creators of the Redux library smoothly got rid of HOCs in favor of hooks. Earlier, developers had to wrap components in the connect HOC and throw mapStateToProps and mapDispatchToProps into both the HOC and the wrapped component. Now, Redux provides the ability to use the useSelector and useDispatch hooks, which makes it easier to read components, allows you to write less code, and makes the code you do write easier to read.

Let’s see how code readability changes when using hooks (right) instead of HOCs (left):

Using HOCs vs hooks
Screenshot 6. Using HOCs vs hooks

How to handle this:

  • Use fewer HOCs and more hooks to improve code readability.

Related services

Custom Web Application Development Services & Solutions

Challenge 2. Using the useState hook

useState is a React hook that lets you add a state variable to your component. In React, the state is data or properties you can use in your application. State values can change, and you can use the useState hook to handle and manage your states. The useState hook allows you to create, track, and update a state in functional components.

However, using this hook can be tricky. Let’s discuss a few common React mistakes and ways to overcome them.

5 common mistakes when working with the useState hook

1. Using the useState hook extensively

Some developers might place everything they want to render in the useState hook, but this is a rookie mistake. The rule of thumb is to think first about whether the data you need to render will be changed. Otherwise, using the useState hook is redundant.

Say you have a setOceans variable that you don’t want to change and you need to display its data as a list. In this case, this is what your code might look like with the useState hook (left) and without it (right):

Using the useState hook vs only displaying data as a list
Screenshot 7. Using the useState hook vs only displaying data as a list

Moreover, if you use ESLint, it will show you the no-unused-vars warning for the setOceans variable.

Let’s explore another example. Say you need to make a simple form to gather feedback from users.

Creating a simple form for gathering user feedback
Screenshot 8. Creating a simple form for gathering user feedback

Note how many times the render log is displayed in DevTools. The first render comes out right when the component is rendered. The next five renders appear for each character typed into the input. This is okay, since React will re-render SomeComponent, passing the new text value to it.

In a situation where you don’t need to validate the form, you can change the controlled input to an uncontrolled input using the useRef hook:

Changing a controlled input to an uncontrolled input
Screenshot 9. Changing a controlled input to an uncontrolled input

Now you can see that the render is logged only once after the component is rendered.

As the useRef hook is synchronous, it updates the REF value immediately and persists its value through the component’s lifecycle, but it doesn’t trigger a re-render.

In this example, there is no division into good or bad code writing choices. But it’s important for your developers to remember that the useState hook does an additional re-render when the state changes.

How to handle this:

  • Use the useState hook only when absolutely necessary. Check out the official React documentation if you have doubts on whether to use this hook.

2. Creating multiple useState hooks for the same entity

Say you have three inputs that are part of the same common logic, making the code look like this:

Multiple useState hooks for the same entity
Screenshot 10. Multiple useState hooks for the same entity

In this case, you can easily reduce the number of useState hooks to one by adding the name attribute to the inputs. Thus, you’ll have one short function that will handle the state of the entire general logic:

Reducing the number of useState hooks to one
Screenshot 11. Reducing the number of useState hooks to one

How to handle this:

  • Combine common data into one useState hook to keep your code clean.

Read also:
What are Single-Page Apps (SPAs): Architecture and Development Benefits

3. Forgetting that the useState hook is asynchronous

Even with well-documented React APIs, some beginners forget that useState is an asynchronous hook.

Let’s explore an example with counters:

The counter inside log remains 0 after clicking the plus button
Screenshot 12. The counter inside log remains 0 after clicking the plus button

The first line in DevTools shows that the counter outside equals 0. This was the first render, and we passed the default value 0.

When we click the plus button, the counter inside the log remains 0. But why does this happen if the counter was increased in the previous line? The reason is that the useState hook waits for the component to be re-rendered before changing the state of a stateful value.

Let’s change the code a bit and pass the setCounter function as an argument to update the counter. The function will receive the previous value and return an updated value.

Passing a function to setState
Screenshot 13. Passing a function to setState

In this case, we see that the counter inside the log has the same value as prevCounter, and it changes the value only after being re-rendered.

How to handle this:

  • Remember that the useState hook is asynchronous.

Pass the setCounter function as an argument to the useState hook if you need to rely on the previous value.

4. Losing data after using setState

Sometimes, after updating the state of a component, some data may be lost. A possible reason for this might be using the setState method on class components. You might think that the useState hook will automatically merge and update the state as well, for example, but it won’t.

Say you have code where the user object has the email key as the default value, but then you erase it after introducing some changes.

Erasing the email key in the default value after introducing changes
Screenshot 14. Erasing the email key in the default value after introducing changes

Let’s pass an update function the first parameter of which returns the previous state of the user and therefore doesn’t delete the email key:

Passing a function that doesn’t delete the email key
Screenshot 15. Passing a function that doesn’t delete the email key

How to handle this:

  • Keep the previous state of unchanged states when working with complex states.

5. Missing a default value for the useState hook

As we know, true, false, null, and undefined are valid variables in React that can’t be rendered. However, there are some JavaScript methods you may use with them that can break your code.

Here’s an example of code where we expect that the userName stateful value will be some sort of a string. However, without a default value it’s undefined.

userName is undefined without a default value
Screenshot 16. userName is undefined without a default value

In such a situation, you need to add a default value to your code to solve this problem and reduce additional checks in the render:

Adding default values
Screenshot 17. Adding default values

How to handle this:

  • Add a default value to the useState hook.

Read also:
Building a Cross-Platform Mobile Web Application with Ionic and Angular

Challenge 3. Using the useEffect hook

The useEffect hook manages side effects in functional React components. A few examples of side effects are:

  • Fetching data
  • Directly updating the DOM
  • Using timer functions

Thus, useEffect helps you handle these side effects in what are otherwise pure React components.
Now let’s take a look at three common mistakes React developers make when working with the useEffect hook.

3 common mistakes when working with the useEffect hook

1. Using the useEffect hook inefficiently

Sometimes you need to show some data based on other data.

Say you want to display a particular period of time to and from certain dates. Using the useEffect hook in this situation will be redundant, as you can accomplish this task without it.

Instead of creating a new useState hook that would contain information about time period dates, let’s make a template literal with two variables.

Setting time period dates with the useEffect hook vs without it
Screenshot 18. Setting time period dates with the useEffect hook vs without it

How to handle this:

  • Make sure to use the useEffect hook only when absolutely necessary.

2. Creating an infinite loop with the useEffect hook

The useEffect hook accepts two parameters: effect callback and an array of dependencies. When omitted or incorrectly used, the array of dependencies can lead to an infinite loop.

infinite loop

Let’s explore an example of how an infinite loop occurs:

  1. Initial rendering. The useEffect hook executes the side effect callback that updates the state (setCounter).
  2. Re-rendering. The state (counter) update triggers re-rendering.
  3. Infinite loop. The useEffect hook executes the side effect callback (setCounter) and updates the state (counter), which triggers the re-rendering again, and does so ad infinitum.
An infinite loop
Screenshot 19. An infinite loop

Let’s explore this situation in detail and try to figure out how to manage and stop this endless loop.

For instance, we can try adding a simple start/stop button and the isCounterStarted check. Let’s check whether our counter will still work if the isCounterStarted variable is true:

Checking whether the counter will work if the isCounterStarted variable is true
Screenshot 20. Checking whether the counter will work if the isCounterStarted variable is true

It turns out that the counter doesn’t work because the useEffect hook doesn’t know that isCounterStarted has changed. Although the counter doesn’t work, there are no errors in the console and no infinite loop either.

Now, let’s add isCounterStarted to the array of dependencies:

Adding isCounterStarted to the array of dependencies
Screenshot 21. Adding isCounterStarted to the array of dependencies

The counter is running (increased by +1) and can be stopped with a button. The useEffect hook sees that isCounterStarted has changed, though there is an error in the console, and the loop is the same.

We need to find out what dependency makes a component re-render over and over again. Of course, it is the counter. Let’s remove it from the dependencies array:

Removing the counter from the dependencies array
Screenshot 22. Removing the counter from the dependencies array

Now, there’s no error in the console and no infinite loop either. The counter works, but only when the start button is pressed.

The bottom line here is that the correct way to avoid an infinite loop is to properly manage the array of dependencies. Control when the side effect callback should fire.

How to handle this:

3. Forgetting to use the cleanup function in the useEffect hook

There could be a situation when you need to know for sure whether your code has been cleaned up, such as when resetting data in a form or unsubscribing from events. To do this, it’s enough to return a cleanup function with the useEffect hook. It will also help you reduce memory leaks and avoid creating unpredictable bugs.

Let’s explore an example where we have two buttons that navigate between components. In the SomeComponentA component, inside the useEffect hook, we add the setInterval method that will run one second after the initial render.

As you can see in the example below, everything looks good: the counter is five, and the number of logs in DevTools is also five.

Logs in SomeComponentA
Screenshot 23. Logs in SomeComponentA

But when we go to the tab with the SomeComponentB component, which doesn’t have setInterval at all, we still see the same log, “This will run after 1 second”, called by the console.log command:

Logs in SomeComponentB
Screenshot 24. Logs in SomeComponentB

To fix this, we need to add clearInterval to the cleanup function. This is an optional cleanup mechanism for effects. Every effect may return a function that cleans up after it.

When SomeComponentA is unmounted, the logs from the interval stop being recorded.

Adding clearInterval to the cleanup function that’s returned from the useEffect hook
Screenshot 25. Adding clearInterval to the cleanup function that’s returned from the useEffect hook

How to handle this:

  • Don’t forget to cancel useEffect to avoid its side effects. The function that is returned from the useEffect hook fires when a component is destroyed.

Read also:
Traffic Spikes in Cloud-Based Applications: How to Mitigate Them on a Budget

Challenge 4. Common issues with JSX

JavaScript XML (JSX) is a syntax extension to JavaScript and the foundation for any markup that React will render in your application. JSX makes it easier to write React applications and allows React to show more useful error and warning messages.

Here are two major mistakes React developers make when creating applications using JSX:

2 common mistakes when working with JSX

1. Using large anonymous functions

Imagine your parent component has large pieces of code that you need to forward to child components. If you decide to use large anonymous functions, your code might become cluttered and hard to understand.

By creating separate named functions and moving them to the top, you can make the code cleaner and easier to read and use.

Using large anonymous functions vs creating separate named functions and moving them to the top
Screenshot 26. Using large anonymous functions vs creating separate named functions and moving them to the top

How to handle this:

  • Avoid using large anonymous functions as part of a prop.
  • Create separate functions and move them to the top to keep your code concise and clean.

2. Overusing embedded expressions

JSX provides React developers with numerous options to write clean and simple code. For example, the string inside curly braces in JSX is equal to plain text inside tags.

Thus, you can make your code easy to read and reduce the risk of introducing bugs.

String inside curly braces vs plain text inside tags
Screenshot 27. String inside curly braces vs plain text inside tags

How to handle this:

  • Don’t overuse embedded expressions for better code readability. It’s especially handy to use JSX when you need to perform inline calculations or contract the data.

Related services

SaaS Development Services

Conclusion

Although React is a powerful web development technology, your developers need to be careful and know technical details related to things like passing props and using hooks and renderings. It’s essential to learn what mistakes to avoid when using React to ensure an efficient development process.

Understanding the nuances of writing applications in React can help your development team build reliable solutions and deliver them on time. And if you don’t have time to educate your web developers on React tips and tricks, you can delegate your React project to niche professionals. At Apriorit, we have experienced frontend developers who know how to create flawless custom web applications.

Contact us to start working on the web application of your dreams!

Tell us about your project

Send us a request for proposal! We’ll get back to you with details and estimations.

By clicking Send you give consent to processing your data

Book an Exploratory Call

Do not have any specific task for us in mind but our skills seem interesting?

Get a quick Apriorit intro to better understand our team capabilities.

Book time slot

Contact us