Skip to content

iamtango/redux-course

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Redux Course

  • Why we need Redux?
    • It is state management library which is help to effeciently manage the stateof the application and Avoid Prop drilling.
  • What is different between ContextAPI and Redux
    • Behind the scene Redux using ContextAPI but in the ContextAPI it requires creating multiple context providers for independent state pieces, potentially leading to nested providers when managing multiple contexts.
    • Redux simplifies state management by providing a single provider <Provider> for the entire application, ensuring predictable state updates and facilitating global access to state with minimal setup.
// Example of Context API with multiple context provider and pass the children inside the context Provider
<Context1.Provider value={value1}>
  <Context2.Provider value={value2}>
    <Context3.Provider value={value3}>...</Context3.Provider>
  </Context2.Provider>
</Context1.Provider>

// Example of Redux with multiple Redux provider and pass the children inside the Provider
<Provider store={value}>
<Provider>

Redux Components

What is a Store?

  • The store is a container that holds the application state, and the only way the state can change is through actions dispatched to the store. Redux allows individual components to connect to the store.
  • It is highly recommended to keep only one store in any Redux application

What is a Reducer?

  • A reducer is a pure function, which returns the state of the application based on the action dispatched by the store.

What is an Action?

  • It is a payload of information that transmits data from an application to a store. They are the sole source of information for the store. One can send them to the store using store. dispatch() .

  • Actions are plain JavaScript objects that must have:

    • A type property to indicate the type of action to be carried out
    • A payload object that contains the information that should be used to change the state
  • Actions are created via an action creator, which is a function that returns an action. Actions are executed using the dispatch() method, which sends the action to the store

What is a Middleware?

  • Redux allows developers to intercept all actions dispatched from components before they are passed to the reducer function. This interception is done via middleware, which are functions that call the next method received in an argument after processing the current action.

createStore

  • in createStore we need to pass the reducer function
  • it has 3 method getState, dispatch & subscribe

getState()

  • Returns the current state tree of your application. It is equal to the last value returned by the store's reducer.

dispatch()

  • It is use to call the reducer funtion.
  • Dispatches an action. This is the only way to trigger a state change by passing th action.
  • The store's reducer function will be called with the current getState() result and the given action synchronously.

subscribe()

  • Adds a change listener. It will be called any time an action is dispatched, and some part of the state tree may potentially have changed. You may then call getState() to read the current state tree inside the callback.

combineReducers

  • It is use to combine multiple reducers
  • The combineReducers helper function turns an object whose values are different slice reducer functions into a single combined reducer function.

Action Types

  • Action is just an object which goal is to describe your actual intention with a type field and maybe by adding some additional data.

Action creators

  • Action creators are functions that create and return action objects.
  • Action creators help organize and centralize the logic for creating these action objects

Ducks Pattern

  • In the same file we write actions, action Creators , reducer.

useSelector

  • It is hook which help us to subscribe to the redux store it take one call back function and comes from react-redux

  • useSelector() will also subscribe to the Redux store, and run your selector whenever an action is dispatched

  • When an action is dispatched, useSelector() will do a reference comparison of the previous selector result value and the current result value.

  • If they are different, the component will be forced to re-render. If they are the same, the component will not re-render.

  • useSelector() uses strict === reference equality checks by default, not shallow equality

    const productsList = useSelector((state) => state.products);

useDispatch

  • This hook returns a reference to the dispatch function from the Redux store. You may use it to dispatch actions as needed and comes from react-redux.

    const dispatch = useDispatch();
    dispatch(action());

produce method

createSlice

  • it return actions and reducer
  • A function that accepts an initial state, an object of reducer functions, and a "slice name", and automatically generates action creators and action types that correspond to the reducers and state.

initialState

  • The initial state value for this slice of state.

name

  • A string name for this slice of state. Generated action type constants will use this as a prefix.

reducers

  • An object containing Redux "case reducer" functions (functions intended to handle a specific action type, equivalent to a single case statement in a switch).
import { createSlice } from "@reduxjs/toolkit";

