20 Feb 2024
In Redux, data flow follows a unidirectional pattern, which means that data flows in one direction through the application. This pattern helps to manage and maintain the application's state in a predictable and centralized manner. The data flow in Redux typically involves the following steps:
-
Action Dispatch: When an action occurs in the application, such as a user clicking a button or fetching data from an API, an action is dispatched. An action is a plain JavaScript object that describes the type of action that occurred and may contain additional data payload.
-
Action Creators: Action creators are functions responsible for creating and returning action objects. They encapsulate the logic of creating actions and are typically called from within components or other parts of the application.
-
Reducers: Reducers are pure functions that specify how the application's state should change in response to dispatched actions. A reducer takes the current state and an action as input, and returns a new state object based on the action type and payload.
-
Store: The store is a single JavaScript object that holds the application's state tree. It is the central hub of Redux where all the application's state is stored. The store is created by passing the root reducer to the Redux
createStore()function. -
Dispatching Actions to the Store: Actions are dispatched to the store using the
dispatch()method. When an action is dispatched, the store calls the root reducer with the current state and the action. The root reducer then delegates the handling of the action to the appropriate reducer based on the action type. -
State Update: Reducers calculate the new state based on the current state and the action dispatched. They should always return a new state object without mutating the original state. Redux uses shallow equality checks to determine if the state has changed, and if it has, it notifies any subscribed components that the state has been updated.
-
Component Rendering: Components that are interested in particular slices of the state can subscribe to the Redux store using the
connect()function provided by the React Redux library. When the state in the store changes, subscribed components are re-rendered with the updated state, ensuring that the UI reflects the latest data.
Overall, Redux provides a predictable and manageable way to handle application state by enforcing a strict unidirectional data flow and separating concerns between actions, reducers, and the store.
Simple counter example to explain the data flow in Redux:
- Initial State Setup: In Redux, you start by defining an initial state for your application. For our counter example, the initial state might look like this:
const initialState = {
count: 0
};
- Actions: Actions are payloads of information that send data from your application to the Redux store. They are plain JavaScript objects and must have a
typeproperty that indicates the type of action being performed. In our example, we might have actions likeINCREMENTandDECREMENTto increase or decrease the count:
const increment = () => ({
type: 'INCREMENT'
});
const decrement = () => ({
type: 'DECREMENT'
});
- Reducers: Reducers specify how the application's state changes in response to actions sent to the store. A reducer is a pure function that takes the previous state and an action, and returns the next state. Here's how we might define a reducer for our counter example:
const counterReducer = (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;
}
};
- Store: The Redux store brings actions and reducers together. It holds the application state, allows access to state via
getState(), dispatches actions viadispatch(action), and registers listeners viasubscribe(listener). You create a store by passing your root reducer to thecreateStorefunction:
const { createStore } = Redux;
const store = createStore(counterReducer);
-
Data Flow: Now, let's see how data flows in Redux:
- Action Dispatch: When an action is dispatched (e.g.,
increment()), it flows through the Redux store.
store.dispatch(increment());- Reducer Execution: The store passes the action to the reducer along with the current state. The reducer then calculates the new state based on the action type and the current state.
const counterReducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 }; // Other cases... } };-
State Update: Once the reducer calculates the new state, Redux notifies all components subscribed to the store about the state change.
-
Component Update: Components that are connected to the store receive the updated state as props and re-render with the new data.
- Action Dispatch: When an action is dispatched (e.g.,
// In a React component
const CounterComponent = ({ count, increment, decrement }) => (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
const render = () => {
ReactDOM.render(
<CounterComponent
count={store.getState().count}
increment={() => store.dispatch(increment())}
decrement={() => store.dispatch(decrement())}
/>,
document.getElementById('root')
);
};
store.subscribe(render);
render(); // Initial rendering
This is how data flows in Redux:
- Action is dispatched.
- Reducer calculates the new state.
- Store notifies subscribed components of the state change.
- Components receive new state and re-render.
This cycle continues as actions are dispatched and state changes occur within the application.
Diagram illustrating the data flow in Redux with a counter example:
Action
|
v
+---------+
| Reducer |-----------> New State
+---------+ ^
| |
v |
Store <---------------+
|
v
Component
- Action: Actions are dispatched from components.
- Reducer: Reducers take the current state and an action, and return the new state.
- Store: The store holds the state tree of the application and is responsible for dispatching actions, applying reducers, and notifying subscribers.
- Component: Components subscribe to the store and receive updates whenever the state changes, causing re-rendering.