// blog / react-best-practices
January 25, 2024 · Technical · 3 min read
React Best Practices in 2024
Essential patterns and practices for writing clean, maintainable React code in modern applications.
ReactJavaScriptBest Practices
React Best Practices in 2024
As React continues to evolve, so do the patterns and practices we use to build applications. Here are some best practices I've found valuable in 2024.
1. Component Composition Over Props Drilling
Instead of passing props through multiple levels, use composition:
// ❌ Avoid: Props drilling
function App() {
const user = { name: "John" };
return <Header user={user} />;
}
function Header({ user }) {
return <UserProfile user={user} />;
}
// ✅ Better: Component composition
function App() {
const user = { name: "John" };
return (
<Header>
<UserProfile user={user} />
</Header>
);
}
function Header({ children }) {
return <header>{children}</header>;
}
2. Custom Hooks for Logic Reuse
Extract reusable logic into custom hooks:
// useDebounce.js
import { useState, useEffect } from 'react';
export function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchInput() {
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(search, 500);
useEffect(() => {
// Perform search with debouncedSearch
}, [debouncedSearch]);
return <input value={search} onChange={(e) => setSearch(e.target.value)} />;
}
3. Use TypeScript
TypeScript catches errors early and improves developer experience:
interface User {
id: number;
name: string;
email: string;
}
interface UserCardProps {
user: User;
onEdit: (user: User) => void;
}
export function UserCard({ user, onEdit }: UserCardProps) {
return (
<div>
<h3>{user.name}</h3>
<p>{user.email}</p>
<button onClick={() => onEdit(user)}>Edit</button>
</div>
);
}
4. Keep Components Small and Focused
Single Responsibility Principle applies to components:
// ❌ Avoid: Large, multi-purpose component
function UserDashboard() {
// Too much logic and UI in one component
}
// ✅ Better: Small, focused components
function UserDashboard() {
return (
<>
<UserProfile />
<UserStats />
<UserActivity />
</>
);
}
5. Error Boundaries
Always handle errors gracefully:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<App />
</ErrorBoundary>
6. Memoization When Needed
Use useMemo and useCallback for expensive operations:
function DataList({ items, filter }) {
const filteredItems = useMemo(() => {
return items.filter(item => item.includes(filter));
}, [items, filter]);
const handleClick = useCallback((id) => {
console.log('Clicked:', id);
}, []);
return filteredItems.map(item => (
<Item key={item.id} onClick={() => handleClick(item.id)} />
));
}
Conclusion
These practices have helped me write cleaner, more maintainable React code. Remember: write code for humans first, computers second.
What React best practices do you follow? Let me know!