Skip to content

Repository files navigation

Understanding Redux: A Comprehensive Guide

This documentation serves as a practical reference for Redux concepts, designed for future project use and to assist anyone seeking clarity on state management in JavaScript applications.


What is Redux?

Redux is a predictable state container for JavaScript apps, most commonly used with React. Imagine it as a centralized warehouse for all the data (the "state") your application needs. This makes it significantly easier to manage and access that data consistently from any component within your application.

redux official diagram

The Core Principle: Unidirectional Data Flow

At its heart, Redux operates on a simple, one-way data flow. This fundamental principle ensures predictability and makes debugging easier. The flow involves three main parts: the Store, Actions, and Reducers.

1. The Store

The Store is a single, immutable JavaScript object that holds the entire state of your application. It's the "single source of truth" for your application's data. You'll only ever have one Redux store in your application.

2. Actions

When you want to change the state, you can't modify it directly. Instead, you dispatch an Action. An action is a plain JavaScript object that describes what happened. Think of it as sending a formal request or a "memo" to the store, indicating an intent to change the state.

Example Action to add a movie:

{
  "type": "movies/addMovie",
  "payload": "Transformers"
}

3. Reducers

A Reducer is a pure function that receives the current state and an action. Its sole responsibility is to decide how to update the state based on that action and return a brand new state object. It's called a "reducer" because it takes all incoming actions and "reduces" them down to a single new state. Crucially, reducers must never mutate the original state directly; they must always return a new copy.


The Redux Data Flow Cycle:

The entire process follows a clear cycle:

  1. An event happens in your UI (e.g., a button click, an API response).
  2. Your UI dispatches an Action describing the event.
  3. The Store passes the current state and the action to a Reducer.
  4. The Reducer computes and returns a brand new state object.
  5. The Store updates itself with this new state.
  6. Any UI components subscribed to the relevant parts of the state automatically re-render to reflect the changes.

This is better visualized in the official Redux data flow diagram:


The Example Project: Movie Management with Redux Toolkit

This project demonstrates a simple movie management application, originally derived from a tutorial (YouTube Link). Its primary purpose is to illustrate state management across multiple components, where Redux (specifically Redux Toolkit) proves invaluable.

While the original tutorial utilized JavaScript, this implementation has been converted to TypeScript for enhanced type safety and adherence to modern industry standards. Additionally, the styling has been customized.

Here's a detailed, step-by-step breakdown of the data flow when a new movie is added: diagram

The Movie Addition Process: Step-by-Step

1. User Interaction (in MovieInput.jsx)

  • A user types a movie title, like "Transformers," into the <input> field and clicks the "Add Movie" button.
  • The button's onClick={handleAddMovie} event is triggered.
  • Inside handleAddMovie, the dispatch function is called with the addMovie() action creator (from your slice). The current value of the input (newMovie state) is passed as the argument.
    // This function call initiates the Redux cycle
    dispatch(addMovie("Transformers"));

2. The Action is Created and Dispatched

  • The addMovie("Transformers") action creator generates a plain JavaScript object. Redux Toolkit automatically combines the name from your slice ("movies") with the reducer function name ("addMovie") to create the unique action type.
  • The dispatched action object looks like this:
    {
      "type": "movies/addMovie",
      "payload": "Transformers"
    }
  • This action object is then sent to the Redux store via dispatch.

3. The Store and Root Reducer (in store.js)

  • The store receives the action. Its primary role is to pass the current application state and the action to its main "root reducer."
  • Your configureStore setup defines how different parts of your global state are managed. For instance, it specifies that the movies property in your global state is handled by movieReducer.
    reducer: { 
        movies: movieReducer, // <-- The store maps 'movies/...' actions to this reducer
    },
  • The store, through its root reducer, inspects the action type ('movies/addMovie') and intelligently forwards it to the appropriate movieReducer.

4. The Slice Reducer Executes (in movieSlice.js)

  • The movieReducer from your movieSlice takes over. It examines the action.type and executes the corresponding function defined in its reducers object.
  • The addMovie function within movieSlice is executed:
    • state here refers only to the current movies slice of the global state (e.g., { movies: [...] }), not the entire app state.
    • action is the action object from Step 2.
    addMovie: (state, action) => {
        // action.payload is "Transformers"
        const newMovie: Movie = {
            id: state.movies[state.movies.length - 1].id + 1, // this state.movies 
            // is saying (state.movies).movies, state = (state.movies) in this scope
            title: action.payload // The movie title from the action
        }
        // Redux Toolkit's Immer allows "mutating" logic,
        // which safely produces an immutable new state.
        state.movies.push(newMovie); 
    },
    (Note: The id generation has been updated to Date.now() for robust uniqueness, and newMovie is pushed instead of action.payload to ensure the correct object structure.)

5. The State is Updated

  • Redux Toolkit leverages a library called Immer behind the scenes. This powerful tool allows you to write state-updating logic that looks like it's directly "mutating" the state (e.g., state.movies.push(...)). Immer then safely translates these mutations into immutable updates, returning a brand new state object.
  • The movieSlice returns a new state object specifically for the movies slice.
  • The root reducer then assembles this new slice with any other (unchanged) slices to form the complete, new global state object.
  • The store is now updated and holds this new, final application state.

6. The UI Re-Renders

  • Any React components in your application that are subscribed to the Redux store (typically using the useSelector hook to extract data like the movies list) will be notified of the state change.
  • useSelector efficiently compares the new data with the previously selected data. If the data has changed, the component will automatically re-render to display the updated information, ensuring your UI always reflects the latest state.

Getting Started

To explore this application and see Redux in action:

  1. Clone the repository to your local machine.
  2. Navigate to the project directory in your terminal.
  3. Install dependencies: npm install
  4. Start the development server: npm run dev

Feel free to experiment with and extend this project. Its core purpose is to inform and assist in understanding Redux, building upon the foundational concepts from the original tutorial.

About

A small redux example I got from a youtube tutorial, I expanded on it by using typescript and created a diagram for better understanding.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages