← Back to engineering journal

React Is Not the Hard Part.

Components are easy. The difficult part starts when state, data flow, API calls and application architecture begin growing together.

React is surprisingly easy to start with. That is exactly why people underestimate it.

You create a component, return some JSX, pass a few props and suddenly you have a working interface.

The first few days feel almost magical.

Then the application becomes bigger.

A component needs data from an API. Another component needs the same data. A user logs in. The navbar needs to know who the user is. The cart needs to survive navigation. The checkout page needs the cart. The admin panel needs a completely different permission model.

That is where React stops being about components and starts being about architecture.

FIELD NOTE 002

Learning React syntax teaches you how to write React. Building a real application teaches you why architecture matters.

01 / THE ILLUSION

The tutorial makes everything look smaller than it is.

Most beginner React examples are intentionally simple. That is useful for learning syntax, but it hides the problems that appear in production-style applications.

THE EASY VERSION REACT
function Product() {
  return (
    <div>
      <h2>Laptop</h2>
      <button>Buy</button>
    </div>
  );
}

There is nothing wrong with this component. The problem begins when the button needs to update a cart, authenticate a user, call an API, display a loading state, handle an error and remain consistent with another page.

02 / COMPONENTS

Components are boundaries.

One of the most useful ideas in React is that the interface can be divided into smaller pieces.

But a component should not automatically become a place where every piece of logic gets dumped.

I prefer thinking about components as boundaries with a clear responsibility.

BETTER STRUCTURE PROJECT
ProductCard
 ├── ProductImage
 ├── ProductInfo
 ├── Price
 └── AddToCartButton

Cart
 ├── CartItem
 ├── CartSummary
 └── CheckoutButton

This does not mean every five lines of JSX need a new component. It means the boundaries should make the application easier to understand.

03 / STATE

State is where things get interesting.

A static interface is easy. An application with changing data is not.

A simple local state might look like this:

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

function increase() {
  setCount(count + 1);
}

That works perfectly when the state belongs to one component.

Problems start when the same information is needed in multiple places.

THE QUESTION Where should this state actually live?

That question is more important than knowing another React hook.

If the state belongs to a single component, keep it local. If several related components need it, move it to their common parent. If unrelated areas of the application need it, a shared state solution may make more sense.

04 / CONTEXT

Context is useful. It is not magic.

Context can solve a very practical problem: passing shared data through many component levels without manually forwarding props through every layer.

Authentication is a good example.

AUTH CONTEXT REACT
const AuthContext = createContext();

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

Now components such as the navbar, profile page and protected routes can access authentication information without creating long prop chains.

But putting every piece of application state into one giant context can create a different problem: everything becomes connected to everything.

Shared state should still have boundaries.

05 / API

The frontend is only half the conversation.

A React application often becomes much more interesting once it starts communicating with a backend.

A typical request looks simple:

API REQUEST AXIOS
const response = await axios.get(
  "/api/products"
);

setProducts(response.data);

But a real request has more states than success.

REAL REQUEST STATE
idle
  ↓
loading
  ↓
success

or

loading
  ↓
error

A good UI should account for those states instead of assuming the network will always cooperate.

06 / ARCHITECTURE

A growing React project needs a map.

Once the project becomes large enough, throwing everything into a components folder becomes difficult to maintain.

01 PAGES Routes and screens
02 COMPONENTS Reusable UI
03 CONTEXT Shared state

Then the backend and data layer sit outside the React interface. This gives the application a mental model.

A developer joining the project should be able to look at the structure and understand where a new feature belongs.

07 / MISTAKES

The mistakes taught me more than the components did.

01 One component doing everything

UI, API calls, state management and business logic all inside one component quickly becomes difficult to debug.

02 Global state for everything

Not every value needs to be globally accessible. Local state is often simpler and safer.

03 Ignoring loading and error states

A successful API response is only one possible outcome. Real applications have failures.

04 Designing after coding

A few minutes spent thinking about data flow can save hours of moving code around later.

08 / FINAL THOUGHT

React is the tool. Architecture is the skill.

Learning hooks, JSX and components is important. But those things are only the beginning.

The real challenge is deciding how the pieces should communicate.

Where does the state live? Who owns the data? Which component should know about the API? Which information should be global? What happens when the request fails? What happens when the application becomes ten times larger?

Those questions do not have a single React API as their answer.

FIELD NOTE / 002 Don't just learn
how React works.
Learn how your
application works.
PREVIOUS ARTICLE ← I Built It. Then I Broke It. NEXT ARTICLE Your Website Isn't Slow →

Have something
worth building?

Need a developer or want to discuss an interesting project? Let's build something useful.

Contact Prince →