1. “Destructure Props Directly Instead of Accessing Them Individually“
❌ Bad: Accessing props repeatedly makes the code harder to read.
function UserProfile(props) { return ( <div> <h1>{props.name}</h1> <p>Age: {props.age}</p> <p>Email: {props.email}</p> <button onClick={props.onEditProfile}>Edit Profile</button> </div> ); }
✅ Good: Destructure props at the start for cleaner and more readable code.
function UserProfile({ name, age, email, onEditProfile }) { // destructuring props return ( <div> <h1>{name}</h1> <p>Age: {age}</p> <p>Email: {email}</p> <button onClick={onEditProfile}>Edit Profile</button> </div> ); }
2. Make Sure To Set Default Values for Props While Destructuring them.
❌ Bad: defining default values in multiple places introduces new redundant variables.
function UserCard({ name, age, email }) { const displayAge = age || "Unknown"; // Default set here const displayEmail = email || "Not provided"; // Default set here return ( <div> <h1>{name}</h1> <p>Age: {displayAge}</p> <p>Email: {displayEmail}</p> </div> ); }
✅ Good: Define default values directly while destructuring props , making them easy to locate & modify and Keeps the component logic cleaner.
function UserCard({ name, age = "Unknown", email = "Not provided" }) { return ( <div> <h1>{name}</h1> <p>Age: {age}</p> <p>Email: {email}</p> </div> ); }
3. Drop Curly Braces for String-Type Props
❌ Bad: Using curly braces for strings unnecessarily adds visual noise.
<UserCard name={"John Doe"} age={"30"} email={"john@example.com"} />
✅ Good: Use quotes directly for string props.
<UserCard name="John Doe" age="30" email="john@example.com" />
4. Use Boolean Checks for Conditional Rendering
❌ Bad: Using truthy checks without ensuring a boolean condition can lead to unintended output (like 0 or empty strings).
function ItemList({ items }) { return <div>{items.length && <ul>{items.map((i) => <li>{i}</li>)}</ul>}</div>; // `0` may appear if items.length is 0 }
✅ Good: Use explicit boolean conditions for safety.
function ItemList({ items }) { return ( <div> {items.length > 0 && <ul>{items.map((i) => <li>{i}</li>)}</ul>} // boolean check </div> ); }
5. Use Functions to Scope Intermediate Variables
❌ Bad: Declaring variables in the component scope unnecessarily clutters it.
function AverageGrade({ grades }) { if (grades.length === 0) { return <p>No grades available.</p>; } let total = 0; grades.forEach((grade) => (total += grade)); const average = total / grades.length; return <p>Average Grade: {average}</p>; }
✅ Good: Encapsulate logic in a helper function.
function AverageGrade({ grades }) { if (grades.length === 0) { return <p>No grades available.</p>; } const calculateAverage = () => { // helper function return grades.reduce((sum, grade) => sum + grade, 0) / grades.length; }; return <p>Average Grade: {calculateAverage()}</p>; }
6. Avoid Multiple Return Statements in Functional Components
❌ Bad: Multiple Return Statements Make It Hard to Trace the Output
function ProductList({ products, searchTerm, onProductSelect }) { const filteredProducts = products.filter((product) => product.name.includes(searchTerm) ); if (filteredProducts.length === 0) { return <p>No products found.</p>; // Return early } return ( <ul> {filteredProducts.map((product) => { return ( <li key={product.id} onClick={() => onProductSelect(product.id)}> {product.name} </li> ); })} </ul> ); }
✅ Good: Consolidate Logic to Use a Single Return Statement
function ProductList({ products, searchTerm, onProductSelect }) { const filteredProducts = products.filter((product) => product.name.includes(searchTerm) ); return ( <div> <h1>Product List</h1> <ul> {filteredProducts.length === 0 ? ( <p>No products found.</p> ) : ( filteredProducts.map((product) => ( <li key={product.id} onClick={() => onProductSelect(product.id)}> {product.name} </li> )) )} </ul> </div> ); }
7. Keep State Close to Where It’s Needed to avoid unnecessary re-renders.
This example demonstrates toggling the visibility of text using a button.
❌ Bad: In this version, the state (isVisible
) is managed in the parent component. Even though only the Content
component uses the state, the parent and all its children will re-render when the state changes.
function App() { const [isVisible, setIsVisible] = React.useState(false); return ( <div className="App"> <Header /> <Content isVisible={isVisible} setIsVisible={setIsVisible} /> </div> ); } function Content({ isVisible, setIsVisible }) { return ( <div> <button onClick={() => setIsVisible((prev) => !prev)}> {isVisible ? "Hide" : "Show"} Content </button> {isVisible && <p>This is the content!</p>} </div> ); } function Header() { return <h1>My App</h1>; }
✅ Good: In this version, the state (isVisible
) is kept within the Content
component, ensuring that only Content
re-renders when the state changes. The Header
component remains unaffected.
function App() { return ( <div className="App"> <Header /> <Content /> </div> ); } function Content() { const [isVisible, setIsVisible] = React.useState(false); return ( <div> <button onClick={() => setIsVisible((prev) => !prev)}> {isVisible ? "Hide" : "Show"} Content </button> {isVisible && <p>This is the content!</p>} </div> ); } function Header() { return <h1>My App</h1>; }
8. With React.memo Re-render Components, Only When Props Changes
❌ Bad: In this example, every item re-renders when the parent updates the selected item state.
function App() { return ( <div className="App"> <Header /> <Content /> </div> ); } function Content() { const [isVisible, setIsVisible] = React.useState(false); return ( <div> <button onClick={() => setIsVisible((prev) => !prev)}> {isVisible ? "Hide" : "Show"} Content </button> {isVisible && <p>This is the content!</p>} </div> ); } function Header() { return <h1>My App</h1>; }
✅ Good: Here, we wrap the Item
component with React.memo
to ensure it only re-renders when its props (item
or isSelected
) change.
import React from "react"; function App() { const [selectedItemId, setSelectedItemId] = React.useState(null); const items = [ { id: 1, name: "Apple" }, { id: 2, name: "Banana" }, { id: 3, name: "Cherry" }, ]; console.log("App re-rendered"); // Logs every time App re-renders return ( <div> <h1>Item List</h1> <ul> {items.map((item) => ( <MemoizedItem key={item.id} item={item} isSelected={item.id === selectedItemId} onSelect={setSelectedItemId} /> ))} </ul> </div> ); } const MemoizedItem = React.memo(function Item({ item, isSelected, onSelect }) { console.log(`Item ${item.id} re-rendered`); // Logs only when Item's props change return ( <li onClick={() => onSelect(item.id)} style={{ color: isSelected ? "red" : "black", cursor: "pointer", }} > {item.name} </li> ); }); export default App;
9. Use Debouncing Technique to Optimize Search Inputs and Reduce Unnecessary API Calls
❌ Bad: The fetchSearchResults
function is called on every keystroke. This can lead to performance issues and excessive API requests.
import React, { useState } from "react"; function SearchApp() { const [searchTerm, setSearchTerm] = useState(""); const handleChange = (event) => { setSearchTerm(event.target.value); fetchSearchResults(event.target.value); // Triggers API call on every keystroke }; const fetchSearchResults = (query) => { console.log(`Fetching results for: ${query}`); // Simulate API call }; return ( <div> <h1>Search Input</h1> <input type="text" value={searchTerm} onChange={handleChange} placeholder="Search..." /> </div> ); } export default SearchApp;
✅ Good: The fetchSearchResults
function is only called after the user stops typing for 500ms.Prevents unnecessary API requests and improves performance.
import React, { useState, useEffect } from "react"; function SearchApp() { const [searchTerm, setSearchTerm] = useState(""); const [debouncedTerm, setDebouncedTerm] = useState(""); useEffect(() => { const timer = setTimeout(() => { setDebouncedTerm(searchTerm); // Update debounced term after delay }, 500); // 500ms debounce delay return () => clearTimeout(timer); // Cleanup timer on input change }, [searchTerm]); useEffect(() => { if (debouncedTerm) { fetchSearchResults(debouncedTerm); // Only fetch when debouncedTerm updates } }, [debouncedTerm]); const fetchSearchResults = (query) => { console.log(`Fetching results for: ${query}`); // Simulate API call }; return ( <div> <h1>Debounced Search Input</h1> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="Search..." /> </div> ); } export default SearchApp;
10. Assign a unique key
prop to each item in the Lists
❌ Bad: Missing Key or Using Index as Key
import React from 'react'; function ItemList({ items }) { return ( <ul> {items.map((item, index) => ( <li key={index}>{item.name}</li> {/* Using index as key */} ))} </ul> ); } export default ItemList;
✅ Good: Using Unique Keys in List.
import React from 'react'; function ItemList({ items }) { return ( <ul> {items.map((item) => ( <li key={item.id}>{item.name}</li> // Unique key based on item id ))} </ul> ); } export default ItemList;