State Management in a React App in 2021

A most common pattern is using React for UI rendering and Redux global state management together with Flux pattern. Before you put your app into Redux rabbit hole consider whether you actually need Redux. This is a very higher-level view of a frontend component architecture. We should shape architecture to promote code readability, maintainability, debuggability, testability, and better separation of concerns.

I’ll explain each section in detail from the bottom up.

Presentational Components

Presentational components are…

  1. Only responsible for the DOM markup.
  2. They get data via props and communicate back only via callbacks we passed on as props.
  3. They might have a state, but most of the time, the state is lifted up to a container component and passed to the presentational component via props.
  4. Since presentational components are loosely coupled it’s really easy to reuse them.

Container Components

  1. Fetching data from backend APIs and forward them as props for presentational components.
  2. Composing dispatchable actions as callbacks for presentational components.
  3. Passing data from global store to presentational components.
  4. Storing local state/callbacks to trigger actions lifted from presentational components.

Container components are not reusable but we can break them into reusable smaller units like custom hooks which I will discuss later.


  1. Lots of boilerplate code to fetch data from an API. It’s a boring repetitive task.
  2. Pollutes the global state.
  3. Having to make redux state async adds additional complexity to the code base.
  4. API calls are duplicated if we want the same data in several components. So it impacts application performance. Of course, you can dedupe them with prop drilling but again it’s additional complexity.
  5. Duplicated API calls also mean additional renderings. Again it impacts application performance.

And the answer I found for the above problem is …

Alternative Approach to Data fetching — Custom Hooks

SWR makes our life easier with…

  1. A builtin cache for request deduplication
  2. Builtin optimistic rendering — first return the muted data from cache (stale), then send the fetch request (revalidate), and finally, come with the up-to-date data.
  3. Automatic retry on error.
  4. Polling on interval.
  5. Data revalidation on focus (If the user switch browser tabs or returned after locked ).

Redux Store

That’s it, this is how I think we should build a frontend React app in 2021. If you have any questions/suggestions feel free to leave a comment. In the next post, I will share how to accommodate automated testing into this architecture.