In this section, we will delve into the core concepts of Redux: Actions and Reducers. These are fundamental building blocks for managing state in a Redux application.

Key Concepts

Actions

  • Definition: Actions are plain JavaScript objects that represent an intention to change the state.
  • Structure: An action must have a type property, which is a string that indicates the type of action being performed. It can also have an optional payload property that contains additional information needed to perform the action.

Reducers

  • Definition: Reducers are pure functions that take the current state and an action as arguments and return a new state.
  • Pure Functions: A pure function is a function that, given the same input, will always return the same output and does not have any side effects.

Actions in Detail

Creating Actions

Actions are created using action creators, which are functions that return an action object.

// Action Types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// Action Creators
const increment = () => {
  return {
    type: INCREMENT
  };
};

const decrement = () => {
  return {
    type: DECREMENT
  };
};

Dispatching Actions

Actions are dispatched to the store using the dispatch method.

store.dispatch(increment());
store.dispatch(decrement());

Reducers in Detail

Creating Reducers

Reducers handle the dispatched actions and update the state accordingly.

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;
  }
};

Combining Reducers

In larger applications, you might have multiple reducers managing different parts of the state. These can be combined using combineReducers.

import { combineReducers } from 'redux';

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

Practical Example

Setting Up Redux

  1. Install Redux and React-Redux:

    npm install redux react-redux
    
  2. Create the Redux Store:

    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import rootReducer from './reducers'; // Assuming you have a rootReducer
    
    const store = createStore(rootReducer);
    
    const App = () => (
      <Provider store={store}>
        <Counter />
      </Provider>
    );
    
  3. Connecting Components to Redux:

    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import { increment, decrement } from './actions'; // Assuming you have these action creators
    
    const Counter = () => {
      const count = useSelector(state => state.counter.count);
      const dispatch = useDispatch();
    
      return (
        <div>
          <h1>{count}</h1>
          <button onClick={() => dispatch(increment())}>Increment</button>
          <button onClick={() => dispatch(decrement())}>Decrement</button>
        </div>
      );
    };
    
    export default Counter;
    

Exercises

Exercise 1: Create a New Action and Reducer

  1. Task: Create a new action type RESET that resets the count to zero.
  2. Steps:
    • Define the RESET action type.
    • Create an action creator for RESET.
    • Update the reducer to handle the RESET action.

Solution:

// Action Types
const RESET = 'RESET';

// Action Creators
const reset = () => {
  return {
    type: RESET
  };
};

// Reducer
const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT:
      return {
        ...state,
        count: state.count + 1
      };
    case DECREMENT:
      return {
        ...state,
        count: state.count - 1
      };
    case RESET:
      return {
        ...state,
        count: 0
      };
    default:
      return state;
  }
};

Exercise 2: Combine Multiple Reducers

  1. Task: Create a new reducer to manage a list of items and combine it with the counter reducer.
  2. Steps:
    • Create a new reducer for managing items.
    • Combine the new reducer with the counter reducer using combineReducers.

Solution:

// Items Reducer
const itemsReducer = (state = [], action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      return [...state, action.payload];
    case 'REMOVE_ITEM':
      return state.filter(item => item !== action.payload);
    default:
      return state;
  }
};

// Combine Reducers
const rootReducer = combineReducers({
  counter: counterReducer,
  items: itemsReducer
});

Common Mistakes and Tips

  • Mutating State: Ensure that you do not mutate the state directly in reducers. Always return a new state object.
  • Action Types: Use constants for action types to avoid typos and make the code more maintainable.
  • Pure Functions: Reducers should be pure functions. Avoid side effects like API calls or modifying variables outside the function.

Conclusion

In this section, we covered the basics of Redux actions and reducers. We learned how to create and dispatch actions, how to write reducers to handle these actions, and how to combine multiple reducers. These concepts are crucial for managing state in a Redux application. In the next section, we will explore how to connect Redux to React components.

React Course

Module 1: Introduction to React

Module 2: React Components

Module 3: Working with Events

Module 4: Advanced Component Concepts

Module 5: React Hooks

Module 6: Routing in React

Module 7: State Management

Module 8: Performance Optimization

Module 9: Testing in React

Module 10: Advanced Topics

Module 11: Project: Building a Complete Application

© Copyright 2024. All rights reserved