Core Concepts: Actions, Reducers, and the Store in React JS
Redux is a powerful state management tool often used with React to manage the state of your application in a predictable way. At its core, Redux revolves around three primary concepts: Actions, Reducers, and the Store. These concepts work together to ensure that your application state is consistent and manageable.
1. Actions in Redux
Actions are the payloads of information that send data from your application to the Redux store. They are JavaScript objects that describe an event or change that has occurred in your application. Every action must have a type property, which is a string constant representing the type of the action.
Actions are dispatched by components or other parts of your app, and they are processed by the reducers to update the state of the application.
Example 1: Defining an Action
const increment = () => ({
type: 'INCREMENT'
});
const decrement = () => ({
type: 'DECREMENT'
});
In the example above, we define two actions: increment and decrement. Both actions are simple objects that contain a type property. The type helps identify the action that is being dispatched.
2. Reducers in Redux
Reducers are pure functions that specify how the application's state changes in response to an action. A reducer takes the current state and the action being dispatched, and it returns a new state. Reducers never modify the current state directly; instead, they create a new copy of the state with the necessary changes.
Reducers are the heart of Redux, as they determine how the state should change based on the dispatched actions.
Example 2: Creating a Reducer
const initialState = {
count: 0
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
In the example above, we define a counterReducer function. This reducer listens for two action types: INCREMENT and DECREMENT. Based on the action type, the reducer updates the state by incrementing or decrementing the count value.
3. The Store in Redux
The store is a centralized place where the entire state of the application is stored. It is created using the createStore function from Redux, and it holds the application’s state. The store allows components to access the state and dispatch actions to modify it.
The store also provides methods to subscribe to state changes, so components can re-render when the state changes. The store is the only place where the state of your app resides, ensuring that the state is predictable and consistent.
Example 3: Creating the Store
import { createStore } from 'redux';
// Define the reducer
const initialState = { count: 0 };
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
// Create the Redux store
const store = createStore(counterReducer);
// Log the initial state
console.log(store.getState());
In this example, we use the createStore function to create a Redux store that is backed by the counterReducer. After creating the store, we can access the initial state by calling store.getState().
4. Dispatching Actions
Once the store is created, we can dispatch actions to modify the state. This is done using the dispatch method provided by the store. Actions can be dispatched by components, event handlers, or other parts of the application.
Example 4: Dispatching Actions
// Dispatch an action to increment the count
store.dispatch({ type: 'INCREMENT' });
// Dispatch an action to decrement the count
store.dispatch({ type: 'DECREMENT' });
// Log the updated state
console.log(store.getState());
In the example above, we dispatch INCREMENT and DECREMENT actions to the store. The store then updates the state based on the logic defined in the reducer, and we can log the updated state using store.getState().
5. Putting It All Together
Now that we’ve covered actions, reducers, and the store, let’s combine them to see how they work together in a simple React and Redux application.
Example 5: Full Redux Setup
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider, useDispatch, useSelector } from 'react-redux';
// Define the initial state and reducer
const initialState = { count: 0 };
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
// Create the Redux store
const store = createStore(counterReducer);
// Counter Component
const Counter = () => {
const count = useSelector((state) => state.count); // Get the current count from the store
const dispatch = useDispatch(); // Get the dispatch function
return (
Count: {count}
);
};
// App Component
const App = () => {
return (
);
};
// Render the app
ReactDOM.render( , document.getElementById('root'));
In this example, we set up a complete Redux flow. The Counter component uses the useSelector hook to access the current count value from the Redux store and the useDispatch hook to dispatch actions to modify the state. The store is wrapped around the entire application using the Provider component, ensuring that the Redux store is accessible throughout the app.
6. Conclusion
Understanding the core concepts of Redux — actions, reducers, and the store — is crucial for managing state in large-scale React applications. These concepts work together to provide a predictable and centralized way of managing application state. Actions describe changes, reducers apply those changes, and the store holds the entire application state. By mastering these concepts, you can take full advantage of Redux to build scalable and maintainable React applications.