What is Redux?
Redux allows you to manage and update your application state, using events called actions. And it works by providing a single centralized place to contain the global state in your application, and specific patterns to follow when updating that state to make the code predictable
Terminology
State
Refers to the data that represents the current state of your application. It represents the values and information that your application needs to render and operate correctly.
In the beginning, React will render the UI based on the values of the initial state of the app. When something happens (such as a user clicking a button), the state is updated based on what occurred and the UI re-renders based on the new state.
Actions
They are simple plain JavaScript objects that describe an intention to change the state of your application.
The action object must have a type
field and should be a string that specifies the type of action being performed.
By convention, we put additional information or the related data of the action in a field calledΒ payload
This is an example of an action object:
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
Action creator
Is a function that creates and returns an action object in Redux. It is a way to encapsulate the logic of creating an action, making it easier to trigger actions from different parts of your application.
Here's an example of an action creator function:
function incrementCounter(amount) {
return {
type: 'INCREMENT_COUNTER',
payload: amount
};
}
Reducers
Is a function that specifies how the application's state changes in response to actions dispatched to the Redux store. It is responsible for transforming the current state into a new state based on the action received.
The reducer takes two parameters: the current state and an action object. The state parameter represents the current state of the application, while the action parameter contains information about the action being dispatched, such as the type and payload.
Here's a basic example of a reducer function:
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
Store
Is a central object that holds the complete state tree of your application. It is responsible for managing the application state and providing an interface to interact with the state.
The Redux store has the following responsibilities:
- Holds the state: The store maintains the current state of your application. It represents a single source of truth, meaning that all data needed by the application is stored in the store.
- Allows state access: Components can access the state stored in the store by subscribing to it. This allows components to read the state and use it to render the UI or make decisions based on the application's current state.
- Allows state update: Components can dispatch actions to the store, indicating the desired changes to the state. The store receives the actions, processes them through the reducers, and updates the state accordingly. This triggers the re-rendering of subscribed components to reflect the updated state.
- Provides additional methods: The Redux store provides additional methods, such as
getState()
, which returns the current state, anddispatch(action)
, which dispatches an action to trigger a state update. It also supports middleware and enhancers for extending store functionality.
We can create a store by passing a reducer
function.
Here's an example of creating a Redux store using the createStore
function from Redux:
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
Dispatch Action
Is the process of sending an action object to the Redux store using the dispatch(action)
function. Dispatching an action is a way to indicate an intention to change the application state.
Here's an example of a dispatch action:
const action = {
type: 'INCREMENT',
payload: 1
};
store.dispatch(action);
Selectors
These are functions used to extract specific data from the Store.
By using selectors, you can create a clean and efficient way to access and transform data from the Redux store, promoting a more maintainable and scalable application architecture.
Here's an example of a selector function:
const selectCounter = state => state.counter;
Redux Application Data Flow
Redux provides a very clear and concise way of managing the app state;
-
Initial setup:
- Create a Redux store using a root reducer, which combines all individual reducers into one.
- The store calls the root reducer to get the initial state of the application.
- UI components access the initial state from the store and render based on that data.
- Components can also subscribe to the store to receive updates when the state changes.
-
Updates:
- An event or user interaction occurs in the application, such as a button click.
- App code dispatches an action to the Redux store, indicating the type of action being performed.
- The store runs the root reducer with the previous state and the dispatched action.
- The reducer function determines how the state should change based on the action type and payload.
- The reducer returns a new state object, which becomes the updated state of the application.
-
Notification and Re-rendering:
- The store notifies all subscribed components that the state has been updated.
- Components that have subscribed and depend on the changed parts of the state compare the new state with the previous state.
- If the relevant parts of the state have changed, those components initiate a re-render to update their UI based on the new data.
- The components access the updated state through selectors, extracting the specific data they need for rendering.
-
Component Rendering:
- Components receive the updated state and re-render their UI using the new data.
- The UI reflects the changes in the application state triggered by the dispatched action.
- Components that do not depend on the changed parts of the state are not re-rendered, optimizing performance.
This cycle repeats whenever actions are dispatched to the store. The Redux Application Data Flow ensures a predictable and controlled process for managing the state and updating the UI in response to actions. It separates the concerns of state management and UI rendering, making it easier to understand, maintain, and scale Redux applications.
Redux Toolkit App Structure
This is the recommended structure for organizing and architecting a Redux application using Redux Toolkit. This structure helps developers maintain a clear and scalable codebase by defining a consistent and predictable organization of files and folders.
Here's an overview of the Redux Toolkit App Structure:
src
βββ index.js
βββ app
β βββ store.js
β βββ rootReducer.js
βββ features
β βββ feature1
β β βββ featureSlice.js
β β βββ featureSelectors.js
β β βββ featureThunks.js
β βββ feature2
β β βββ featureSlice.js
β β βββ featureSelectors.js
β β βββ featureThunks.js
β βββ ...
βββ components
β βββ Component1.js
β βββ Component2.js
β βββ ...
βββ pages
βββ Page1.js
βββ Page2.js
βββ ...
-
src folder: This is the root folder of your application's source code.
-
index.js: The entry point of your application where you typically set up the Redux store and render the root component.
-
app folder: This folder contains the core configuration and setup for Redux Toolkit.
- store.js: This file is responsible for creating and configuring the Redux store using the
configureStore
function from Redux Toolkit. It combines multiple reducers, applies middleware, and sets up other store enhancements. - rootReducer.js: The root reducer combines all individual reducers into a single reducer using the
combineReducers
function from Redux Toolkit. This file serves as the entry point for your application's state management.
- store.js: This file is responsible for creating and configuring the Redux store using the
-
features folder: This folder houses the features or slices of your application's state. Each feature has its own folder with the following files:
- featureSlice.js: A feature slice represents a portion of your application's state along with its associated actions and reducers. It defines the initial state, action creators, and reducer logic for that specific feature.
- featureSelectors.js: This file contains selectors that extract specific data from the feature's slice of the state. Selectors provide a clean and efficient way to access and transform data from the Redux store.
- featureThunks.js: If you're using Redux Thunk middleware, this file includes any thunks (asynchronous actions) related to the feature. Thunks allow you to perform side effects and dispatch multiple actions asynchronously.
-
components folder: This folder holds your reusable and presentational components.
-
pages folder: This folder contains the components representing the different pages or views of your application.
By structuring your Redux Toolkit app in this way, you separate concerns, keep related code together, and improve maintainability. It provides a clear organization for features, actions, reducers, selectors, and other components of your application, making it easier to understand and scale as your application grows.
Conclusion
In this post, we have covered the fundamental concepts of React Redux Toolkit, providing beginners with a comprehensive understanding of how Redux works and how to effectively manage application state. Also covered a typical Redux Toolkit App structure. Redux is a powerful tool that allows developers to centralize and control the state of their React applications, enabling predictable and scalable code.