Skip to content

Redux - Data Flow

Redux enforces a strict unidirectional data flow. This means that data in your application always flows in a single direction, making the logic predictable and easier to understand. It prevents complex chains of updates and potential infinite loops that can occur with two-way data binding.

The lifecycle of data flow in a typical Redux application (especially one using a UI library like React) follows these steps:

Imagine a user interacts with your application, like clicking a button.

  1. 1. Action Dispatch: An event occurs in the UI (e.g., button click). The event handler calls dispatch(action). The action is a plain object describing what happened (e.g., { type: 'todos/add', payload: 'Learn Redux' }).
  2. 2. Middleware (Optional): The dispatched action first goes through any configured middleware. Middleware can intercept the action, perform side effects (like API calls), dispatch other actions, or modify the action before passing it on. For asynchronous operations, middleware like redux-thunk allows dispatching functions that handle the async logic and dispatch further actions upon completion.
  3. 3. Reducer Processing: The action (potentially modified or delayed by middleware) reaches the root reducer function. The store calls the root reducer with the current state and the action.
  4. 4. State Update: The root reducer (often composed of smaller slice reducers) determines how the state should change based on the action type. It calculates the next state immutably (meaning it creates new objects/arrays for any changed parts of the state) and returns it. If no reducer recognizes the action type, the current state is returned unchanged.
  5. 5. Store Update: The Redux store saves the new state returned by the root reducer.
  6. 6. Listeners Notified: The store notifies all registered listeners (subscribed components) that the state has potentially changed. It calls the listener functions provided to store.subscribe().
  7. 7. UI Update: Components connected to the Redux store (e.g., using react-redux’s useSelector hook or connect) re-run their selector functions with the new state. If the selected data has changed, the component re-renders to reflect the updated state.

This cycle ensures that:

  • State changes are initiated only through dispatched actions.
  • State transformations are handled centrally and purely by reducers.
  • The UI always reflects the current state in the store.

This predictable flow is one of Redux’s main advantages, especially in large applications where tracking state changes can become challenging.

You can visualize this flow as:

UI Event -> dispatch(action) -> [Middleware (optional)] -> Reducer(state, action) -> New State -> Store -> Notify Listeners -> UI Re-renders with New State