If you're a junior developer feeling overwhelmed with React, you're not alone.
When I first started, I made plenty of mistakes—mistakes I could have avoided if I’d known these ten things from the start.
Let me help you skip those missteps.
📚 Download my FREE 101 React Tips And Tricks Book for a head start.
1. Drastically Improve App Performance with the children
Prop
The children
prop isn’t just for passing nested elements.
It’s a powerful tool with these benefits:
Avoid prop drilling by passing props directly to child components instead of routing them through the parent.
Write extensible code by modifying child components without altering the parent.
Avoid unnecessary re-renders of "slow" components (see example below 👇).
❌ Bad: MyVerySlowComponent
renders whenever Dashboard
renders, which happens every time the current time updates.
function App() {
// Some other logic…
return <Dashboard />;
}
function Dashboard() {
const [currentTime, setCurrentTime] = useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(new Date());
}, 1_000);
return () => clearInterval(intervalId);
}, []);
return (
<>
<h1>{currentTime.toTimeString()}</h1>
<MyVerySlowComponent />
</>
);
}
💡 You can see the slowness in action in the picture 👉 here, where I use React Developer Tool's profiler.
✅ Good: MyVerySlowComponent
doesn't re-render unnecessarily.
function App() {
return (
<Dashboard>
<MyVerySlowComponent />
</Dashboard>
);
}
function Dashboard({ children }) {
const [currentTime, setCurrentTime] = useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(new Date());
}, 1_000);
return () => clearInterval(intervalId);
}, []);
return (
<>
<h1>{currentTime.toTimeString()}</h1>
{children}
</>
);
}
💡 You can see the result of the optimisation in this picture 👉 here.
2. When to Use Refs vs. State
Refs are perfect for values that shouldn’t trigger re-renders.
Instead of defaulting to state, ask yourself: "Does this need to trigger a re-render when it changes?" If the answer is no, use a ref.
They are ideal for tracking mutable values like timers, DOM elements, or values that persist across renders but don’t affect the UI.
❌ Bad: We are storing intervalId
in the state. The component will re-render when intervalId
state changes, even if the UI stays the same.
function Timer() {
const [time, setTime] = useState(new Date());
const [intervalId, setIntervalId]= useState();
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1_000);
setIntervalId(id);
return () => clearInterval(id);
}, []);
const stopTimer = () => {
intervalId && clearInterval(intervalId);
};
return (
<>
<>Current time: {time.toLocaleTimeString()} </>
<button onClick={stopTimer}>Stop timer</button>
</>
);
}
✅ Good: We are storing intervalId
as a ref. This means we don’t have an additional state triggering a re-render.
function Timer() {
const [time, setTime] = useState(new Date());
const intervalIdRef = useRef();
const intervalId = intervalIdRef.current;
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1_000);
intervalIdRef.current = id;
return () => clearInterval(id);
}, []);
const stopTimer = () => {
intervalId && clearInterval(intervalId);
};
return (
<>
<>Current time: {time.toLocaleTimeString()} </>
<button onClick={stopTimer}>Stop timer</button>
</>
);
}
3. Prefer Named Exports Over Default Exports
Named exports make refactoring and debugging easier.
❌ Bad: Renaming requires manual updates.
export default function Button() {}
// Later...
import Button from './Button';
✅ Good: Renaming happens automatically.
export function Button() {}
// Later...
import { Button } from './Button';
You can find more arguments for named imports in this post 👉 101 React Tips & Tricks.
4. Avoid useEffect
at All Costs
Every app crash I’ve dealt with had useEffect
hiding in the code 😅.
Seriously, avoid useEffect
:
It is a sure way to end with excessive renders, obscure code, etc.
It makes it harder to follow the code flow
Instead, consider if you can achieve the same outcome without it.
❌ Bad: Calling onToggled
inside useEffect
.
function Toggle({ onToggled }) {
const [on, setOn] = React.useState(false);
const toggle = () => setOn(!on);
useEffect(() => {
onToggled(on);
}, [on]);
return (
<button onClick={toggle}>
{on ? 'on' : 'off'}
</button>
);
}
✅ Good: Call onToggled
when relevant.
function Toggle({ onToggled }) {
const [on, setOn] = React.useState(false);
const handleToggle = () => {
const next = !on;
setOn(next);
onToggled(next);
};
return (
<button onClick={handleToggle}>
{on ? 'on' : 'off'}
</button>
);
}
You can find more tips to avoid
useEffect
here 👉 You Might Not Need an Effect.
5. Understand React’s Lifecycle
Learn the lifecycle stages to avoid bugs and performance issues:
Mounting: When a component first renders.
Updating: When some state changes and React re-renders.
Unmounting: When a component is removed from the DOM.
6. Track Silly Mistakes with ESLint
ESLint can save you from subtle bugs, especially in hooks.
❌ Bad: Missing dependencies in useEffect
.
useEffect(() => {
console.log(data);
}, []); // Missing dependencies!
✅ Good: Use ESLint and plugins like eslint-plugin-react-hooks.
7. Debug Smarter with React DevTools
React DevTools is gold for debugging performance issues. Use the "Profiler" tab to find slow components.
❌ Bad: Relying solely on console.log
and timers to guess why your app is slow.
✅ Good: Use the Profiler to:
Find components with excessive renders.
Pinpoint expensive renders with flame graphs.
💡 Learn how to use it in this great guide.
8. Use Error Boundaries to Avoid Total Crashes
By default, if your application encounters an error during rendering, the entire UI crashes 💥.
To prevent this, use error boundaries to:
Keep parts of your app functional even if an error occurs.
Display user-friendly error messages and optionally track errors.
💡 Tip: you can use the react-error-boundary package
9. Organize Your Code Like a Pro
Clean code is maintainable code.
You can follow these simple tips:
Colocate components with related assets (i.e., styles, tests, …)
Keep files small.
Group related components into folders.
❌ Bad: When you delete Button.js
, it will be easy to forget about Button.css
.
src/
components/
Button.js
Modal.js
styles/
Button.css
✅ Good: We keep related files together.
src/
components/
Button/
Button.js
Button.css
Button.test.js
10. The React Website Has Everything You Need
Don’t overlook the official React docs. They’re full of examples, explanations, and best practices.
❌ Bad: Endlessly googling basic React concepts and landing on outdated blog posts.
✅ Good: Bookmark the official docs and make them your first stop when you’re stuck.
Summary
Mastering React takes time.
But these lessons will save you from my early mistakes.
Keep coding, stay curious, and remember—you’ve got this!
🐞 SPOT THE BUG
That's a wrap 🎉.
Leave a comment 📩 to share what you wished to know earlier about React.
And don't forget to drop a "💖🦄🔥".
If you're learning React, download my 101 React Tips & Tricks book for FREE.
If you like articles like this, join my FREE newsletter, FrontendJoy.
Top comments (2)
Awesome article. Thanks for writing, Ndeye!
Glad you liked it Shefali 😀