It is important to consider performance in React applications because it can have a significant impact on the user experience. Poor performance can lead to slow rendering times, which can make your application feel sluggish and unresponsive. In addition, performance issues can lead to increased resource consumption on the client side, which can drain battery life and bandwidth on mobile devices.
so here are my top mistakes or takeaway as a beginner you can improve while coding
use of useState when only required
consider below form submission code
import React, {useState} from 'react'
export default function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const onSubmitHandler = (e) => {
e.preventDefault();
console.log(email, password);
};
return (
<div>
<form onSubmit={onSubmitHandler}>
<input
placeholder="Enter email"
type="email"
onChange={(e) => {
setEmail(e.target.value);
}}
/>
<input
type="password"
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
/>
<input type="submit" />
</form>
</div>
);
}
The above is a simple Login component with 2 states name and password
but do we really need those states because we can control the functionality without using any state and can optimize for better as below
import React, { useRef } from 'react';
export default function Login() {
const emailref = useRef();
const passwordref = useRef();
const onSubmitHandler = (e) => {
e.preventDefault();
const data = {
email: emailref.current.value,
password: passwordref.current.value,
};
console.log(data);
};
return (
<div>
<form onSubmit={onSubmitHandler}>
<input placeholder="Enter email" type="email" ref={emailref} />
<input type="password" placeholder="Password" ref={passwordref} />
<input type="submit" />
</form>
</div>
);
}
- calling setState does not immediately mutate the component's state. Instead, it schedules an update to the component's state and tells React that it needs to re-render the component. The actual update and re-rendering of the component will take place asynchronously, at a later time.
This means that you should not rely on the state being updated immediately after calling setState. If you need to perform an action that depends on the updated state, you should either pass a callback function to setState as the second argument or use the useEffect Hook to perform the action after the component has re-rendered.
// Using a callback function
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count); // This will log the updated value of count
});
// Using useEffect
useEffect(() => {
console.log(count); // This will log the updated value of count
}, [count]); // The effect will run when count changes
- It's important to be mindful of the dependencies you specify in the dependency array for the useEffect Hook. If you include a dependency that does not actually change between renders, this can cause the effect to run unnecessarily on every render. This can lead to performance issues and unexpected behavior.
Here is an example of an unnecessary useEffect that runs on every render:
import { useEffect, useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [message, setMessage] = useState('Hello');
// This effect will run on every render, even though the message is not changing
useEffect(() => {
console.log('The message is:', message);
}, [message]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
To fix this issue, you can simply remove the unnecessary dependency from the dependency array. In this case, the effect does not actually depend on the value of message, so it can be removed from the dependency array
Referential equality mistakes
In React, referential equality mistakes can occur when a component's props or state are updated in a way that causes the component to re-render, but the new props or state do not actually change the component's behavior.
One common mistake is updating a component's props or state with new object or array references that have the same contents as the previous references, but are not actually the same objects. Because the references are different, React will treat the props or state as having changed, and the component will re-render. However, because the actual contents of the objects or arrays are the same, the component's behavior will not change, leading to unnecessary re-renders.
To avoid this mistake, you can use the shouldComponentUpdate lifecycle method to compare the current and next props or state and return false if they are equal. You can also use the React.memo higher-order component to wrap a functional component and only re-render it if its props have changed.
Another common mistake is using the wrong comparison operator when comparing props or state in a component's render method. For example, using == instead of === to compare values can cause a component to re-render unnecessarily. It is important to use the strict equality operator (===) when comparing values in React, as it ensures that the types of the values are also compared.
Finally, it is important to be mindful of the order in which props and state are compared. If a prop or state value is being compared to itself, it should be compared to the previous value, not the current value. For example:
if (this.props.value !== prevProps.value) {
// do something
}
Not aborting fetch requests
In a React application using hooks, you can use the useEffect hook to cancel ongoing fetch requests when a component unmounts or when certain dependencies change.
Here is an example of how you can use the AbortController API to cancel a fetch request when a component unmounts:
import { useEffect, useState } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch('/data', { signal })
.then(response => response.json())
.then(data => setData(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch request was cancelled');
} else {
console.error(error);
}
});
return () => controller.abort();
}, []);
return (
// render the component using the data
);
}
In this example, the useEffect hook is called with an empty array of dependencies, which means that it will only be executed once, when the component mounts. The AbortController and its signal are created inside the useEffect hook, and the fetch request is made using the signal. The useEffect hook also returns a function that cancels the fetch request by calling the abort method on the AbortController when the component unmounts.
You can also use the useEffect hook to cancel a fetch request when certain dependencies change. For example
import { useEffect, useState } from 'react';
function MyComponent({ url }) {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal })
.then(response => response.json())
.then(data => setData(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch request was cancelled');
} else {
console.error(error);
}
});
return () => controller.abort();
}, [url]);
return (
// render the component using the data
);
}
In this example, the useEffect hook is called with the url prop as a dependency. This means that the useEffect hook will be executed whenever the url prop changes, and the fetch request will be cancelled and a new request will be made using the updated url.
Top comments (0)