← Back to Exhibition

Gallery II: The Sacred 4 UI Flows

CRUD operations and when to use each tool

Once we dive a layer deeper than inputs/outputs, we uncover that all UIs are a combination of these 4 UI flows (ordered from simplest to most complex):

  1. Read: simplest
  2. Create: is an input flow that saves / persists data
  3. Delete: is a combination of reading/displaying output, then deleting the data from a storage system (often from DB)
  4. Update: most complex. A combination of reading/displaying output, then updating the data via an http request to a server

"Learn to see simplicity in complexity"

Lens: How to view state management across the 4 UI flows

And why I believe Redux is overused

What's the purpose of reflecting on these four flows?

Under the hood, this came about when I tried to answer the question "when should I choose redux"? I ripped apart each core flow and tried to determine where each piece fits.

If you understand & dissect these four flows, you start to see the lanes that each tool occupies. It's to understand whether to use context, redux, or Tanstack query. Once you see this, you'll spend less time debating tooling or overthinking how these pieces fit together. You'll intuitively understand where each piece fits.

Each tool has its purpose and should be used intentionally. Let's dive into each of the 4 UI flows:

1. Read

Reading = displaying output

We show data in the UI all the time. Where does this displayed output originate? 9/10 times it should be the DB!

Recall, the db is the ultimate source of truth. This frame ultimately led me to the perspective that read is truly a HTTP fetch + caching problem. Think about this.

What goes into making a screen that displays data to read? Either it is:

a. something we need to get or

b. something we already have

For A something we need to get (remote data):

For this case, when we are retrieving data, reach for something that does HTTP caching (like Tanstack).

That way, while the data is being fetched you can either:

a. show a loading spinner

b. return cached data (*** if your UX/business allows stale data)

If you have cached data and your UX/business goals aligns so that it makes sense to show cached data (even though it's outdated). Then the solution/tool should be focused on caching/http responsibilities. At the time of this writing, Tanstack Query is best suited for this.

We're oversold on the idea that components need to put all their state in a heavy client-side store. If the source of truth is the DB/API then sharing that data and deduplicating network calls is a call to use Tanstack, not redux.

For B something we already have (client state):

Given that we already have this data, this means that it is either cached somewhere, in the URL, or stored in-memory in the app.

Often we need to read data across layers in a component tree. The classic pain known as prop drilling appeared to be solved by redux. It does so, with indirection. What was local state, now becomes global state.

Prop drilling visualization

Taking local state and putting it in a global redux store is not technically incorrect. It works. I'd argue though, that Redux's lane is for client-side state not server data. However, when it ends up sending an HTTP request, then Tanstack is objectively better in many ways as it handles caching + the HTTP layer elegantly.

So is it really worth the boilerplate of redux to avoid prop drilling? Probably not! If it's remote data, then you're better off using a HTTP caching layer and hooks (like Tanstack) to access the data.

For purely client-side inputs/state just prop drill when you don't have many layers. But for anything with more layers, perhaps you grab React context and useReducer. The idea that you need redux for all cross-cutting stuff is inaccurate.

Instead, I'd challenge you to ask whether it could have been stored/fetched from the HTTP layer? Could prop drilling be solved with better code organization with less layers or better composition? Perhaps fetching the data at a lower level?

If you're still struggling with prop drilling, Context is a better next step than redux. If you don't like context, perhaps Zustand will suite you better. However, for most apps out there, keeping client-state local (colocation) makes way more sense.

2. Create

Creating = saving input

Creation flows are more complex from a state management perspective. On one hand you have form state/input state and on the other hand you have a coordination problem.

Coordination becomes necessary when you share this state and aggregate it before you make the HTTP request to save the data. For example, once an input changes, an event handler function is called to update state.

Ultimately, this lands in a DB through an API request, but the state management/coordination dance happens before you send it. To manage state (input), you are actually engineering many underlying things to get to that point:

Input state iceberg visualization

The user may only see the UI input and validation messages/loading state, however much of frontend engineering is invisible.

To be continued....check back soon!

Going to deep dive into update and delete flows next! Next, we'll explore this through practical, hands-on examples. Stay tuned/share these mental models!

← Previous Gallery

Gallery I: Foundations

Next Gallery →

Gallery III (Coming soon)

Gallery Status

Additional content under curation

This gallery will expand to include detailed coverage of Create, Delete, and Update flows with practical examples.