Reducers & actions

Introduction

In modern front-end development, state management is a crucial aspect of building scalable and maintainable applications. Redux has emerged as a popular library for managing application state, offering a predictable state container for JavaScript apps. Central to Redux are the concepts of actions and reducers. This blog will delve into the details of these two key elements, providing a comprehensive understanding of how they work together to manage state in Redux applications.

What are Actions?

Definition

Actions are plain JavaScript objects that represent an intention to change the state. Every action must have a type property, which is a string that indicates the type of action being performed. Beyond type, actions can include any additional data needed to describe the event.

Structure

const action = {
    type: 'ADD_TODO',
    payload: {
        id: 1,
        text: 'Learn Redux'
    }
};

Action Creators

Action creators are functions that create and return an action. They help encapsulate the creation of action objects, making the code more manageable and easier to understand.

const addTodo = (id, text) => ({
    type: 'ADD_TODO',
    payload: {
        id,
        text
    }
});

Dispatching Actions

To change the state in Redux, actions need to be dispatched. Dispatching an action sends it to the Redux store, where the reducers will handle it and produce the new state.

store.dispatch(addTodo(1, 'Learn Redux'));

What are Reducers?

Definition

Reducers are pure functions that take the current state and an action as arguments, and return a new state. They specify how the state changes in response to an action. The term "pure" means that the function returns the same output for the same input and does not produce side effects.

Structure

const initialState = {
    todos: []
};

const todoReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return {
                ...state,
                todos: [
                    ...state.todos,
                    {
                        id: action.payload.id,
                        text: action.payload.text
                    }
                ]
            };
        default:
            return state;
    }
};

Combining Reducers

In larger applications, there may be multiple reducers, each managing different parts of the state. Redux provides a combineReducers function to combine these reducers into a single reducer function.

import { combineReducers } from 'redux';

const rootReducer = combineReducers({
    todos: todoReducer,
    // other reducers can be added here
});

Putting It All Together

Store Creation

The Redux store holds the application state and provides a few methods to interact with it, such as dispatch and getState. The store is created using the createStore function, which takes the root reducer as an argument.

import { createStore } from 'redux';

const store = createStore(rootReducer);

Workflow

  1. Action Creation: An action is created using an action creator.

  2. Action Dispatching: The action is dispatched to the Redux store.

  3. Reducer Handling: The store passes the action to the appropriate reducer.

  4. State Update: The reducer processes the action and returns the new state.

  5. Store Update: The Redux store updates the state and notifies any subscribed components.

Example

Let's walk through a complete example.

Step 1: Define Actions

const addTodo = (id, text) => ({
    type: 'ADD_TODO',
    payload: { id, text }
});

Step 2: Define Reducer

const initialState = {
    todos: []
};

const todoReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return {
                ...state,
                todos: [...state.todos, { id: action.payload.id, text: action.payload.text }]
            };
        default:
            return state;
    }
};

Step 3: Combine Reducers

import { combineReducers } from 'redux';

const rootReducer = combineReducers({
    todos: todoReducer
});

Step 4: Create Store

import { createStore } from 'redux';

const store = createStore(rootReducer);

Step 5: Dispatch Actions

store.dispatch(addTodo1, 'Learn Redux'));
console.log(store.getState());
// Output: { todos: [{ id: 1, text: 'Learn Redux' }] }

Conclusion

Understanding reducers and actions is fundamental to mastering Redux. Actions encapsulate the intentions to change state, while reducers specify how the state changes in response to those actions. Together, they provide a predictable and maintainable way to manage state in JavaScript applications. By following the structured approach of creating actions, defining reducers, combining them, and dispatching actions, you can build robust and scalable applications with Redux.

By mastering these concepts, you'll be well-equipped to handle state management in your Redux applications, ensuring your code is clean, predictable, and easy to debug.

Last updated