DEV Community

Cover image for React Like a Pro: 10 Things I Regret Not Knowing Earlier
Ndeye Fatou Diop
Ndeye Fatou Diop

Posted on • Originally published at frontendjoy.com

React Like a Pro: 10 Things I Regret Not Knowing Earlier

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.

Section Divider

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 />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

💡 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}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

💡 You can see the result of the optimisation in this picture 👉 here.

Section Divider

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>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Section Divider

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';
Enter fullscreen mode Exit fullscreen mode

Good: Renaming happens automatically.

export function Button() {}
// Later...
import { Button } from './Button';
Enter fullscreen mode Exit fullscreen mode

You can find more arguments for named imports in this post 👉 101 React Tips & Tricks.

Section Divider

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

You can find more tips to avoid useEffect here 👉 You Might Not Need an Effect.

Section Divider

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.

Section Divider

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!
Enter fullscreen mode Exit fullscreen mode

Good: Use ESLint and plugins like eslint-plugin-react-hooks.

Section Divider

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.

Section Divider

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

Section Divider

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
Enter fullscreen mode Exit fullscreen mode

Good: We keep related files together.

src/
  components/
    Button/
      Button.js
      Button.css
      Button.test.js
Enter fullscreen mode Exit fullscreen mode

Section Divider

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.

Section Divider

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!

Section Divider

🐞 SPOT THE BUG

Section Divider

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.

If you want daily tips, find me on X/Twitter or on Bluesky.

Top comments (1)

Collapse
 
devshefali profile image
Shefali

Awesome article. Thanks for writing, Ndeye!