In this article, we’ll explore the top 10 mistakes in React.js and how to avoid them with best practices and code examples.
1️⃣ Mutating State Directly Instead of Using setState()
❌ Mistake: Modifying State Directly
const [count, setCount] = useState(0);
const increment = () => {
count += 1; // ❌ Directly modifying state
};
✔ Issue:
- React does not detect the state change, so the component does not re-render.
✅ Solution: Use setState()
to Update State Properly
const [count, setCount] = useState(0);
const increment = () => {
setCount(prevCount => prevCount + 1); // ✅ Correct way
};
✔ Benefit: Ensures React re-renders the component properly.
2️⃣ Using index
as a Key in Lists
❌ Mistake: Using Array Index as Key
{items.map((item, index) => (
<li key={index}>{item.name}</li> // ❌ Bad practice
))}
✔ Issue:
- If items are reordered, React cannot track changes properly, leading to unexpected UI behavior.
✅ Solution: Use a Unique Identifier as a Key
{items.map(item => (
<li key={item.id}>{item.name}</li> // ✅ Correct way
))}
✔ Benefit: Prevents unnecessary re-renders and ensures better list tracking.
3️⃣ Overusing State Instead of Using Props or Context API
❌ Mistake: Managing Global Data in Local State
const [theme, setTheme] = useState("light"); // ❌ Managing global theme locally
✔ Issue:
- Makes it difficult to share data across multiple components.
✅ Solution: Use Context API for Global State
const ThemeContext = createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
✔ Benefit: Makes data sharing across components easier.
4️⃣ Not Using useEffect
Dependencies Properly
❌ Mistake: Forgetting the Dependency Array
useEffect(() => {
console.log("Component Mounted!");
}); // ❌ Will run on every render
✔ Issue:
- This runs on every render, causing performance issues.
✅ Solution: Provide Dependency Array
useEffect(() => {
console.log("Component Mounted!");
}, []); // ✅ Runs only once
✔ Benefit: Avoids unnecessary re-executions, improving performance.
5️⃣ Overusing useEffect
for State Changes
❌ Mistake: Updating State Inside useEffect
Without a Condition
useEffect(() => {
setData(fetchData()); // ❌ Infinite loop risk
}, [data]);
✔ Issue:
- This can cause an infinite loop if not handled properly.
✅ Solution: Use Conditions or Memoization
useEffect(() => {
setData(fetchData());
}, []); // ✅ Runs only once
✔ Benefit: Prevents unnecessary re-renders and infinite loops.
6️⃣ Not Memoizing Expensive Calculations (useMemo
)
❌ Mistake: Performing Expensive Calculations in Render
const result = heavyCalculation(data); // ❌ Runs on every render
✔ Issue:
- Recalculates on every render, causing performance issues.
✅ Solution: Use useMemo()
to Optimize Performance
const result = useMemo(() => heavyCalculation(data), [data]); // ✅ Optimized
✔ Benefit: Caches the result, reducing unnecessary computations.
7️⃣ Using useEffect
Instead of useMemo
or useCallback
❌ Mistake: Using useEffect
for Derived State
const [filteredItems, setFilteredItems] = useState([]);
useEffect(() => {
setFilteredItems(items.filter(item => item.active));
}, [items]); // ❌ Unnecessary state updates
✔ Issue:
- Triggers unnecessary re-renders.
✅ Solution: Use useMemo
Instead
const filteredItems = useMemo(() => items.filter(item => item.active), [items]); // ✅ Optimized
✔ Benefit: Prevents unnecessary state updates.
8️⃣ Forgetting to Cleanup Effects in useEffect
❌ Mistake: Not Cleaning Up Event Listeners
useEffect(() => {
window.addEventListener("resize", handleResize);
}, []); // ❌ Event listener never removed
✔ Issue:
- Leads to memory leaks.
✅ Solution: Cleanup with Return Function
useEffect(() => {
const handleResize = () => console.log("Resized!");
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize); // ✅ Cleanup
};
}, []);
✔ Benefit: Prevents memory leaks and ensures proper event handling.
9️⃣ Using Anonymous Functions in useEffect
Dependencies
❌ Mistake: Passing Functions Directly as Dependencies
useEffect(() => {
fetchData();
}, [fetchData]); // ❌ Triggers effect on every render
✔ Issue:
- Creates a new function reference on every render, triggering useEffect unnecessarily.
✅ Solution: Use useCallback
to Memoize Functions
const fetchData = useCallback(() => {
// API call
}, []);
useEffect(() => {
fetchData();
}, [fetchData]); // ✅ Function reference remains stable
✔ Benefit: Prevents unnecessary effect executions.
🔟 Not Lazy Loading Components (React.lazy()
)
❌ Mistake: Importing All Components at Once
import Dashboard from "./Dashboard";
import Profile from "./Profile";
✔ Issue:
- Increases initial page load time.
✅ Solution: Use Lazy Loading for Performance Optimization
const Dashboard = React.lazy(() => import("./Dashboard"));
const Profile = React.lazy(() => import("./Profile"));
<Suspense fallback={<div>Loading...</div>}>
<Dashboard />
</Suspense>
✔ Benefit: Improves page speed by loading components only when needed.
🎯 Conclusion
React is powerful but also tricky to master. By avoiding these common mistakes, you can build faster, scalable, and maintainable applications.
✔ Use setState()
instead of modifying state directly
✔ Avoid using array index as keys in lists
✔ Use Context API instead of unnecessary local state
✔ Optimize performance with useMemo
and useCallback
✔ Ensure proper effect cleanup in useEffect
✔ Use lazy loading for better performance
By following these best practices, you’ll write better React code and build high-performing applications! 🚀
Comments
Post a Comment
Leave Comment