Topic 11: Redux
📖 5 min read · 🎯 Advanced · 🧭 Prerequisites: rest-apis, actions-and-reducers
Why this matters
Up until now, every component in your React app has been managing its own state — and for a while, that works fine. But picture this: you build a shopping cart feature, and suddenly your navbar needs to know the cart count, your checkout page needs the items, and your product list needs to know what's already been added. Passing that data around through props becomes a nightmare fast. Redux solves this by giving your entire app one single place to store state — one source of truth. Every component reads from it, every action updates it, and nothing gets out of sync.
What You'll Learn
- Understand the five core concepts of Redux: Store, Action, Reducer, Dispatch, and Provider
- Install and configure
reduxandreact-reduxin a React Native project - Build a Redux store with action creators and a reducer from scratch
- Connect a React Native component to the store using
useSelectoranduseDispatch - Run the full Redux loop end-to-end in an Expo application
The Analogy
Think of Redux as a city's central post office. Every citizen (component) who wants to change something — update an address, file a complaint, register a business — submits a standardized form (an Action) to the post office counter. The clerk (the Reducer) reads the form, consults the master ledger (current State), writes the updated entry, and hands back a fresh ledger. The previous ledger is never crossed out; a brand-new copy is produced every time. Any citizen who needs to know the current state of the ledger can walk up to the window and read it (useSelector) — they all see the same, authoritative copy. Nothing changes behind closed doors; every update is explicit, traceable, and predictable.
Chapter 1: Introduction to Redux
Redux is a predictable state container for JavaScript applications. It gives you a single, centralized place — the store — to hold all application state, so any component in the tree can read from or write to it without prop-drilling or scattered local state.
Key Concepts of Redux
- Store — The centralized state of the entire application; there is exactly one store per app.
- Action — A plain JavaScript object that describes a change. It must have a
typeproperty; it may carry additional payload data. - Reducer — A pure function that receives
(currentState, action)and returns a new state object. It never mutates the existing state. - Dispatch — The store method you call to send an action into the reducer pipeline:
dispatch({ type: 'INCREMENT' }). - Provider — A React component from
react-reduxthat wraps your app and makes the Redux store accessible to every component in the tree via React context.
flowchart LR
UI["UI Component"] -->|"dispatch(action)"| Store
Store -->|"calls"| Reducer
Reducer -->|"returns new state"| Store
Store -->|"useSelector()"| UI
Chapter 2: Setting Up Redux in a React Native Project
Step 1: Install Redux and React-Redux
npm install redux react-redux
Step 2: Create the Redux Store
Create a new file store.js at the root of your project. This file defines your initial state, action type constants, action creators, the reducer, and the store itself.
store.js
import { createStore } from 'redux';
// Initial state
const initialState = {
count: 0,
};
// Action types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// Action creators
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
// Reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
};
// Create store
const store = createStore(reducer);
export default store;
Key points in store.js:
initialStatedefines what the state looks like before any action fires.- Action type constants (
INCREMENT,DECREMENT) are plain strings — using constants prevents hard-to-spot typos. - Action creators (
increment,decrement) are factory functions that return the action objects. Exporting them lets components dispatch without knowing the action shape. - The reducer uses the spread operator (
...state) to return a new object on every update — never mutating the existing state. createStore(reducer)wires the reducer to the store.
Step 3: Integrate the Store with the App
Wrap your root component with <Provider store={store}> so every descendant can access the store.
App.js
import React from 'react';
import { Provider } from 'react-redux';
import { StyleSheet, View } from 'react-native';
import store from './store';
import Counter from './Counter';
export default function App() {
return (
<Provider store={store}>
<View style={styles.container}>
<Counter />
</View>
</Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
Provider must be the outermost wrapper — place it at the very top of your component tree so no connected child is left outside its reach.
Chapter 3: Creating a Redux-Connected Component
With the store wired up, any component can subscribe to state and dispatch actions using the useSelector and useDispatch hooks from react-redux.
Step 1: Create the Counter Component
Create Counter.js in the project root.
Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { View, Text, Button, StyleSheet } from 'react-native';
import { increment, decrement } from './store';
const Counter = () => {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<View style={styles.container}>
<Text style={styles.count}>{count}</Text>
<Button title="Increment" onPress={() => dispatch(increment())} />
<Button title="Decrement" onPress={() => dispatch(decrement())} />
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
},
count: {
fontSize: 48,
marginBottom: 20,
},
});
export default Counter;
How it works:
useSelector((state) => state.count)reads thecountvalue from the store. The component re-renders automatically whenever that slice of state changes.useDispatch()returns the store'sdispatchfunction. Callingdispatch(increment())sends the{ type: 'INCREMENT' }action through the reducer.- The component itself holds zero local state — all state lives in the store.
Chapter 4: Testing the Redux Integration
Once the files are in place, start the development server:
expo start
Open Expo Go on a physical device or simulator. You should see the Counter component rendered in the center of the screen displaying 0. Tapping Increment increases the count; tapping Decrement decreases it. Every tap dispatches an action, the reducer returns a new state object, useSelector picks up the change, and the Text re-renders — the full Redux loop in action.
File structure at this point:
my-app/
├── App.js # Provider wraps the tree
├── store.js # store, reducer, action creators
└── Counter.js # connected component
🧪 Try It Yourself
Task: Extend the counter app with a Reset feature.
- In
store.js, add a new action type constantRESET. - Add an action creator
export const reset = () => ({ type: 'RESET' }). - Add a
case RESET:branch in the reducer that returns{ ...state, count: 0 }. - In
Counter.js, importresetand add a third button:<Button title="Reset" onPress={() => dispatch(reset())} />.
Success criterion: After incrementing or decrementing several times, tapping Reset should immediately return the displayed count to 0 without a reload.
Starter snippet (reducer addition):
case RESET:
return { ...state, count: 0 };
🔍 Checkpoint Quiz
Q1. What is the role of the Redux Reducer?
A) It sends actions to the store
B) It wraps the component tree to provide store access
C) It receives the current state and an action, then returns a new state
D) It holds a reference to every dispatched action object
Q2. Given this reducer snippet, what does state.count equal after dispatching { type: 'DECREMENT' } twice on an initial count of 5?
case DECREMENT:
return { ...state, count: state.count - 1 };
A) 5
B) 4
C) 3
D) -2
Q3. What would happen if you removed the <Provider store={store}> wrapper from App.js but kept the useSelector call inside Counter?
A) The component renders with an empty state object
B) Redux silently falls back to local component state
C) React throws an error because useSelector cannot find a store in context
D) The count displays as undefined but the app continues to work
Q4. You have a Redux store for a shopping cart. A user taps "Add to Cart" on a product screen. Which part of the Redux flow fires first?
A) The reducer computes new state
B) useSelector re-runs its selector function
C) dispatch is called with an action object
D) The Provider re-renders all children
A1. C — The reducer is a pure function that takes (state, action) and returns a brand-new state object; it never mutates state in place.
A2. C — Starting at 5, one DECREMENT yields 4, a second yields 3.
A3. C — useSelector (and useDispatch) rely on the Redux context injected by Provider. Without it, they throw: "could not find react-redux context value".
A4. C — The user interaction calls dispatch(addToCart(product)) first. Redux then routes the action to the reducer, computes new state, updates the store, and finally triggers re-renders in subscribed components via useSelector.
🪞 Recap
- Redux provides a single, centralized store so all components share one authoritative state.
- The flow is always unidirectional: UI →
dispatch(action)→ Reducer → new state → UI re-render. createStore(reducer)instore.jswires your reducer to the store;<Provider store={store}>makes it available to the component tree.useSelectorreads a slice of store state;useDispatchgives you the function to send actions.- Reducers must be pure — they return a new state object and never mutate the existing one.
📚 Further Reading
- Redux Official Docs — the source of truth on store setup, middleware, and Redux Toolkit
- React-Redux Hooks API — full reference for
useSelector,useDispatch, anduseStore - Redux Toolkit (RTK) — the modern, opinionated Redux setup that eliminates boilerplate
- ⬅️ Previous: Actions and Reducers
- ➡️ Next: Firebase