The truth is, components are deceptively simple. It’s easy to get started—define a function, return some JSX, and call it a day. But writing components that are clean, maintainable, and pleasant to work with? That’s a whole different ballgame.
Without realizing it, we create components that are:
- Too big to understand at a glance.
- Painfully difficult to test.
- So tightly coupled they’re impossible to reuse.
- Sluggish because of poor performance decisions.
In this post, I’ll walk you through the most common mistakes developers make with React components. More importantly, I’ll show you how to fix them without tearing apart your entire app.
Whether you’re just getting started or have years of experience, these tips will help you write components that are not just functional, but a joy to maintain.
The "Everything Component" Anti-pattern
Let’s talk about the classic blunder we’ve all made: the everything component. You’ve seen it—it starts small and innocent, maybe as a simple form or dashboard. Fast forward a bit, and now it’s managing state, handling API calls, formatting data, and possibly brewing your morning coffee.
// Please, no more of this const UserDashboard = () => { const [userData, setUserData] = useState(null); const [orders, setOrders] = useState([]); const [notifications, setNotifications] = useState([]); const [settings, setSettings] = useState({}); const [isEditing, setIsEditing] = useState(false); const [activeTab, setActiveTab] = useState('profile'); // 15 separate useEffects // 10 different event handlers // Multiple conditional renders // 300+ lines of chaos };
Sound familiar?
How to Tell If You’re Guilty
Your component might have turned into an "everything component" if:
- State overload: You’re tracking more than 3-4 separate pieces of state.
- Endless scrolling: You spend too much time hunting for specific functions or logic.
- Dependency bloat: Your useEffect dependencies look like your weekly shopping list.
- Feature creep denial: You keep telling yourself, just one more feature won’t hurt.
Breaking It Down
The solution? Instead of a single everything component, split responsibilities into smaller, focused pieces.
// A cleaner, smarter approach const UserDashboard = () => { return ( <div> <UserProfile /> <OrderHistory /> <NotificationCenter /> <UserSettings /> </div> ); };
Key Principle: Logic > Layout
When refactoring, don’t break components based on how they look on the screen. Split them by responsibility. Ask yourself: Does this functionality deserve its own component? If it’s handling something distinct—like user profiles or order history—it probably does.
Tip: A good component does one thing and does it well. If you struggle to describe its purpose in a single sentence, chances are it’s trying to do too much.
Prop Drilling Hell
Let’s discuss the not-so-fun game of “pass the props”. If you’ve ever passed the same prop through multiple components just to reach a deeply nested child, you’ve been stuck in prop drilling hell.
// Please, no more of this const UserDashboard = () => { const [userData, setUserData] = useState(null); const [orders, setOrders] = useState([]); const [notifications, setNotifications] = useState([]); const [settings, setSettings] = useState({}); const [isEditing, setIsEditing] = useState(false); const [activeTab, setActiveTab] = useState('profile'); // 15 separate useEffects // 10 different event handlers // Multiple conditional renders // 300+ lines of chaos };
This approach isn’t just annoying—it creates long-term problems. Imagine needing to rename the user prop. Suddenly, you’re updating it in five or more places. Worse, you end up tying components to data they don’t even use.
How to Fix This
There’s no need to play hot potato with your props. Here are two practical solutions to avoid drilling altogether.
1. Use Context for Shared Data
If a piece of data is accessed across different parts of your app, React’s Context API can simplify things.
// A cleaner, smarter approach const UserDashboard = () => { return ( <div> <UserProfile /> <OrderHistory /> <NotificationCenter /> <UserSettings /> </div> ); };
2. Use Composition for Flexibility
Instead of forcing props through layers, restructure components so they only pass down what’s needed.
// This is exhausting const App = () => { const [user, setUser] = useState({}); return ( <Layout user={user}> <Sidebar user={user}> <Navigation user={user}> <UserMenu user={user} /> </Navigation> </Sidebar> </Layout> ); };
Key Takeaways
Context works well for app-wide data like user information, themes, or global settings. However, it’s not always the best option—don’t overuse it. For localized state, think about whether your component structure can be adjusted to avoid drilling entirely.
The goal is to make your components clear and maintainable. Avoiding prop drilling will save you time, frustration, and countless headaches down the road.
Premature Optimization Traps
You've probably heard that famous quote about premature optimization being the root of all evil. Well, welcome to component-level evil. I'm talking about those times we try to make everything reusable before we even know if we'll need it twice.
Here's what it usually looks like:
const UserContext = createContext(); const App = () => { const [user, setUser] = useState({}); return ( <UserContext.Provider value={user}> <Layout> <Sidebar> <Navigation /> </Sidebar> </Layout> </UserContext.Provider> ); }; // Use it only where needed const UserMenu = () => { const user = useContext(UserContext); return <div>{user.name}</div>; };
Let your components evolve naturally. Build for what you know you need today. If new requirements arise, add features when you can clearly justify them. Premature optimization wastes time, adds complexity, and rarely pays off.
Remember: The YAGNI principle (You Aren’t Gonna Need It) applies to components too. The best abstractions come when you’ve actually encountered the problems they’re solving. Over-engineering might feel proactive, but simplicity wins every time.
Side Effect Mismanagement
Here’s a classic example of bad effect management. Look familiar?
// Focused components for better clarity const Navigation = ({ children }) => { return <nav>{children}</nav>; }; // Pass data only where required const App = () => { const user = useUser(); return ( <Layout> <Navigation> <UserMenu user={user} /> </Navigation> </Layout> ); };
Common Mistakes and Fixes
1) Messy Data Fetching
Bad data handling creates more bugs than it solves. Here's a cleaner approach:
// Behold, the over-engineered button const Button = ({ children, variant = 'primary', size = 'medium', isFullWidth = false, isDisabled = false, isLoading = false, leftIcon, rightIcon, onClick, customClassName, style, loadingText = 'Loading...', tooltipText, animationType, // ... 10 more props }) => { // 50 lines of prop processing logic return ( <button className={generateComplexClassNames()} > <h3> Why This Hurts </h3> <ul> <li>Your “simple” button now requires an instruction manual.</li> <li>Most of those 15+ props will never be used.</li> <li>Making updates becomes risky because you have to account for endless combinations.</li> <li>Writing tests becomes painful, with a hundred possible scenarios to consider.</li> </ul> <h3> Better Approach: </h3> <p>Instead of building for every imaginable scenario, start small and let your components grow as needed.<br> </p> <pre class="brush:php;toolbar:false">// Start simple const Button = ({ children, onClick, variant = 'primary' }) => { return ( <button className={`btn btn-${variant}`} onClick={onClick} > {children} </button> ); } // Create specific buttons when you actually need them const LoadingButton = ({ isLoading, children, ...props }) => { return ( <Button {...props}> {isLoading ? 'Loading...' : children} </Button> ); }
2) Forgetting Cleanup
Always clean up after yourself:
const UserProfile = ({ userId }) => { const [user, setUser] = useState(null); const [posts, setPosts] = useState([]); // Dependency array woes useEffect(() => { fetchUserData(userId); fetchUserPosts(userId); // No cleanup? Yikes. }, []); // eslint-disable-line react-hooks/exhaustive-deps // Multiple effects, all tangled useEffect(() => { const subscription = subscribeToUserStatus(userId); }, [userId]); // Cleanup? What cleanup? useEffect(() => { const interval = setInterval(checkNotifications, 5000); }, []); };
3) Ignoring Race Conditions
Avoid overlapping requests with this technique:
// Improved version const UserProfile = ({ userId }) => { const { data: user, isLoading } = useQuery( ['user', userId], () => fetchUserData(userId) ); // Keep concerns separate const { data: posts } = useQuery( ['posts', userId], () => fetchUserPosts(userId), { enabled: !!user } ); };
Quick Tips
- Think before using useEffect: Sometimes, you might not need it at all.
- Keep it focused: One effect should handle one responsibility.
- Always clean up: Subscriptions, intervals, and event listeners need attention.
- Use the right tools: Libraries like React Query simplify data fetching and caching.
- Don't cheat with eslint-disable: Fix the dependency issues instead of hiding them.
Performance Blind Spots
Let’s talk about those sneaky performance issues. They’re the kind that fly under the radar because everything seems fine—until it isn’t. Let’s uncover these silent culprits and see how to fix them.
The Problem
Here’s a component with some subtle performance pitfalls:
// Please, no more of this const UserDashboard = () => { const [userData, setUserData] = useState(null); const [orders, setOrders] = useState([]); const [notifications, setNotifications] = useState([]); const [settings, setSettings] = useState({}); const [isEditing, setIsEditing] = useState(false); const [activeTab, setActiveTab] = useState('profile'); // 15 separate useEffects // 10 different event handlers // Multiple conditional renders // 300+ lines of chaos };
Can you spot the issues? Let’s break them down.
Fixes
1) Memoize Expensive Calculations
Instead of recalculating everything on each render, use useMemo to cache results:
// A cleaner, smarter approach const UserDashboard = () => { return ( <div> <UserProfile /> <OrderHistory /> <NotificationCenter /> <UserSettings /> </div> ); };
This avoids recalculating data and recreating event handlers on every render. It also prevents unnecessary re-renders of child components with memo.
2) Efficient State Updates
Poor state management can also kill performance. Here’s a better way to handle updates like search results:
// This is exhausting const App = () => { const [user, setUser] = useState({}); return ( <Layout user={user}> <Sidebar user={user}> <Navigation user={user}> <UserMenu user={user} /> </Navigation> </Sidebar> </Layout> ); };
Debouncing ensures we’re not fetching data on every keystroke, reducing unnecessary API calls and re-renders.
Quick Performance Tips
- Don’t overuse memoization: Optimize only when it’s worth it.
- Avoid inline functions: Stable references improve performance.
- Keep props predictable: Shallow and stable props help components stay efficient.
- Break up large lists: Tools like react-window can handle big datasets gracefully.
- Move state closer: Only manage state where it’s actually needed.
- Profile with DevTools: Always measure before optimizing.
Conclusion
Building components isn’t rocket science, but let’s be real—it’s easy to stumble into bad habits. I’ve made every one of these mistakes (and sometimes still do). That’s okay. What matters is catching them early, fixing them, and avoiding rough codebase.
Quick Checklist for Better Components
? Single Responsibility: If you can’t summarize your component’s job in one sentence, it’s time to break it apart.
? Props Management: Passing props through layers upon layers? Consider using Context or leveraging composition instead.
? State & Effects: Keep effects focused, clean them up properly, and let modern tools handle complex data fetching.
? Performance: Don’t optimize for the sake of it—measure first. Use tools like memo, useMemo, and useCallback smartly when needed.
? Start Simple: Solve the problems you have now, not the ones you might have someday.
The best components aren’t flashy or overly clever—they’re the ones your team can read and maintain without groaning six months down the line.
Remember: these aren’t hard rules, just guidelines. You’ll break them sometimes, and that’s perfectly fine. The goal isn’t perfection—it’s building components that don’t make you question your career choices when you revisit them later.
The above is the detailed content of Stop Making These Component Mistakes. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Java and JavaScript are different programming languages, each suitable for different application scenarios. Java is used for large enterprise and mobile application development, while JavaScript is mainly used for web page development.

