Five most common beginners' mistakes in ReactJS
All of them were made by me in a period of one year.
Having been worked with React for a couple of years now, I've had my fair share of bugs and other errors. In no way I'm a complete expert in this Front End library but can name some very common mistakes I did in my career so that you do not have to learn the hard way. Ready? Here goes
Mistake #1: Invoking component name starting with a lowercase letter
This is the first mistake beginners make when they start with react. This is a common mistake you might commit having worked with js. In JavaScript, your file names and your function names are usually lowercase as that is the convention.
To demonstrate this, let’s look at the following two code snippets.
message.js
function message() {
return <div> Hello World !!</div>
}
export default message
App.js
import message from './message'
function App(){
return (
<div className = "App">
<message/>
</div>
)
}
export default App
As you would expect, the browser throws an error because it doesn’t recognize a user-defined component when it starts with a lowercase letter. If you want to render a react component, its name must start with an upper-case letter during invocation.
The error itself provides a clear message that you might want to start the name of a component using an uppercase letter.
All you need to change in the code is the name of the import in the App.js file to avoid this error.
App.js
import Message from './message'
function App(){
return (
<div className = "App">
<Message/>
</div>
)
}
export default App
But it is always recommended to stick to React conventions and rename the component files and the functions like message.js
to Message.js
.
Message.js
function Message() {
return <div> Hello World !!</div>
}
export default Message
Mistake #2: Importing named exports as default exports
Let’s first see how a named export looks like :
NamedExport.js
export const NamedExport = () => {
return (
<div> Named Export </div>
)
}
Named export differs from default export in the way they are exported. Named exports doesn’t contain the default
keyword in their export statement.
While importing them in App.js, we tend to do so without using curly braces.
App.js
import NamedExport from './NamedExport' // the error is in this line
function App(){
return (
<div className = "App">
<NamedExport/>
</div>
)
}
export default App
In the browser console, the following error is shown.
It says that the file you want to import from does not contain a default export.
The way to avoid this error is either to change the named export to a default export in the Component file or to wrap your import with curly braces in App.js
file.
import {NamedExport} from './NamedExport'
function App(){
return (
<div className = "App">
<NamedExport/>
</div>
)
}
export default App
Mistake #3: Incorrect usage of the Setter function in the UseState Hook.
Let us first look at the faulty code and try to understand what the error is.
Counter.js
import {useState} from 'react'
export const Counter = () => {
const [count, setCount] = useState(0)
return (
<div> <button onClick = {setCount(count + 1)}>
Count = {count}
</button>
</div>
)
}
When we add this component to the App.js file, the browser again throws an error .
Too many re-renders
- React is limiting the number of re-renders being made due to some infinite loop in our code.
So, can you guess the reason for this error ?
This is a result of us calling the setter function instead of passing a reference to that function in the onClick handler.
Currently, on initial render, setCount is invoked, and the count value is incremented to 1
. The count value increments, and the component needs to re-render. This will again cause setCount to invoke and the value is now 2
. This again causes the re-render and the same goes on forever giving rise to an infinite loop.
Hence react throws an error to prevent the infinite loop.
Let’s see how we pass a reference to the setter function in the above code :
Counter.js
import {useState} from 'react'
export const Counter = () => {
const [count, setCount] = useState(0)
return (
<div> <button onClick = {()=>setCount(count + 1)}>
Count = {count}
</button>
</div>
)
}
To correct this, we just need to pass the setCount function inside another arrow function into the onClick handler.
i.e., Change {setCount(count + 1)}
to {()=>setCount(count + 1)}
.
This is a mistake I made the most and took most of my debug time while starting out with useState hook.
Mistake #4: Mutating Objects and Arrays while using useState
Let's have a look at the following faulty code snippet :
import {useState} from 'react'
export const Name = () => {
const [name, setName] = useState({
fname : `John`,
lname : 'Doe'
})
const changeName = () => {
name.fname = 'Elon',
name.lname = 'Musk'
setName(name)
}
return (
<div>
<p>
Name is {Name.fname} {name.lname}
</p>
<p> Set the name to Elon Musk </p>
<button onClick={changeName}> Set name </button>
</div>
)
}
In the above code we have a button which when clicked tries to change (Mutate) the name object from John
, Doe
to Elon
, Musk
.
If we include this component to App.js
, and go to the browser, unlike earlier errors, we do not encounter an error. But the Name is John Doe doesn’t change to Name is Elon Musk.
This unexpected behavior is due to the fact that useState setter function expects new object reference and not the same object that is mutated.
Let’s see how we can do that by changing the previous faulty code, ever so slightly.
import {useState} from 'react'
export const Name = () => {
const [name, setName] = useState({
fname : `John`,
lname : 'Doe'
})
const changeName = () => {
name.fname = 'Elon',
name.lname = 'Musk'
setName({
fname : 'Elon',
lname : 'Musk'
})
}
return (
<div>
<p>
Name is {Name.fname} {name.lname}
</p>
<p> Set the name to Elon Musk </p>
<button onClick={changeName}> Set name </button>
</div>
)
}
Now, when we click the Button, the code works as expected, and the name gets changed.
As a beginner, when we maintain object as state, it is a mistake that happens quite often. Personally, it has been the reason for many of my sleepless nights.
This is the same case while mutating arrays as well. Keep in mind that the setter function always needs the new array/object reference to work properly.
Do not mutate the array using array.push
or array.pop
method. Instead, make a copy of the array and perform the desired operation. Then pass that copy into the setter function.
Unlike other mistakes, here React doesn’t throw any error. Which makes it harder to debug and identify what is wrong with the code.
Mistake #5: Forgetting that the UseState setter function is Asynchronous
As beginners we often forget that the useState setter function is asynchronous, which can lead to unusual bugs in the code.
Let’s find out with an example :
AsyncSetState.js
import {useState} from 'react';
export const AsyncSetState = () => {
const [count, setCount] = useState(0);
const sendCountVal = () => {
setCount(count + 1)
console.log('Sending count value ',count);
// Assume we are calling an API
}
return (
<div>
<button onClick = {sendCountVal}>Count - {count} </button>
</div>
)
}
Within the above component, we have a state variable called count
initialized to 0
. In the jsx, we have a button that shows the count, which when clicked , calls an API with the updated count value.
We can assume that we are calling an API when we encounter the console log after updating the count value inside the function.
If we include the component in App.js
and go to the browser, we see that the count
value inside the button is updated. However, in the console (or when sending to the API) , the count value isn’t updated and is still showing 0
.
Our intention was to send the updated count value to the API but that did not happen. This is due to the fact that setCount doesn’t get executed synchronously, i.e. it runs after the console.log
.
To avoid this error, we need to use the useEffect
hook.
Let’s see how we need to change the code in order to get the desired results.
import {useState, useEffect} from 'react';
export const AsyncSetState = () => {
const [count, setCount] = useState(0);
const sendCountVal = () => {
setCount(count + 1)
// we are not calling the API from here anymore
}
useEffect(()=>{
console.log('Sending count value ', count);
// this function runs whenever the value of count is changed.
},[count])
return (
<div>
<button onClick = {sendCountVal}>Count - {count} </button>
</div>
)
}
If we now go to the browser and click on the button, the code runs as expected, and the most recent value of count
is sent to the API.
On page load, the API would be called with a count value of 0
, but we can bypass this by making a check that the API isn’t called when the count value is 0 in the Effect hook.
So, we need to keep in mind that the setter function is Asynchronous.
That is all in this Article. I hope this helped you with one or more of the mistakes that generally happens to beginners while starting with React.
Let me know in the comments what other beginner ,mistakes you encountered while starting with React JS.