Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ReactNext.jsJune 23, 20265 min read

React Purity and Side Effects: Building Predictable UI Components

Master React purity to build predictable UI. Learn how functional programming in React keeps your components bug-free and easy to maintain as your app grows.

ReactFrontend EngineeringFunctional ProgrammingWeb DevelopmentJavaScriptNext.jsTutorial

During a late-night debugging session on a critical checkout flow last month, I found myself staring at a component that re-rendered four times for a single click. Every time it updated, the button label flickered between "Loading" and "Submit." It was a classic case of a component trying to be too clever with its state and side effects, making the UI feel jittery and unpredictable.

If you’ve been building with React for a while, you’ve likely felt this pain. The solution isn't more complex state management; it’s embracing React purity. When you treat your components like pure functions, you stop fighting the framework and start working with it.

What Does React Purity Actually Mean?

In functional programming, a pure function is one where the output is determined solely by its input, with no side effects. In the context of React, this means that given the same props, your component should always return the same JSX.

If your component is reaching outside of itself—writing to the DOM, calling an API, or modifying a global variable—it’s no longer pure. While React isn't a purely functional library, it leans heavily on these principles. When your components are pure, React reconciliation and component state persistence: A mental model becomes much easier to reason about because you aren't fighting hidden state mutations.

The Cost of Impure Components

Early in my career, I constantly put logic directly inside the component body. I’d initialize a socket connection or parse a large JSON object right in the function scope.

Here is what that looks like:

JSX
function UserProfile({ userId }) {
  // DON'T DO THIS
  const data = JSON.parse(localStorage.getItem(CE9178">'cache')); 
  
  return <div>{data.name}</div>;
}

Every time UserProfile renders, it hits localStorage. If that read operation is slow or if the cache is malformed, your UI hangs. Worse, if you decide to change how your React component architecture: Mastering Colocation for Better Maintainability handles data, you’ll have to hunt through these "impure" components to find the side effects.

Identifying the Side Effect

A side effect is anything that affects something outside the scope of the function being executed. Common culprits include:

  • console.log (yes, even this!)
  • API calls (fetch or axios)
  • Direct DOM manipulation (e.g., document.title = ...)
  • Setting up subscriptions or event listeners

When these live in your render path, your component becomes unpredictable. You lose the ability to rely on the "UI as a function of state" mental model.

Achieving Predictable UI through Separation

To build a predictable UI, you need to move side effects into dedicated lifecycle hooks like useEffect. By isolating these, you keep your component body clean and focused on rendering.

If you find your logic getting too messy, it’s often a sign that you need to move that logic into a custom hook or a service layer. Think about React state management: Mapping Your Next.js Component Hierarchy as a way to push complexity upward, away from the view layer.

A Better Approach

Instead of doing work during render, delegate it to the framework:

JSX
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // Side effect is now isolated
    api.fetchUser(userId).then(setUser);
  }, [userId]);

  if (!user) return <Loading />;
  return <div>{user.name}</div>;
}

By moving the fetch into useEffect, the render function remains pure. It receives userId, and it returns a loading state or a user profile. It doesn't care how the user was fetched, just that the data is eventually available.

Functional Programming in React: Why It Matters

Functional programming in React isn't just an academic exercise; it’s a defensive coding strategy. When your components are pure, they are easier to test. You can pass in mock props and verify the output without setting up a complex environment.

We’ve found that when we enforce this "render-only" philosophy, our codebases survive the growth of a team much better. If you’re interested in how this applies to large-scale apps, check out how we manage component architecture that survives a growing team in Next.js.

Frequently Asked Questions

Is it ever okay to have a side effect in the render body? Generally, no. React renders can happen multiple times for a single update. If you perform a side effect during render, it might trigger multiple times or cause infinite loops.

Does this mean I can't use Math.random() or new Date()? Technically, those are impure because they return different values every time. If your component output depends on them, you might see "hydration mismatches" in Next.js. Always pass the result of those as props or initialize them in a useEffect.

How does this relate to state machines? Using state machines (like XState) is a great way to formalize the transitions of your component. It helps you avoid "boolean soup" and makes your UI logic even more predictable.

Wrapping Up

My biggest mistake as a junior was trying to force components to do too much. I wanted them to be "all-in-one" modules. I learned the hard way that modularity is the real goal.

Next time you're stuck debugging a weird UI glitch, stop and ask: "Is this component actually pure?" If the answer is no, start by moving those side effects out. It’s usually about two hours of refactoring, but it saves you days of head-scratching later. I’m still learning how to balance this in complex legacy codebases, but aiming for purity is the closest thing I’ve found to a "silver bullet" in frontend engineering.

Back to Blog

Similar Posts

ReactNext.jsJune 22, 20264 min read

React reconciliation and component state persistence: A mental model

React reconciliation determines how component state persistence works during re-renders. Learn how React Fiber maintains your UI state across cycles.

Read more
Close-up of JavaScript code on a laptop screen, showcasing programming in progress.
React
Next.js
June 20, 2026
4 min read

React State Management: How to Lift State Up Effectively

React state management gets easier when you learn how to lift state up. Discover how to sync sibling components and build a predictable data flow today.

Read more
ReactNext.jsJune 22, 20264 min read

React derived state: Stop using useEffect for data calculations

React derived state is the key to faster components. Learn how to stop abusing useEffect for data transformations and simplify your React performance optimization.

Read more