const counterSlice = createSlice({
  name: "counter",
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
  },
});
export const { ncrement } = counterSlice.actions;
export default counterSlice.reducer;

configureStore

  • Combining the slice reducers into the root reducer
  • Creating the middleware enhancer, usually with the thunk middleware or other side effects middleware, as well as middleware that might be used for development checks
  • Adding the Redux DevTools enhancer, and composing the enhancers together
  • Calling createStore

middleware

  • It is the curring funtion
    • it has 3 params store, next and action
      • store has 2 params getState and dispatch
  • By default middleware stop the execution but we can avoid using next(action)
  • we can create the middleware using the middleware key in configureStore object
  • we can Provide multiple middlewares by passing the middlewares in the array

Fetching the data we have 4 methods to fetch the data

Custom API Middleware

Thunk

Redux Toolkit Query

Redux Saga


We cannot directly update the whole store by assigning the action.payload to whole application intead we can return the action.payload to update the whole store.
if u want to update the whole state in redux then instead of assigning it state= action.payload ❌ just return it.

return action.payload βœ…

  • Incorrect Approach: state = action.payload ❌

    • This approach mutates the state variable directly. In Redux, you should never mutate the state directly. Instead, you should return a new state object.
  • Correct Approach: return action.payload βœ…

    • This approach returns a new state object which is a clean and straightforward way to replace the entire state. This ensures that the state remains immutable and adheres to the principles of Redux.

What is Selector

  • A callback function which is use to pass inside the useSelector hook is calle selector
  • A good convention is to write the selector inside the where slice has been define
// slice file
export const getAllProducts = (state) => state.products.list;

// file where useselector hook is used
const productsList = useSelector(getAllProducts);
  • createSelector is a function which is use to memoize the selector by passing the selector as first argument and in the second argument we can pass the callback function
createSelector(getCartItems, (cartItems) => cartItems);
  • It is normal function which has
export const fetchData = (payload) => ({ type: "api/makeCall", payload });
  • Redux Thunk is a middleware that allows dispatching functions and executing delayed tasks.
  • by default the thunk middleware comes with RTK an If we donot write any middlware then the thunk middleware will be activated
  • Thunk is something a piece of code that does some delayed work
export const func =
  ({ dispatch, getState }) =>
  (next) =>
  (action) => {
    if (typeof action === "function") {
      action(dispatch, getState);
    } else {
      next(action);
    }
  };
  • If we want to use custom middleware along with the thunk middleware then we ca use by using following syntax
      middleware: (getDefaultMiddleware) => [...getDefaultMiddleware(), logger],

Redux Thunk middleware allows dispatching of functions in the Redux store for data fetching and handling.

  • We need to pass the action type as first argument and callback function as second argument
  • It will automatically create action creators and they are fulfilled, pending and rejected are the three action creators
export const fetchCartItemsData = createAsyncThunk(
  "cart/fetchCartItems",
  async () => {
    try {
      const response = await fetch(`https://fakestoreapi.com/carts/5`);
      return response.json();
    } catch (err) {
      throw err;
    }
  }
);

extraReducers

  • extraReducers allows createSlice to respond and update its own state in response to other action types besides the types it has generated.

  • However, unlike the reducers field, each individual case reducer inside of extraReducers will not generate a new action type or action creator.

  • If two fields from reducers and extraReducers happen to end up with the same action type string, the function from reducers will be used to handle that action type.

  • the extraReducers field uses a "builder callback" notation to define handlers for specific action types, matching against a range of actions, or handling a default case.

  extraReducers: (builder) => {
    builder
      .addCase(fetchCartItemsData.pending, (state) => {
        state.loading = true
      })
      .addCase(fetchCartItemsData.fulfilled, (state, action) => {
        state.loading = false
        state.list = action.payload.products
      })
      .addCase(fetchCartItemsData.rejected, (state, action) => {
        state.loading = false
        state.error = action.payload || 'Something went wrong!'
      })
  },
  • RTK Query is a powerful data fetching and caching tool.
  • It is designed to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.
  • RTK Query is an optional addon included in the Redux Toolkit package, and its functionality is built on top of the other APIs in Redux Toolkit.
  • Optimistic update is a technique used in web development to enhance the user experience. Sometimes, in order to make our application feel faster to users, we can assume that certain operations will succeed and update the UI immediately, without waiting for confirmation from the server.
  • createApi is the core of RTK Query's functionality.
  • It allows you to define a set of "endpoints" that describe how to retrieve data from backend APIs and other async sources, including the configuration of how to fetch and transform that data.
  • It generates an "API slice" structure that contains Redux logic (and optionally React hooks) that encapsulate the data fetching and caching process for you.
  • It Also Provide the chaching functionality
  • It take Object as argument where it contains