JavaScriptcommentsareessentialformaintaining,reading,andguidingcodeexecution.1)Single-linecommentsareusedforquickexplanations.2)Multi-linecommentsexplaincomplexlogicorprovidedetaileddocumentation.3)Inlinecommentsclarifyspecificpartsofcode.Bestpractic

The following points should be noted when processing dates and time in JavaScript: 1. There are many ways to create Date objects. It is recommended to use ISO format strings to ensure compatibility; 2. Get and set time information can be obtained and set methods, and note that the month starts from 0; 3. Manually formatting dates requires strings, and third-party libraries can also be used; 4. It is recommended to use libraries that support time zones, such as Luxon. Mastering these key points can effectively avoid common mistakes.

PlacingtagsatthebottomofablogpostorwebpageservespracticalpurposesforSEO,userexperience,anddesign.1.IthelpswithSEObyallowingsearchenginestoaccesskeyword-relevanttagswithoutclutteringthemaincontent.2.Itimprovesuserexperiencebykeepingthefocusonthearticl

JavaScriptispreferredforwebdevelopment,whileJavaisbetterforlarge-scalebackendsystemsandAndroidapps.1)JavaScriptexcelsincreatinginteractivewebexperienceswithitsdynamicnatureandDOMmanipulation.2)Javaoffersstrongtypingandobject-orientedfeatures,idealfor

JavaScripthassevenfundamentaldatatypes:number,string,boolean,undefined,null,object,andsymbol.1)Numbersuseadouble-precisionformat,usefulforwidevaluerangesbutbecautiouswithfloating-pointarithmetic.2)Stringsareimmutable,useefficientconcatenationmethodsf

Event capture and bubble are two stages of event propagation in DOM. Capture is from the top layer to the target element, and bubble is from the target element to the top layer. 1. Event capture is implemented by setting the useCapture parameter of addEventListener to true; 2. Event bubble is the default behavior, useCapture is set to false or omitted; 3. Event propagation can be used to prevent event propagation; 4. Event bubbling supports event delegation to improve dynamic content processing efficiency; 5. Capture can be used to intercept events in advance, such as logging or error processing. Understanding these two phases helps to accurately control the timing and how JavaScript responds to user operations.

Java and JavaScript are different programming languages. 1.Java is a statically typed and compiled language, suitable for enterprise applications and large systems. 2. JavaScript is a dynamic type and interpreted language, mainly used for web interaction and front-end development.
