DEV Community

ak0047
ak0047

Posted on

Why React Feels So Confusing: My Observations

Introduction

I’ve worked with various programming languages and frameworks, both professionally and through self-study.

Usually, after reading a basic book or going through a tutorial, I can get the hang of things by looking up details online as needed.

However, React didn’t fit into my usual approach at all.

Even after building a few React apps, I often found myself unsure where to write code when I wanted to make small changes to existing functionality.

This made me wonder: why is React so hard to grasp?

In this article, I’ll share my thoughts. I hope it can help people who are just starting with React, or those struggling with it, find a clearer way to understand it.


Reason 1: Multiple Layers of Processing

Let’s look at a simple example:

const [count, setCount] = useState(0);

return (
  <button onClick={() => setCount(count + 1)}>
    {count}
  </button>
);
Enter fullscreen mode Exit fullscreen mode

This code increases a number when the button is clicked.
However, the actual process behind the scenes is more complex than it seems.

Here’s what happens step by step:

  1. The JS/TS code written by the developer calls React’s API (updates the state).
  2. React determines whether re-rendering is necessary.
  3. React updates the virtual DOM.
  4. The browser updates the actual DOM based on React’s instructions.

In other words:

  • Developer-written JS/TS code
  • React’s internal processing
  • Browser rendering

…all happen at different layers. This separation makes it hard to intuitively understand which code runs at which layer, which I think is one reason why React feels confusing.


Reason 2: Triggers for Code Execution Are Hard to Identify

In React, code can be triggered by multiple events:

  • When a button is clicked (event)
  • When state changes
  • When props change

For example:

useEffect(() => {
  console.log("updated");
}, [count]);
Enter fullscreen mode Exit fullscreen mode

This code:

  • Doesn’t explicitly call a function
  • Doesn’t involve a direct button click

…but still runs whenever count changes.

This makes it difficult to intuitively understand from the code itself when and why a piece of code will execute, which adds to React’s learning curve.


Reason 3: Display Elements and Logic Are Mixed

In React, JSX mixes the elements displayed in the browser with the logic controlling them:

return (
  <div>
    {count > 0 && <p>{count}</p>}
  </div>
);
Enter fullscreen mode Exit fullscreen mode

While it looks similar to HTML:

  • Conditional expressions
  • Variable evaluation
  • Display logic

…are all included.

This mixing makes it hard to immediately tell which elements will appear under which conditions, contributing to React’s complexity.


Summary

React feels confusing because of:

  • Multiple layers of processing
  • Triggers that are not obvious from the code
  • Blurred boundaries between display elements and logic

These factors combine to create a sense of “I don’t know what’s happening, when, or where”.
On top of this, the variety of JS/TS syntax and styling approaches only adds to the difficulty.

I’m still learning, but being aware of these points helps me approach React more consciously.


💬 How about you?

  • What aspects of React did you find most confusing when you started?
  • How do you usually keep track of different layers (state, React processing, DOM updates)?
  • Do you have any tips for making React code easier to reason about?

Top comments (4)

Collapse
 
miketalbot profile image
Mike Talbot ⭐

You're thinking imperatively, where React is naturally declarative. You are thinking, "Where does my code run and at what time?" rather than declaring "What my code should do" and then not caring when it's being run. It's natural for this to feel odd when you are first learning it, but it's a pattern I constantly use in architecture I design. The reason for its power is the ability to decouple things.

Now it's fair to say that React running your code whenever it wants to can lead to real performance bottlenecks, and a need to understand when to optimise; these are awkward concepts and probably feel like a waste of your time. I'd just realise that everything is a compromise - declarative has massive improvements over imperative in the real world. Things like Svelte fix a lot of the problems by making a compiler - effectively making a different language.

I've shipped apps in Svelte, React, Angular and Vue - my current team project is on React because of the number of people who know it, but there are pluses and minuses with all of them.

Collapse
 
thiraisuvadu_dev profile image
திரைச்சுவடு • Edited

Here are the most confusing aspects of React form my perspective when I'm in beginner, summarized with Hot Points:
State vs. Variables (The Re-render Trigger)

  • Updating a standard JavaScript variable (e.g., let count = 0) does not update the UI. You must use useState to signal React to "re-paint" the screen. The useEffect Infinite Loop (Dependency Logic)
  • If you update a state inside useEffect and include that same state in the dependency array [], you create an infinite loop that can crash your browser. Props Drilling (The Middleman Problem)
  • Passing data through 5 levels of components just to reach a "Grandchild" that needs it, even when the intermediate components don't use the data at all. This leads to messy, hard-to-maintain code. Stale Closures (The Snapshot Concept)
  • React state acts like a snapshot. If you trigger a setTimeout, it will remember the state value from the moment it was called, not the "live" updated value, which feels counter-intuitive to JS beginners. JSX Constraints (The Single Root Rule)
  • You cannot return two adjacent HTML elements (like two tags) without wrapping them in a single parent or a Fragment <> ... </>. JSX is a function call, and a function can only return one thing. One-Way Data Flow (The Upward Struggle)
  • Data only flows down. If a child needs to change something in a parent, you must pass a function down as a prop, which is a significant shift from "Two-Way Binding" seen in other frameworks.
Collapse
 
thiraisuvadu_dev profile image
திரைச்சுவடு

To make your React code easier. I am following the same, follow these Hot Points:

  • Logic Isolation (Custom Hooks):Move all your data fetching and complex calculations out of the UI. If a component is more than 50 lines, move the logic into a useSomething() hook.
  • Derived State (Stop Syncing): Never create a new state if you can calculate the value from existing props or state. For example: don't store filteredList in state; calculate it directly from list and searchQuery during render.
  • The "Early Return" Pattern: Handle Loading, Error, or Empty states at the very top of your function. This prevents deeply nested if/else or ternary operators in your main JSX.
  • Prop-Driven UI (Pure Components): Aim to make most of your components "dumb." They should just take props and show them, making them easy to test and reuse without side effects.
  • Feature-Based Folders: Stop using folders like /components or /hooks. Group everything by feature (e.g., /features/auth contains its own UI, hooks, and utils). This makes it easier to find related code.
  • TypeScript as Documentation: Use strict types for props and state. It acts as an "auto-updating manual" that tells you exactly what data a component expects without you having to read the logic.
  • Single Source of Truth: Always "Lift State Up" to the nearest common parent. Avoid having the same data stored in two different states, which inevitably leads to UI bugs.
Collapse
 
ingosteinke profile image
Ingo Steinke, web developer • Edited

Many "React problems" are not unique to React. JavaScript/TypeScript has come a long way historically resulting in inconsistent concepts and different possible syntax to achieve the same thing. Syntax might be a matter of taste or what you learned first, like classic vs. arrow functions. Many developers prefer React hooks over the legacy lifecycle methods, while others miss the descriptive names and more obvious logic despite verbosity and missing customization. JSX is optional (in theory) and other libraries/frameworks use JSX and even MDX syntax ass well.

React apps differ depending on third party components and extensions as well. I like to combine React with Astro, so that the static parts of an app can stay static and dynamic rendering with its performance issues and logical considerations is restricted to those parts that really need it.