export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: "http://localhost:3000" }),
  tagTypes: [""], // it help to refetch data after a successful completion of the request
  endpoints: (builder) => ({}),
});
  • If we want to get the data then we need to use the builder.query method to get the data
 getTasks: builder.query({
      query: () => "/tasks",
      transformResponse: (tasks) => tasks.reverse(), // it help to get the data in reverse order
      providesTags: ["Tasks"], // it help to call the function after thesuccessful completion of the request
    }),
  • But for Post,Update or delete then we have to use the builder.mutation method
 addTask: builder.mutation({
      query: (task) => ({
        url: "/tasks",
        method: "POST",
        body: task,
      }),
      invalidatesTags: ["Tasks"], //this will call the getTasks()  after post call made
    }),
  • After Using the createApi if we wanted to use in the app then we must need to Wrap our component with the < ApiProvider> < /ApiProvider>Component
<ApiProvider api={api}>
  <App />
</ApiProvider>

Commonly asked Redux questions to help you ace your interview! πŸ’»

What is Redux, and why is it used in React applications?

Redux is a predictable state management library commonly used with JS applications. It provides a centralized store to manage the state of the entire application, which helps in maintaining consistency, predictability, and traceability of the application's state. Redux is particularly useful for managing complex state interactions, avoiding prop drilling, and facilitating easier debugging and testing.

Why Redux is used in React applications:
Centralized State Management: Redux provides a single source of truth for the application's state, making it easier to manage and maintain.
  Predictability: Redux enforces strict rules for how the state can be updated, ensuring that the state changes in a predictable manner.
  Debugging: Redux's single state tree and clear flow of actions and reducers make it easier to debug and understand how the state changes over time.
  Testing: Redux's pure functions (reducers) and clear state transitions simplify unit testing.
  Middleware: Redux allows the use of middleware to handle side effects, asynchronous actions, logging, and more, making it a flexible solution for various needs.
  Time-Travel Debugging: With Redux DevTools, developers can rewind and replay state changes, aiding in identifying issues and understanding the application's behavior.
  Scalability: Redux is designed to handle applications of any size, making it suitable for both small and large applications.

Explain the core principles of Redux (Actions, Reducers, Store).

Redux is built on three core principles: Actions, Reducers, and Store. These principles work together to manage and update the state of an application in a predictable and organized manner.

Actions: Actions are plain JavaScript objects that represent an intention to change the state.
An action must have a type property, which indicates the type of action being performed. Optionally, it can have a payload property that carries additional data needed for the state transition.
 
    const incrementAction = {
     type: 'INCREMENT',
    payload: 1
    };
    
Reducers: Reducers are pure functions that take the current state and an action as arguments, and return a new state.
Reducers specify how the application's state changes in response to actions sent to the store.
Reducers must be pure functions, meaning they should not have side effects and should return the same output given the same input.
 
   const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + action.payload;
    case 'DECREMENT':
      return state - action.payload;
    default:
      return state;
  }
};
    
Store: The store is a central repository that holds the entire state of the application. or we can say Store holds the application's state and provides methods to access, dispatch actions, and listen for state changes.
getState(): Returns the current state of the application.
dispatch(action): Dispatches an action to the store to change the state.
subscribe(listener): Adds a change listener that is called whenever anaction is dispatched, and the state might have changed.
Reducers must be pure functions, meaning they should not have side effects and should return the same output given the same input.
 
    const incrementAction = {
     type: 'INCREMENT',
    payload: 1
    };
    

