April 8, 2026

A Repeatable Redux Module Pattern for a Growing React Native App

Somewhere around the third or fourth new Redux domain added to the NewsFeed rebuild, it became obvious that the question “where does this code go” needed exactly one answer, every time. So each domain — channel, the right-side menu, the official channel page menu, the news feed itself — ended up split into the same four files:

  • type.js — action type constants
  • action.js — action creators (including thunks for API calls)
  • reducer.js — how those actions change state
  • service.js — the actual API calls

Nothing exotic. What made it worth writing about is what it replaced.

The problem it solved

Before this got applied consistently, state that logically belonged together would end up split across whatever files were most convenient at the time an action was needed — a reducer here, an inline dispatch there, an API call made straight from a component because a proper action creator didn’t exist yet for that one specific case. That’s exactly the kind of thing that produces the state-sync bugs I’ve written about elsewhere on this feed: a screen that reads state from one place and writes to another eventually drifts.

Splitting strictly into type/action/reducer/service made a few things structurally true instead of just intended:

  • Every state mutation had to go through a named, typed action — no direct reducer mutations from a one-off dispatch
  • Every API call lived in exactly one place per domain — no more duplicated fetch logic sitting inside two different components doing slightly different things to the same endpoint
  • Adding a new async operation meant touching the same four files in the same order every time, which is the kind of repetition that’s boring in a good way

What it looks like in practice

The type.js files follow a consistent request/success/failure/clear convention per operation:

const ActionType = {
  FETCH_NEWS_FEED_REQUEST: '@NEW_FEED/FETCH_NEWS_FEED_REQUEST',
  FETCH_NEWS_FEED_SUCCESS: '@NEW_FEED/FETCH_NEWS_FEED_SUCCESS',
  FETCH_NEWS_FEED_CLEAR: '@NEW_FEED/FETCH_NEWS_FEED_CLEAR',
  FETCH_NEWS_FEED_FAILURE: '@NEW_FEED/FETCH_NEWS_FEED_FAILURE',
  // ...
}

Each domain gets its own namespaced prefix (@NEW_FEED/, @NEW_FEED_CHANNELS/), so action types never collide across domains even though the request/success/failure/clear suffixes repeat everywhere. The _CLEAR action in particular is easy to forget when you’re moving fast, but it’s what lets a screen reset a list’s state when the user navigates away, instead of showing stale data for a split second the next time they come back.

Why this mattered more than any single decision inside it

None of the individual choices here are unusual — request/success/failure actions are Redux 101, and splitting reducers by domain is standard practice. The value wasn’t in any one pattern being clever, it was in applying the same pattern to every domain without exception, across four different feature areas built at different points in the same project. That consistency is what made it possible to open a Redux module I’d never touched before and immediately know where to find the API call, where to find the action creator, and where to find the state shape — without reading any of the code first.