So let's not waste any time and take a look at our problems and how we can solve them 👏.
- Forgetting to add keys with a list of elements
- Not returning a list correctly
- Not cleaning up certain
- 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.
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
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 👇
and our component 👇
and finally the warning from React 👇.
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
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
Date object. You could also use a package like nanoid that generates unique string id's for you.
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.
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:
Instead our data will appear to be missing 👇.
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.
Correct! Here we are not returning anything either implicitly or explicitly using the
Let's take a look at another 👇.
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.
Let's start with the explicit returns. If we move our
article element in line with the return statement all is well.
See below 👇
We could also wrap the return elements with parenthesis like this:
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 👇.
or inline like this 👇.
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 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.
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.
Let's take a look at two different situations where we might add some cleanup to our
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 the
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 👇
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.
We begin by adding a cleanup function to our
useEffect like this:
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
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 👇.
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.
If we want the wrapper to be part of the DOM for structural purposes then go with some semantic element where possible (
<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.
and the error it throws 👇
It is likely that your code editor gave you a warning about this before the error pops up which is great.
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.
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.
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.
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.