Describe the flow of data in a Redux application.

Action Creation: An action is an object that describes a change or event in the application. It has a type and, optionally, a payload.
Dispatching an Action: An action is dispatched using the dispatch function. This sends the action to the Redux store.
Reducers: Reducers are pure functions that take the current state and the dispatched action as arguments. They return a new state based on the action's type and payload.
Store Update: The Redux store updates its state based on the new state returned by the reducers.
State Access: Components access the updated state using useSelector (in React) or by subscribing to the store.
UI Re-render: Components that depend on the updated state re-render to reflect the changes.

Work Flow

1. Action Creation β†’ 2. Dispatching an Action β†’ 3. Reducers β†’ 4. Store Update β†’ 5. State Access β†’ 6. UI Re-render


Why is immutability important in Redux, and how is it achieved?

Immutability ensures that state changes are predictable and traceable. Each action results in a new state, making it easier to debug and understand the flow of data.

Immutability enables shallow comparison checks, improving performance. React components can quickly determine if they need to re-render by comparing previous and current state objects. Immutability prevents unintended side-effects by ensuring that state objects are not modified directly. This leads to more reliable and maintainable code.
How is Immutability Achieved in Redux?

♦ Reducers are designed as pure functions that take the current state and an action as arguments and return a new state without modifying the original state.
♦ The spread operator (...) is used to create shallow copies of objects or arrays, ensuring that the original state remains unchanged.
♦ Methods like map, filter, and concat create new arrays without mutating the original array.
♦ Libraries like Immer can be used to simplify immutable state updates. Immer allows you to write code that appears to mutate state while ensuring immutability.


What are Action Creators?

Action creators are functions that create and return action objects. They encapsulate the creation of actions, making the code more modular and reusable.
They help centralize and organize the logic for creating actions, making the codebase cleaner and easier to maintain.
They encapsulate the creation logic, making it easier to update or change the way actions are created without affecting other parts of the code.


Explain the role of Reducers in Redux.

Pure functions that take the current state and an action as arguments and return a new state. They specify how the state of the application changes in response to actions sent to the store.
They do not produce side effects and always return the same output for the same input.
They encapsulate the creation logic, making it easier to update or change the way actions are created without affecting other parts of the code.They never modify the existing state directly but instead return a new state object.
Reducers determine how the state should transition based on the action type and payload.
Each reducer handles specific action types and updates the state accordingly.


What is a Redux Selector? Why and when would you use it?

A selector in Redux is a function that takes the Redux state as an argument and returns specific data from the state. It allows components to extract and subscribe to only the parts of the Redux state that are relevant to them.
Selectors provide a centralized way to extract data from the Redux store.
Selectors can be memoized using libraries like Reselect to improve performance by caching results.
Reducers determine how the state should transition based on the action type and payload.
They promote reusability by encapsulating logic related to retrieving derived state or computed values.
When components need to access parts of the state that are deep or nested.
To avoid unnecessary re-renders by ensuring that components only update when relevant parts of the state change.


Discuss the difference between Redux Thunk and Redux Saga for handling async actions.

Redux Thunk: Selectors provide a centralized way to extract data from the Redux store. Commonly used for simpler async logic or straightforward API calls. Easier to integrate into existing Redux applications due to its simplicity and minimal setup. Redux Saga: Suitable for complex async flows, long-running tasks, and scenarios requiring advanced control over async actions.


What is a Redux Middleware? Provide examples of commonly used middlewares.

Middleware in Redux intercepts actions dispatched to the store before they reach the reducer. It sits between the action dispatch and the reducer invocation, allowing you to perform additional logic, side effects, or modifications on actions.
Commonly Used Redux Middlewares
♦ Redux Thunk
♦ Redux DevTools Extension
♦ Redux Saga
♦ Redux Logger
♦ Redux Persist


How does a middleware handle actions in the Redux flow?

