Are you starting out in React? Just as you were getting the hang of JavaScript, here comes React and JSX with little "ghosts" that sneak up on you. As a level one player, let's explore a few basic gotchas as we begin to navigate the maze of React.
There are gotchas in every programming language and let's face it, even the well-seasoned players still get caught by the ghosts occasionally. You'll never get that hour of your life back that you spent searching for that error that turned out to be a minor gotcha. Don't be hard on yourself.. we have all been there.. and odds are, it will happen again.
Keys must be unique or you will receive an error!
"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" React
Below when using .map()
I used key={ghost.index}
which triggers a warning message:
function GhostList({ ghosts }) {
const displayGhosts = ghosts.map((ghost) =>
<Ghost key={ghost.index} ghost={ghost} />)
return (
<div>
<h1>Gotcha Ghosts</h1>
<ul>
{displayGhosts}
</ul>
</div>
)
}
You should not be using an array index for keys and should only be used as a last resort. Doing so can cause errors/bugs in your code and cause issues with component state. Level Up - Tell Me Why Instead opt for the element.id or another unique string that will identify the list item. Here I used the ghost.id for my key but alternatively I could use key={ghost.name}
if there is no id available and each name is unique.
const displayGhosts = ghosts.map((ghost) =>
<Ghost key={ghost.id} ghost={ghost}/>)
Recently I received this warning when working with keys:
I thought what am I doing wrong? I was dealing with an array of objects (like my ghosts), and the key's value was an array...
const ghosts = [
{
"id": 1,
"name": "Inky",
"color": "blue",
"altNames":["Fickle", "Bashful"]
},
{
"id": 2,
"name": "Blinky",
"color": "red",
"altNames":["Chaser", "Shadow"]
},
{
"id": 3,
"name": "Pinky",
"color": "pink",
"altNames":["Ambusher", "Speedy"]
},
{
"id": 4,
"name": "Clyde",
"color": "orange",
"altNames":["Stupid", "Pokey"]
}
]
When you .map() through the "altNames" array to display them on the DOM, you can not use key={ghost.id} or key={ghost.name}. These will not provide a unique key for each element of the "altNames" array (list) and you will receive an error like the one above. The following was my solution, by assigning altName as the key to each element. This only works because in this data each altName (elements in array) is unique:
function Ghost({ ghost }) {
const displayAltNames = ghost.altNames.map((altName) =>
<li key={altName}>{altName}</li>
)
return (
<div>
<h3>Ghost: {ghost.name}</h3>
<p>Color: {ghost.color}</p>
<p>Other Names:</p>
<ul>{displayAltNames}</ul>
</div>
)
}
In JSX you can only return one parent element. Notice in the code below there are two parent <div>
s with children trying to be returned. In the console, you will receive a syntax error.
function Ghost({ ghost }) {
const displayAltNames = ghost.altNames.map((altName) => (
<li key={altName}>{altName}</li>
));
return (
<div>
<h2>Say hello to {ghost.name}!</h2>
</div>
<div>
<h3>Ghost: {ghost.name}</h3>
<p>Color: {ghost.color}</p>
<p>Other Names:</p>
<ul>{displayAltNames}</ul>
</div>
);
}
As you can see, the error even suggests what to do. After getting this error once, you’ll know exactly what mistake to look for in your code. I wish all errors were this obvious! Now, how do you fix this? You could wrap it all in another <div>
OR use <React.Fragment>
. If you need those two <div>
s you can now return them by wrapping your return with <React.Fragment>
:
return (
<React.Fragment>
<div>
<h2>Say hello to {ghost.name}!</h2>
</div>
<div>
<h3>Ghost: {ghost.name}</h3>
<p>Color: {ghost.color}</p>
<p>Other Names:</p>
<ul>{displayAltNames}</ul>
</div>
</React.Fragment>
);
}
Or if I just had one <div
, I could replace it with <React.Fragment>
or the shortened syntax <> </>
return (
<>
<h3>Ghost: {ghost.name}</h3>
<p>Color: {ghost.color}</p>
<p>Other Names:</p>
<ul>{displayAltNames}</ul>
</>
);
}
You can also use <React.Fragment>
to replace a wrapping element to avoid cluttering the DOM with unneeded nodes. When selecting your wrapping element, keep in mind a key is the only attribute that can be passed to <React.Fragment>
. Why must JSX expressions have only ONE parent element?
Another potential JSX snag is components must begin with an uppercase, which is quite conflicting from what we have learned in JavaScript. This new naming convention is one of the first gotchas you encounter in React. Below are two different errors triggered by forgetting to capitalize component names.
<ghost />
should be <Ghost />
console error...
function ghostList({ ghosts }) {
...
export default ghostList;
console error....
Also check your import component names because that error will tell you something is really wrong! It is good practice to intentionally experiment with errors throughout your code when learning so you can be familiar with different error messages.
"When an element type starts with a lowercase letter, it refers to a built-in component like <div>
or <span>
and results in a string 'div' or 'span' passed to React.createElement." React
When gathering data for the pac-man ghosts, I noticed all of them have playful names.. but then there is Clyde. When I saw the original Japanese names... poor Clyde's original name is "Stupid". There has to be some long lost story on how Clyde got his name... if you know.. share! Now it's time to chomp on another React power pellet and level up to gotchas dealing with state and hooks.
You can connect with me on LinkedIn & GitHub
Top comments (1)
That's a fun way to look at it 😄