DEV Community

Cover image for My most frequent React errors and how you fix them
Kieran Roberts
Kieran Roberts

Posted on • Originally published at blog.kieranroberts.dev

My most frequent React errors and how you fix them

When using the popular JavaScript library React there are some errors/problems that seem to pop up time and time again. They can be easily avoided in most situations and I would like to share them with you so you can spend less time debugging and more time writing code.

So let's not waste any time and take a look at our problems and how we can solve them πŸ‘.

Content

  1. Forgetting to add keys with a list of elements
  2. Not returning a list correctly
  3. Not cleaning up certain useEffect side-effects
  4. Not wrapping adjacent JSX elements

1) Forgetting to add keys with a list of elements

In React we often find ourselves with lists of data that we want to map into elements or components. This is often done using the Array.prototype.map function to pass data from each index of the array to the element or component through props.

When we do this without adding a key prop React will complain that each element is missing a key. Essentially it is just a special attribute to which we pass a string. It should be a unique string which will distinguish it from its siblings that we are also mapping.

React says:

Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity

Example Problem

Let's map some data before adding keys to our elements to see the problem in action. Here we will have a simple component that deconstructs foods from the prop object. foods is just an array of objects and it looks like this πŸ‘‡

Our example data set

and our component πŸ‘‡

mapped elements without key prop

and finally the warning from React πŸ‘‡.

missing key warning

Solution

To fix this all we have to do is pass a unique key to the element we are returning. Often the data we are mapping comes from a fetch request and usually includes an id. Fortunately we have and id property we can use from our set of data. Let's add our key property.

After adding our key prop

If we didn't have a unique id we would need to have an alternative solution. Often people use the index of the array as the key but this is not recommended for any set of data where positions in the list may change. It can negatively affect the state of the component. See here for more information Reactjs - Reconciliation.

Instead we could create our key by combining the name property with the current date/time using the JavaScript Date object.

2) Not returning a list correctly

To return or not to return

In React as we have already seen we are often iterating over some data . Perhaps we are filtering a data set down to a specific sub-set or mapping to the DOM. Whatever it is there are a few pitfalls we need to watch out for when it comes to returning our data otherwise we might be left scratching our heads.

Example Problem

A frustrating example can be seen when we map a data set to some elements or components. We expect to see the or elements on screen with the data we map into them. However we see nothing.

No error, no warning no data 🀨. In this situation it is likely that you're not returning your result correctly.

For our example we will be mapping our array of foods to some elements so we can show it to the user. It should look like this:

Correct return demonstration

Instead our data will appear to be missing πŸ‘‡.

Incorrect return demonstration

Let me show you some examples where we won't see the output that we were expecting. We are passing our array to our component and destructuring it from the prop object the same as before.

Can you spot the problem below.

Example of not returning correclty

Correct! Here we are not returning anything either implicitly or explicitly using the return keyword.

Let's take a look at another πŸ‘‡.

Second example of not returning correctly

This time we include the return keyword but what we are actually doing here is returning undefined. The code below the return statement never gets read.

There are other examples you might run into but let's take a look at the different solutions we can use.

Solution

Let's start with the explicit returns. If we move our article element in line with the return statement all is well.

See below πŸ‘‡

Example of correct explicit return

We could also wrap the return elements with parenthesis like this:

Example of correct explicit return

Another option is to return the result implicitly which means we can forget the return statement and the function body curly braces. Check it out πŸ‘‡.

Example of correct implicit return

or inline like this πŸ‘‡.

Example of correct implicit return

The choice is up to you as long as you are aware of the possible pitfalls you encounter. If the data appears to be missing make sure you check your map function carefully and make sure you are actually returning correctly.

3) Not cleaning up certain useEffect side-effects

The useEffect hook allows us to perform side-effects inside functional components. Certain side-effects that we perform in this hook require cleanup. This means that when the component unmounts we can run a special function. Sometimes this is necessary otherwise we might see an error warning us of memory leaks in our apps.

Consider a useEffect that performs some kind of asynchronous api call before setting some component state to the response. If the response is slow and the component unmounts before we receive a response then we might be trying to update the state of a component that is not mounted.

Example Problem

Let's take a look at two different situations where we might add some cleanup to our useEffect.

The first is a situation is where we have an asynchronous fetch request inside our useEffect hook. The user of the application navigates to a different page before we receive the response from the fetch call. This is our component before we add a cleanup function to theuseEffect hook.

React useEffect before cleanup