Redux middleware is software that intercepts and provides a third-party extension point between dispatching an action and the moment it reaches the reducer. It allows you to customize the Redux behavior in various ways, such as logging actions, handling async actions, or modifying actions before they reach the reducer.
♦ Middleware is a curried function
♦ Interception: Middleware intercepts every action dispatched using store.dispatch(action).
♦ Next Function: Middleware functions receive a next function as an argument, which represents the next middleware in the chain or the Redux store's dispatch function if it's the last middleware.
♦ Processing: Middleware can inspect the action, perform async operations, modify the action, or dispatch new actions based on conditions. Passing to Next Middleware: After processing, middleware can either:

 Pass the action to the next middleware (if any) using next(action).
Optionally, modify the action before passing it (next(modifiedAction)).

♦ Final Dispatch: If a middleware chooses not to call next(action) (e.g., it handles the action itself), the action flow stops at that middleware, and it doesn't reach the reducer.
♦ Async Actions: Middleware like Redux Thunk or Redux Saga handles async actions by delaying the action dispatch, performing async operations (like API calls), and then dispatching new actions based on the async operation's success or failure.
♦ Termination: Middleware can prevent actions from reaching the reducer altogether or modify the action payload before it reaches the reducer.


What are the main features of Redux Toolkit?

RTK sets up a default Redux configuration that includes good practices such as using immer for immutability, and providing a simpler way to define reducers and actions.
createSlice: This API allows you to define a slice of your Redux state with a reducer function and automatically generated action creators. It reduces boilerplate code significantly.
configureStore: A utility function that combines reducers, middleware, and other store configuration settings into a Redux store instance. It simplifies store setup, including support for middleware and the Redux DevTools Extension.
Immutability and Immer Integration: RTK encourages immutable updates to state by default, using the immer library under the hood. This makes state updates safer and easier to reason about.
Async Logic Simplification: It simplifies handling async logic with the createAsyncThunk API, which generates async action creators for API requests. This removes the need for separate action types and action creators for each async operation
Slices and Encapsulation: Slices allow you to encapsulate related Redux logic, including reducers and actions, into separate modules. This promotes modularity and easier code organization.


Why might someone prefer using RTK over traditional Redux?

RTK significantly reduces the amount of boilerplate code required to set up a Redux store, define actions, reducers, and action creators. It simplifies the process with utilities like createSlice, createAsyncThunk, and configureStore.
♦ Simplified Code
♦ Built-in Middleware
♦ Immutable Update Logic
♦ Performance Optimizations


Discuss the benefits of using createSlice() in Redux Toolkit.

createSlice() combines the creation of action types, action creators, and reducers into a single, concise function.
createSlice() automatically generates action type constants and action creators based on the reducer functions you define. This eliminates the need to manually define these elements, reducing potential errors and making your code more maintainable.
The reducer functions in createSlice() use Immer under the hood, allowing you to write mutable update logic while ensuring immutability
can define asynchronous actions using createAsyncThunk
createSlice() allows you to handle actions defined outside of the slice using extraReducers, enabling better integration with other parts of your Redux store and third-party libraries.


Discuss the benefits of using createSlice() in Redux Toolkit.

createSlice() combines the creation of action types, action creators, and reducers into a single, concise function.
createSlice() automatically generates action type constants and action creators based on the reducer functions you define. This eliminates the need to manually define these elements, reducing potential errors and making your code more maintainable.
The reducer functions in createSlice() use Immer under the hood, allowing you to write mutable update logic while ensuring immutability
can define asynchronous actions using createAsyncThunk
createSlice() allows you to handle actions defined outside of the slice using extraReducers, enabling better integration with other parts of your Redux store and third-party libraries.


How does memoization play a role in Redux selectors for performance optimization?

Memoization is a technique used to improve performance by caching the results of expensive function calls and returning the cached result when the same inputs occur again. In Redux, memoization is crucial for optimizing selectors, which are functions used to retrieve specific pieces of state from the store.
The reselect library provides the createSelector function to create memoized selectors. createSelector takes one or more input selectors and an output selector. It recomputes the output only if one of the input selectors' results has changed.


About

Anurag Singh Procodrr complete Redux Note

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published