In the realm of state management within React applications, Redux stands out as a powerful tool for managing global state. Redux follows a predictable state container design pattern that helps manage application state in a centralized manner. At the core of Redux are two fundamental concepts: actions and reducers.
Actions: Actions are payloads of information that send data from your application to your Redux store. They are plain JavaScript objects with a type field that describes what happened.
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.
In this tutorial, we will delve into defining actions and reducers in Redux, providing practical examples to solidify your understanding.
Actions are payloads of information that describe what happened in your application. They are dispatched from components or other parts of your application to notify the Redux store about changes.
An action is a plain JavaScript object with a type field and optionally additional data fields:
{
type: 'ACTION_TYPE',
payload: { /* optional data */ }
}
### Reducers
Reducers are functions that take the current state and an action as arguments, and return a new state. They must be pure functions, meaning they should not mutate the input state or perform side effects like API calls.
A basic reducer function looks like this:
```jsx
function reducer(state = initialState, action) {
switch (action.type) {
case 'ACTION_TYPE':
// Return new state based on action.payload
return { ...state, /* updated fields */ };
default:
return state;
}
}
## Examples
Let's walk through a practical example to illustrate how actions and reducers work together in Redux.
### Step 1: Define Actions
First, we need to define our actions. For this example, let's create an action that increments a counter:
```jsx
// actions.js
export const increment = () => ({
type: 'INCREMENT'
});
Next, we define the reducer that will handle the INCREMENT action:
// reducer.js
const initialState = {
count: 0
};
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
default:
return state;
}
}
export default counterReducer;
Now, let's combine our reducer and create a Redux store:
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
Finally, we can dispatch actions and subscribe to state changes in our application:
// app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';
import { increment } from './actions';
function App() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
<h1>Counter: {count}</h1>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Actions: We defined an increment action that simply returns an object with a type of 'INCREMENT'.
Reducers: The counterReducer handles the INCREMENT action by returning a new state with the count incremented.
Store: We created a Redux store using createStore, passing in our reducer.
React Integration: We used the Provider component to make the Redux store available to our React components. The useSelector hook is used to access the current state, and the useDispatch hook is used to dispatch actions.
In this tutorial, we learned how to define actions and reducers in Redux. In the next section, we will explore how to connect React components with Redux using hooks like useSelector and useDispatch. This will allow us to build more complex applications with a centralized state management system.
Stay tuned for the next part of our Redux series!