Here we are fetching some data after the component mounts and then using the result to set the components state. Finally we map the state to the DOM. Relatively straight forward πŸ‘.

The second situation is where we add some eventListeners to some DOM elements. If the component unmounts we are going to want to remove these listeners.

Check it out before we clean it up πŸ‘‡

Before adding our cleanup function to useEffect

The logic inside our useEffect is irrelevant for this simple example. All that matters is that we are adding an event listener and that will need to be cleaned up.

Solution

We begin by adding a cleanup function to our useEffect like this:

useEffect cleanup function

It is simply a function that we add to the bottom of our useEffect hook where we add our cleanup logic.

Now to cleanup our fetch request we can use the DOM api AbortController which is available in JavaScript. It allows us to abort web requests which we will use to abort out of the fetch request whenever the component unmounts. Let's see it in action πŸ‘‡.

React useEffect fetch cleanup function

First we create a controller by using the constructor with new AbortController() and then we pass its property signal to the fetch request. This association of controller signal to the request allows us to abort the request by calling abort() inside the cleanup function.

Now we are ensuring that we don't have any requests coming back to a component that is not mounted.

The cleanup function for our eventListener example is simple which you might have already guessed. All we need to do is remove any listeners we create using removeEventListener in our cleanup function. Let's see it in action πŸ‘‡.

React useEffect listener cleanup example

Hopefully now like me you won't forget to clean up your effects πŸ˜‰.

4) Not wrapping adjacent JSX elements

This one is simple to debug but I though I would include it because I sometimes forget to do it until React starts shouting at me πŸ˜….

Adjacent JSX elements must be wrapped with an enclosing tag. There are a few different ways we can do this based on our requirements.

Example Problem

If we want the wrapper to be part of the DOM for structural purposes then go with some semantic element where possible (<article>, <section> etc.). I usually wouldn't recommend wrapping elements with a <div> although it is fine if you want the wrapper for styling purposes.

Often we do not want the wrapper to part of the DOM because it serves no purpose there. We would only be adding markup only to shut React up.

Let's see the problem in action.

Example component not wrapping adjacent JSX

and the error it throws πŸ‘‡

Adjacent JSX error

It is likely that your code editor gave you a warning about this before the error pops up which is great.

Solution

Fortunately React provides us with a solution. We can use React.Fragment to wrap our adjacent JSX if we do not require the wrapper to be part of the DOM. Let's say this is the case for the following example.

First let's use React.Fragment before we see how we can simplify it further.

Wrapping adjacent JSX with React.Fragment

If we don't need any attributes or keys for our fragment we can shorten React.Fragment to this <> empty tag. Have a look below.

Wrapping adjacent JSX with React.Fragment shortened syntax

Finally if we are mapping some data to JSX as we have previously seen then we need to add keys to our wrapping element. If all we have is adjacent JSX then we can wrap our elements with React.Fragment and add a unique key to the fragment.

Wrapping our mapped adjacent JSX with React.fragment and a unique key

Conclusion

Thanks for getting this far! I hope you learned something from the article and now we can both make sure we avoid these problems in our future code.

If you enjoyed it feel free to drop a πŸ‘ on the article. It inspires me to continue improving and make more awesome content.

If you would like to connect with me then come an say hi @Kieran6dev as I am always active in communicating with other devs over on Twitter.

Thanks!

Top comments (5)

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

You should not use nanoid, or any other random ID generator, to create keys for your React array elements. In fact, it says, right on their README, that

There’s currently no correct way to use nanoid for React key prop since it should be consistent among renders.

Using a random random generator for keys forces the the reconciliation process to always view every element in the array as "new" or "changed", which forces it to rerender - and that can cause some nasty side effects.

I was actually learned this the hard way several years ago when I was using my own custom random ID function with Material UI's Collapse component. You can see an explanation of the issue here:

stackoverflow.com/questions/513719...

Collapse
 
souksyp profile image
Souk Syp.

That's good to know

Collapse
 
kieran6roberts profile image
Kieran Roberts

You're right, not sure how I missed this. Removed it for now. Thanks for the input!

Collapse
 
nicozerpa profile image
Nico Zerpa (he/him)

Years ago, I did the return\n error all the time...
Great article, these are the little things that can cause big problems that can be hard to find and solve.

Collapse
 
kieran6roberts profile image
Kieran Roberts

It's easy to make these errors at first πŸ˜…. Thanks for sharing, I'm glad you liked it!