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.
Contents:
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.
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.
Empower your business with custom web solutions!
Let’s collaborate on developing competitive web applications that perform flawlessly and boost user engagement.
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.
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):
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:
Below, you can see the wrong and right ways of mentioning ref in React code:
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
React.js vs Knockout.js vs Vue.js: Features and Performance Comparison
Wondering what JavaScript framework to choose for your web project? Apriorit experts share their experience working with React.js, Knockout.js, and Vue.js, providing practical tips for selecting the ideal framework for your next software.
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:
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):
How to handle this:
- Use state managers like Redux or MobX libraries to avoid props drilling.
- Otherwise, use the createContext API and the useContext hook.
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:
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):
How to handle this:
- Use fewer HOCs and more hooks to improve code readability.
Read also
SaaS Development Lifecycle
Explore the unique challenges and best practices for developing SaaS applications from Apriorit experts. Read this article to learn how to launch successful SaaS projects in today’s competitive market.
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.
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):
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.
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:
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:
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:
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
Explore how you can leverage single-page applications for your project based on their high performance, responsiveness, and seamless user experience. In this article, Apriorit experts offer guidance on potential pitfalls and framework choices for SPAs.
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 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.
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.
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:
How to handle this:
- Keep the previous state of unchanged states when working with complex states.
Related project
Enhancing the Security and Performance of a Virtual Application Delivery Platform
Discover how Apriorit strengthened the Virtual Application Delivery (VAD) platform for a US-based client, enhancing their virtual application delivery solution with advanced security measures and performance enhancements.
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.
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:
How to handle this:
- Add a default value to the useState hook.
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.
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.
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.
Letโs explore an example of how an infinite loop occurs:
- Initial rendering. The useEffect hook executes the side effect callback that updates the state (setCounter).
- Re-rendering. The state (counter) update triggers re-rendering.
- 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.
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:
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:
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:
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:
- Control the dependencies array in the useEffect hook.
- Set up the exhaustive-deps rule in ESLinter.
Read also
Building a Cross-Platform Mobile Web Application with Ionic and Angular
Optimize development time and costs while delivering robust mobile solutions with Ionic and Angular. Discover insights from Aprioritโs experience leveraging the Ionic framework to quickly create applications with simplified UIs and shared codebase across multiple platforms.
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.
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:
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.
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.
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:
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.
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.
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.
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.
Ready to build your next web application?
Receive a reliable and high-performing solution by delegating web development to Apriroitโs expert engineers!