Redux Basics: A Detailed Explanation with Code Examples
Redux is a state management library used widely with JavaScript applications, especially with React. It provides a centralized store for the state of your app, making it easier to manage and debug, especially in large and complex applications. Redux follows a unidirectional data flow and ensures that state changes happen in a predictable manner, making it easier to understand how your app works.
Let's break down the Redux basics step by step, explaining each concept with code examples.
1. What is Redux?
Redux is a predictable state container for JavaScript apps. It helps you manage the state of your app in a centralized way, making it easier to debug and scale.
Core Principles of Redux:
- Single Source of Truth: The whole state of the application is stored in a single object (the store), making it easy to track and manipulate.
- State is Read-Only: The only way to change the state is by dispatching an action.
- Changes are Made with Pure Functions: State is modified by reducers, which are pure functions that specify how the state changes in response to an action.
2. Core Concepts of Redux
Redux relies on the following key components:
1. Action
An action is a plain JavaScript object that describes an event or an action that has occurred in the application. Each action must have a type
property, which describes the action being performed.
Action Example:
// actions.js
export const increment = () => ({
type: 'INCREMENT'
});
export const decrement = () => ({
type: 'DECREMENT'
});
- Here,
increment
anddecrement
are action creators. They return action objects with atype
field. Thetype
property tells Redux how to handle the action in the reducer.
2. Reducer
A reducer is a pure function that takes the current state and an action, then returns a new state. Reducers are the functions that specify how the state should change in response to an action. They should be pure functions, meaning they don’t modify the original state but return a new state object.
Reducer Example:
// reducer.js
const initialState = {
count: 0
};
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;
}
};
export default counterReducer;
- In the example above:
- The initial state is defined with a
count
of0
. - The reducer listens for actions
INCREMENT
andDECREMENT
and updates thecount
state accordingly. - The
return { ...state, count: state.count + 1 }
line creates a new object to maintain immutability and update the state.
- The initial state is defined with a
3. Store
The store holds the entire state of your application. The store is created using the createStore
method from Redux, and it is where the application’s state lives. The store also provides methods to dispatch actions and subscribe to changes in the state.
Store Example:
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
- The store is created by passing the
counterReducer
tocreateStore
. - Now, the state of the application is managed by Redux, and any changes to the state will go through the reducer.
3. Connecting Redux with React
React components need to interact with the Redux store to get the state and dispatch actions. React-Redux, a separate library, is used to connect React with Redux. It provides hooks like useSelector
to access the store’s state and useDispatch
to dispatch actions.
Steps to Connect Redux with React:
-
Wrap your app with the
Provider
component fromreact-redux
to pass the store to all your components. - Use
useSelector
to access the state. - Use
useDispatch
to dispatch actions that modify the state.
4. Setting Up Redux in a React App
Let’s walk through the complete setup to connect Redux with a simple React app.
Step 1: Install Redux and React-Redux
First, you need to install Redux and React-Redux:
npm install redux react-redux
Step 2: Create Actions
In Redux, actions are plain JavaScript objects that describe the change you want to make to the state.
// actions.js
export const increment = () => ({
type: 'INCREMENT'
});
export const decrement = () => ({
type: 'DECREMENT'
});
- The
increment
anddecrement
functions are action creators that return action objects. - The action object has a
type
field that describes the action.
Step 3: Create a Reducer
A reducer is a function that takes the current state and an action, and returns a new state.
// reducer.js
const initialState = {
count: 0
};
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;
}
};
export default counterReducer;
- The reducer listens for the
INCREMENT
andDECREMENT
actions and updates the state accordingly.
Step 4: Create the Store
The store is where the state lives. It is created using the createStore
method from Redux.
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
- We pass the
counterReducer
tocreateStore
to create a store that will manage the state for our app.
Step 5: Connecting Redux with React
Now, let's connect Redux to our React app using the Provider
, useDispatch
, and useSelector
hooks.
// App.js
import React from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import store from './store';
import { increment, decrement } from './actions';
const Counter = () => {
const count = useSelector((state) => state.count); // Get count from the store
const dispatch = useDispatch(); // Get dispatch function to send actions
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
};
const App = () => {
return (
<Provider store={store}> {/* Provide the Redux store to the app */}
<Counter />
</Provider>
);
};
export default App;
-
Provider
: Wraps the entire application, passing the Redux store down to all components. -
useSelector
: Retrieves the current state (count
) from the Redux store. -
useDispatch
: Allows you to dispatch actions (increment
anddecrement
).
5. Redux Toolkit (Optional)
To make Redux easier to use, Redux Toolkit simplifies setup by reducing boilerplate code. It offers utilities like createSlice
and configureStore
to handle common tasks such as creating reducers and configuring the store.
Example Using Redux Toolkit:
import { configureStore, createSlice } from '@reduxjs/toolkit';
// Define a slice of state
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => {
state.count += 1;
},
decrement: (state) => {
state.count -= 1;
}
}
});
// Configure store
const store = configureStore({
reducer: counterSlice.reducer
});
export default store;
- The
createSlice
function automatically generates action creators and reducers for you. -
configureStore
simplifies store setup.
6. Best Practices for Using Redux
- Keep Redux for Global State: Use Redux for managing application-wide state (e.g., user authentication, settings).
-
Use Local State for Simple Components: For smaller state needs (like form inputs), use React's
useState
instead of Redux. - Avoid Direct Mutation: Always return a new object when updating state to ensure immutability.
7. Conclusion
Redux is a powerful tool for managing global state in React applications. By understanding actions, reducers, and the store, you can manage complex state in a predictable way. Using Redux Toolkit can simplify this process further. When used correctly, Redux can make large applications easier to manage, debug, and scale.
With this knowledge, you're now equipped to incorporate Redux into your React applications, ensuring more efficient state management across your app.
Top comments (0)