March Flash Sale

Redux Interview Questions and Answers for 2024

Redux is an important tool for developing web applications. It provides a predictable, centralized state management system for React and React Native applications. It enables developers to create complex applications with better performance, simpler code, and fewer bugs. Redux also allows for easier debugging and testing of applications, as well as improved scalability and maintainability. Redux's architecture makes it easier to share data between components, making it an ideal choice for large-scale applications. Developers with Redux skills are in demand, but Redux interview questions can be challenging to answer, as they are often focused on the technical aspects of Redux. We have curated the most frequently asked Redux interview questions and answers. These top interview questions on Redux have been categorized into basic, intermediate, and advanced questions for the ease of readers. These Redux interview questions as your go-to material to crack any Redux interview with ease.

  • 4.7 Rating
  • 65 Question(s)
  • 35 Mins of Read
  • 10161 Reader(s)

Beginner

Redux is a library that helps to manage the “state” of our app or we can say it is a cache or storage that can be accessed by every component of our app in a structured manner. 

It simply serves as a storage system for storing the state of variables in your project. In order to prevent components from randomly updating or reading the store, Redux builds a process and procedures to communicate with the store. It is comparable to a bank; even though you have money in your account, you cannot just go in at any time, open the vault, and take the money. To withdraw money, you must follow specific procedures.

Redux's main purpose is to manage and centralize application state, and it is typically combined with JavaScript libraries like React or Angular to create user interfaces (UIs) (User Interfaces). Its main goal is to facilitate the scaling of the application by offering the means for managing the state using a unidirectional data flow design.

Redux has three most important basic principles: 

  • Single Source of Truth: The overall state of our application is always kept in an object tree within a single store. A single state tree makes the debugging and inspection easier. This results in faster development cycles by keeping you in the app’s navigation state. 
  • The state is read-only: The state of our application can only be altered by issuing an action, which is an object that specifies the change that has happened. This ensures that no events like network callbacks or views can change the state directly. They can only express an intent to change the state using actions. 
  • Use Pure functions for state changes: According to this principle, we must build pure reducers to specify how the actions alter the state tree. Pure function makes it simple to control the order of reducer calls, pass additional data, or even create reusable reducers. 

A development paradigm called Component-Driven Development (CDD) centers the build process around components. It is a method of creating user interfaces that work from the level of components all the way up to the level of pages or screens. In other words, component-driven development entails creating independent, loosely-coupled components for your software applications.

By breaking programming down into components, you can create modular sections with precisely targeted APIs. As a result, building each component and determining when it is adequate takes less time. You can extend or update the component rather than needing to rework larger portions of your application when you need to tweak or update a specific section of it. 

Components can be extended and reused through the separation of concerns to develop various applications rather than having to repeatedly rewrite them. 

Unit tests may be implemented much more easily to verify the targeted functionality of each component when they are built as modular components. Larger systems can be tested more readily since it is simpler to comprehend and distinguish between the duties of each component of the system. 

The single source of truth in the Redux's official documentation refers to the state tree of our application, which is not overwritten or altered, and is the only reliable source of information in Redux. It enables us to constantly access information with ease and preserve a clear organizational framework for the status of our application. It is crucial to consider whether you are altering data or merely appending to the state when developing your reducers. The reducers act as gatekeepers to the source of truth because of the one-way directed data flow pattern (i.e. unidirectional data binding), which guarantees that the state is not directly changed.

Redux utilizes an immutable state to operate properly in an efficient manner. This implies that in order to change any fields in the Redux state, we must first make a duplicate of the entire state, for example;

let newState = { 
                ...oldState, 
                field1: oldState.someValue,  
                field2: someNewValue 
} 

React only seeks to rerender a component if the value of the props or state has changed. To determine whether one of these values has changed it performs a "deep comparison" in which it iteratively examines each field inside the state and prop to see if they have changed.

When using Redux, large apps typically have a very deep state structure with multiple nested levels (typically in the count of 100s or even 1000s). The UI will lag if a deep comparison is performed here, possibly multiple times per second. On the other hand, it will be quicker if we perform a "shallow comparison" in which we merely check to see if the values of the first level fields have changed, but we risk missing changes and violating application logic.

If we could just perform a superficial reference check without missing any changes, the issue of rerendering could be resolved. This would provide us with the performance we require while maintaining the application's logic.

This can be accomplished using the presumption that "if and only if the reference of two variables (state variables in this case) are different, we can assume that their values are different." So, we can simply perform a reference check, such as oldState === newState, to see whether a state's value has changed (if this is false, then the reference has changed). We can infer that the values must have changed if their reference has changed and triggered rerender. 

Expect to come across this popular question in Redux interview questions.  

The store in Redux is the main central bucket that stores all the states of an application. It works as a single source of truth for all the states of the application. It refers to an immutable object tree that contains all the states of the application.

The store and state are two distinct concepts. While the state is like a data object, the store is a behaviour object that manages the state using the methods/behaviours of the store.

There can be only one store in a Redux app. Albeit one store can manage multiple state objects in the program, and act as a single source of truth for all the states. The state object in the store is immutable; thus, every time a change happens, a new state is created to replace the current state. All the state changes are handled by pure reducers.

A store has three important methods: 

  • store.getState() : It returns the state object from the store. 
  • store.dispatch(action) : The dispatch function takes the action argument that has the change information (e.g. type and payload). The dispatch function calls the reducer to make the state change.  
  • store.subscribe() : It assists you in setting up a callback that the Redux store will make once an action has been sent. The view will re-render instantly after the Redux state has been updated.

A must-know for anyone heading into a Redux interview, this question is frequently asked in React Redux interview questions.  

This question is one of the very important React Redux interview questions, here, the interviewer knows that those who have worked with Redux will find it very easy to answer this.

As an alternative to the existing connect() Higher-Order Component, React-Redux provides a set of hook APIs to link container components with Redux. These APIs let you dispatch actions and subscribe to the Redux store without needing to wrap your components in connect(). By combining the Hook API with Function components, components are kept minimal and the code remains clean.

There are two sets of hooks that can be used in place of the connect() higher-order component: useSelector and useDispatch. UseSelector is the alternative to mapStateToProps; it accepts a function parameter that returns the desired portion of the state. 

UseDispatch is the alternative to mapDispatchToProps; We can use useDispatch and then put the result into the dispatch variable. The dispatch variable can work with all Actions imported from the actions folder.

It's no surprise that this one pops up often in React and Redux interview questions.  

There are several advantages of using Redux in your application: 

  • The store retains the state data in Redux. The data is easily accessible to all of the application's components directly. This centralizes all data and makes obtaining the state inside the component relatively simple. The Redux store is therefore strongly recommended when creating large, intricate apps with numerous components. 
  • By avoiding pointless rerenders and making sure that a particular component only rerenders when its data has truly changed, the Redux store enhances the efficiency of the app. 
  • Redux makes it easy to debug and test code by logging behaviour and status. It is frequently used for time-travel debugging since it represents the whole state of an application at any given point in time. 
  • By saving the application's state to local storage and restoring it after a refresh, Redux also provides state persistence. 

Flux is an application design paradigm for creating client-side web apps. It incorporates the concepts of unidirectional data flow and creates enhanced React's composable view components. It is created by Facebook, and you don't need to write a lot of new code outside of React to use Flux right away.

Flux applications are primarily composed of three components: the dispatcher, the stores, and the views (React components). 

  • Store: It contains the flux state; both domain state and user interface state are stored inside the Store. The state is like a data object that is managed by methods in a behaviour object called a Store. 
  • Dispatcher: Dispatcher is a solitary entity that transmits actions (i.e. events) to all registered stores. At the time the application launches, stores must register for events. When an action is received, it is sent to all stores that have registered. 
  • Views: View is essentially a user interface component typically a React component. It is in charge of processing user interaction as well as rendering the user interface. Views monitor store changes and rerender, as necessary. 

Redux's store does not have to include all of the component states. Some users choose to retain all of their data in Redux so that they may always have a fully serializable, controlled version of their application. Others choose to maintain the UI state or non-critical state within a component's internal state.

Therefore, it is up to the developer to strike a balance, choose the types of states that make up your program, and decide where each piece of state should reside. 

Generally, the rule of thumb is to minimize the global state of the application. Many times there is no need to jam everything in the Redux state. For instance, let us consider the drop-down menu, other areas of the app will not likely be impacted by whether it is open or closed. Therefore, putting it in the Redux Store is generally unnecessary. Although nobody is stopping you from it, you will not actually gain anything from it. 

A common question in React native Redux interview questions, don't miss this one.  

Redux Toolkit is a collection of tools that makes Redux development easier. It offers tools for building Redux actions and reducers as well as for generating and managing Redux stores. When utilizing Redux, the Redux team advises using the Redux Toolkit.

Additionally, it comes with the most well-liked Redux add-ons, like Redux Thunk for asynchronous logic, Reselect for creating selector functions, and a number of others that facilitate development and saves the developers time.

It was primarily developed to address Redux's three primary problems: 

  • Redux store configuration is too cumbersome. 
  • To create a large-scale application, many packages must be added. 
  • Redux's excessive use of boilerplate code makes it difficult to develop effective and organized code.

It is a common saying that the Redux toolkit is Redux on steroids for functional components. This statement is quite popular for a variety of benefits of the Redux toolkit, such as:  

  • Compared to Redux, far less boilerplate code is needed. 
  • UseSelector and useDispatch Redux hooks keep things simple and straightforward to use. It is really advantageous to use these hooks in functional components and may be helpful for people who are absolutely new to Redux. 
  • Redux toolkit includes an out-of-the-box createAsyncThunk that makes it easy for you to carry out async operations. This eliminates the requirement for manual thunk configuration. 
  • In order to access state variables across all of your actions or async activities, getState is a highly useful tool. 
  • Do straightforward tasks and let the Redux toolkit handle mutability in the background. 
  • If you want to troubleshoot and figure out where something is going wrong, you can utilize “current” to log your state wherever. Of course, Redux's debugging tools are excellent. 

One of the most frequently posed Redux React interview questions, be ready for it.  

One of the most popular state management packages for complex React apps is Redux. Redux is the greatest option for large-scale applications due to its ability to increase predictability and the ecosystem that has developed around it. Redux also provides a developer tool that makes it simple to track when, where, why, and how the state of your application has changed.

Redux DevTool is a Chrome extension that offers a console from which we can help in the configuration of our Redux development environment. In addition to giving us control over the dispatch and action or state management options, Redux DevTools also allows us to visualize them for simple management. This extension makes it easier to visualize the internal workings of the actions and state changes that typically take place in a Redux-based application.

A staple in Redux interview questions, be prepared to answer this one.  

Redux DevTool makes it easier to track when, where, why, and how the state of your application has changed. The following list includes some of Redux DevTools' key features: 

  • Redux DevTools is a time-traveling environment that enables live editing in Redux with a range of features like action replay, rapid reloading, and customizable UI. 
  • We can examine every state and action payload using Redux DevTools, and we can travel back in time by simply canceling the action. 
  • If the code of the reducer is changed, each stage action is reassessed. 
  • With the help of the persistState() store enhancer, we can also carry on with our debugging sessions even after a page reload.

Redux uses read-only states, which means that for every valid action, a new state is created that incorporates the changes made by the action to the previous state. Redux is predictable because it requires an action to be dispatched before we can change the application's state. This action must specify the state changes we wish to make.

These actions are then consumed by reducers, whose primary purpose is to receive two items (the action and the current state of the application) and return a new updated instance of the state. Reducers do not alter any aspect of the state. Instead, a reducer creates a fresh instance of the state that includes all required modifications.

For instance, if the cart starts off empty, adding one item will result in an increment of one, making the total number of things in the cart equal to 1. And repeating the process of adding a single item to the cart will add a second item, bringing the total to two.

It will always provide the same final state of the entity given an initial state and a certain series of actions carried out in a particular order. Thus, Redux achieves predictability in state management in this manner.

Redux is very widely used with React for state management for big and complex projects. Hence, this article also has React.js Redux interview questions for your preparation. 

Redux is an independent state management toolkit, however, it needs a UI-binding library to interact with any front-end framework or library. The state container (or store) interaction logic, which consists of a series of preset stages connecting a front-end framework to the Redux library, is handled by a UI binding library.

The React components may receive data from a Redux Store and send Actions to the Store to update the data thanks to the React-Redux library, which is the official Redux UI binding library for React applications. The Redux team is in charge of maintaining it. Redux's goal is to enable state management through a unidirectional data flow architecture, which will aid applications in scaling well. 

Moreover, the Redux team has produced three libraries: Redux, React-Redux, and Redux Toolkit. To provide the best React programming experience possible, all three frameworks function in conjunction. 

This is a frequently asked question in React Redux interview questions.  

Generally, functional programming means using functions to the best effect for creating clean and maintainable software. It is a declarative programming style that puts more emphasis on “what to solve” in contrast to “how to solve.” 

Functional programming highly depends upon the concept of Pure functions. A pure function is one whose outcomes depend only on the input variables and whose operation has no side effects on the outside world aside from the return value.

Another important idea behind functional programming is immutability, i.e. avoiding modification of the data outside the function. This is a way of avoiding side effects. In other words, only the function's return value should reflect the work that was done, and function input arguments should be copied, not changed. 

  • Beyond the pure function ideal, functional programming depends on first class functions and higher order functions in actual coding practise.  
  • A first class function is one that is handled as an entity capable of standing on its own and receiving independent treatment. Functional programming utilizes functions as variables, parameters, and return values 
  • A higher-order function manipulates a function, it takes a function as an argument or returns a function. 

Object-oriented programming (OOP) is a computer programming style that organizes software design around objects (or data) instead of functions and logic. An object is a data field with particular characteristics and behaviour. 

Large, sophisticated, and actively updated or maintained programs enjoy many benefits by using Object-oriented programming styles, for instance, large desktop applications, manufacturing system simulation software, and many more. Additionally, it makes the software easy to collaborate and distributed among small teams. 

The OOP is based on four main principles; Encapsulation, Abstraction, Inheritance, and Polymorphism. This brings many benefits to the software in terms of reusability, scalability, and efficiency. 

JSX is essentially a JavaScript language extension that offers a practical mechanism to organize component rendering utilizing JavaScript's expressiveness along with HTML-like Template Syntax. JSX is an abbreviation for JavaScript XML, and it is an extremely important tool in React development. 

Additionally, using JavaScript and HTML together results in apps that are more powerful and perform better. With the help of JSX, we can construct HTML elements in JavaScript and add them to the DOM by transforming the HTML tags into React elements without the use of additional methods like createElement() or appendChild (). This greatly simplifies the HTML file's comprehension. Furthermore, using JavaScript and HTML together results in apps that are more powerful and perform better. 

Only JavaScript objects can be read by browsers, however, JSX is not a typical JavaScript object. So, to allow a browser to read JSX, we must first convert the JSX file into a JavaScript object using JSX transformers like Babel and then provide the JavaScript object to the browser. 

Due to its embedding in HTML components, JSX is not a valid JavaScript syntax. Browsers do not support JSX since it combines HTML and JavaScript. As a result, if a file contains JSX code, the Babel transpiler turns the JSX into JavaScript objects, which are then considered to be legitimate JavaScript. Thereafter, browsers can easily read the code and execute it. For instance, let us consider a JSX element,

import React from 'react'; 
import ReactDOM from 'react-dom'; 
   
const element = (  
    <button className = 'myButton' 
    style = { 
        { textAlign: 'center' } 
    }> 
    Click Me    
    </button> 
); 
   
ReactDOM.render(element, document.getElementById('root')); 

This JSX code is then transpiler by Babel in into a JavaScript code shown below,

import React from 'react'; 
import ReactDOM from 'react-dom'; 
const element = React.createElement("button", { 
        className'myButton', 
        style: { textAlign'center' } 
    }, 
      'Click Me' 
); 
ReactDOM.render(element, document.getElementById('root'));  

Redux adheres to the one-way data flow. This implies that the data from your application will follow a one-way binding data flow. If you have no control over the state of your program, adding new features and reproducing bugs becomes difficult as the application expands and becomes more sophisticated. 

Redux lowers the level of complexity in the code by imposing limitations on how and when state updates can occur. This makes it simple to manage updated states.

Redux's normal data flow begins with a callback from the user interface component, which then dispatches an action with a payload. The sent actions are then intercepted at the Redux Store and get passed to the reducers, creating a new application state. The actions are then passed from the Redux store down a hierarchy of components.

Unidirectional data flow, also referred to as one-way data flow, is a method that implies that the data can only be transmitted in one and only one direction from one component to another component of the application. This generally indicates that data coming from the parent component cannot be updated by child components. Functional reactive programming frequently employs the technique of unidirectional data flow. 

For instance, data coming from a parent is referred to as props in React. In contrast, Bi-directional binding is a technique used by Angular in which data flow occurs in both directions. React does not enable bi-directional binding, so you can be sure that your data flow is structured neatly in one direction. The main advantage of this strategy is that you have better control over your app because data flows throughout it in a single direction. 

Expect to come across this popular question in Redux MCQ questions.  

In Redux, data flow is strictly unidirectional, which means all the components in the Redux architecture follow the same lifecycle pattern. Having a unidirectional data flow in your application has a number of benefits, including 

  • Because every piece of data in an application follows the same lifecycle pattern, its logic is more predictable and simpler to comprehend. 
  • Additionally, it promotes data normalization to prevent the emergence of duplicate, unconnected copies of the same data. 
  • Debugging is much simpler when there is a one-way data flow. The developer can conduct a more effective dry run (or employ tools) when they are aware of the data's origin and destination. 
  • Data flow in one direction gives the developer more control and reduces the likelihood of errors in the software. 
  • An efficient program since excess resources are not squandered because the used libraries are aware of the restrictions and requirements of the unidirectional flow. 

A must-know for anyone heading into a Redux interview, this question is frequently asked in React Redux interview questions.  

The need for a deeply nested component to use data provided by another component that is far higher in the hierarchy frequently arises while developing a modern component-based application. The most straightforward method is to simply pass a prop from one component to the next in the hierarchical structure from the source component to the deeply nested component, this method is known as Prop Drilling.

To Avoid Prop Drilling, a Common Approach is to incorporate Redux in your application. Redux enables you to control the application's state through a unidirectional flow in which a child component can directly access the state from the Redux Store. Redux eliminates the necessity for prop drilling, which means that it is not necessary to obtain state updates from parent components.Component Initiating Change Source

React Context enables data transmission through the component tree without requiring manual prop transmission at every level (also known as Prop Drilling). When using a top-down approach to pass data in a React application, it can be inconvenient for specific types of props that are needed by numerous components. By using context, it is possible to pass values between components without explicitly passing a prop through each level of the component tree.

Considering a React component tree, context is intended to exchange information that could be regarded as "global," such as the currently authenticated user, theme, or chosen language.

Code becomes more complex and difficult to understand when Redux is used to connect states from parent to child components. Context makes it significantly simpler to understand and gives a less complex code than Redux.

Intermediate

It's no surprise that this one pops up often in React Redux interview questions.  

Actions in the Redux architecture are just regular JavaScript objects with type and payload fields. Action can be considered as an event that is used to explain a specific occurrence in the application. Actions only include the minimum necessary information to describe what change has happened.

For example, the initial state of the app is {basket: {apples: 0, mangoes: 2}}. An action to add 5 new apples to the app’s state can be written as:

const addFruits = { 
     type'ADD', 
     payload: {'apples' : 5} 
}  

To update the app’s state in the Redux Store we dispatch the actions, which are then handled by reducers. For example

store.dispatch(addFruits)

Constants in Redux ease the coding for programmers and prevent bugs that may arise due to spelling errors. While we are using an Integrated Development Environment (IDE), the Redux constants give suggestions for all the instances of a particular functionality across our entire project when. Anytime a mistake is made the Redux constants generate a "ReferenceError.” This helps programmers to avoid foolish issues brought on by clumsy typing or typos.

We can write constants in a separate constants folder into one or many files according to our project’s folder structure. For example

// constants.js 
export const ADDING_PRODUCT = 'ADDING_PRODUCT'; 
export const DELETING_PRODUCT = 'DELETING_PRODUCT'; 
export const UPDATE_PRODUCT = 'EDITING_PRODUCT'; 
export const EMPTY_CART = 'EMPTY_CART'; 

Once constants are stored, we can utilize them in our code in actions and reducers.

// actions.js 
import { ADDING_PRODUCT } from './constants'; 
export function addingProduct(product) { 
  return { type: ADDING_PRODUCT, product }; 
} 
// reducer.js 
import { ADDING_PRODUCT } from './constants'; 
export default (state, action) => { 
  switch (action.type) { 
    case ADDING_PRODUCT: 
      return [ 
        ...state, 
        { 
         cart: [...state.cart, action.product], 
        } 
      ]; 
    default: 
      return state 
  } 
};

A common question in Redux interview questions, don't miss this one.  

Presentational components are exclusively focused on the view of the component and are kept unaware of the Redux state. These Presentational components receive their data and callback functions as props.  

Whereas the container components are in charge of how things operate and are fully aware of the Redux state. They are generally created using React Redux and can dispatch actions to the Redux store. Moreover, the container components also subscribe to Redux state updates.

The separation of the components into presentational component and container component provides many benefits to the application, such as

  • Less code duplication: It compels the developers to move all the layout components out as separate presentational components, this allows developers to directly reuse them instead of copy-pasting the code in every page. 
  • Presentational components are the View Layer of your application: Without affecting the app's functionality, you may place all the Presentational components on a single page and let the designer change how they all look.  
  • Better separation of concerns. By writing components in this manner, you gain a greater understanding of your app and its user interface. 
  • Better reusability. You can combine the same presentational component with entirely different state sources to create distinct container components that can be utilized again and again. 

One of the most frequently posed Redux MCQ questions, be ready for it.  

In Redux, a function that actually produces an action object is known as an action creator. Redux allows action creators just to pass the parameter value and return an action object. There are synchronous action creators and asynchronous action creators in Redux.

When we call a synchronous action creator, it immediately produces an Action object with all the necessary data associated with it, ready for processing by our reducers.

Asynchronous action creators take a break for some time to execute an async task before they may finally dispatch an action. Any time an action creator sends a network request, by definition, it falls under the category of an async action creator. It is advisable to install a middleware to use asynchronous action creators in a Redux application.

A staple in Redux interview questions, be prepared to answer this one.  

A middleware module called Redux Saga can be helpful for enabling asynchronous interactions between a Redux store and external resources. For instance, Input/Output activities, accessing browser storage, sending HTTP requests to external services, and many more. These operations are also called side effects.

Redux Saga uses sagas, which are architectural patterns from the field of distributed transactions, to make dealing with side effects easier. 

A saga controls processes that must be carried out in a transactional manner, keeping track of the execution's status and making up for unsuccessful processes. The sagas are declarative and significantly simpler to test without a mock.

Redux uses a saga as middleware since a reducer, which must be a pure function, cannot be used to manage and initiate asynchronous activities (side effects).

A Generator function gives us an iterator, which we can use to pause the function midway, do an action, and then resume it whenever we like. A Generator function can be interrupted as many times as desired and resumed later unlike a standard function, which begins execution and returns when it is finished.

When you call a generator function, it does not immediately start running in full. It will give back an iterator object that may be used to call the function. Therefore, this function can be run sequentially, with the yield expression determining which sections to run when.

The iterator's .next() method is used to run these sections. The function continues running after the .next() method is called until the next yield is located, the function completes, or a return statement is executed. The generator function always returns an object to you when you call the .next() method.

In JavaScript we use function* as the prefix to the generator function name, for example,

function* getNaturalNumber() { 
        let number=1; 
        while (true) 
            yield number++; 
    } 
 
var naturalNumberIterator = getNaturalNumber(); 
 
console.log(naturalNumberIterator.next().value);  // 1 
console.log(naturalNumberIterator.next().value);  // 2 
console.log(naturalNumberIterator.next().value);  // 3 
console.log(naturalNumberIterator.next().value);  // 4 

This question is a regular feature in Redux interview questions, be ready to tackle it.  

When we refresh a page in a web application, the state always resets back to the default values, which is bad when you want to construct a huge online application like an e-commerce site.

Using the native JavaScript localStorage.setItem() method, we can manually make the state persistent, but in reality, this would require us to write the entire thing from scratch and maintain the state's logical structure.

Developers can save the Redux store in persistent storage, such as the local storage, by using the Redux Persist package. As a result, the site state will still be retained even after refreshing the browser. Additionally, Redux Persist provides methods with an intuitive API that lets us customize the state that is saved and rehydrated.

This is a frequently asked question in React Redux interview questions.  

Redux enables you to control the application's state through a unidirectional flow in which a child component can directly access the state from the Redux Store. Redux eliminates the necessity for prop drilling, which means that it is not necessary to obtain state updates from parent components.

As the application's programming and functionality become more complicated, controlling its state will likewise become more difficult until we have sound management or control over the application's state. This is accomplished in Redux using the Redux Workflow, which consists of the subsequent phases.

  1. When a user engages with an application and performs certain actions, the dispatcher sends out an action object to reflect these activities. This indicates that any changes a user makes to an application trigger a request to the store in order to alter the application's state. 
  2. After receiving the request, the store accepts the action object and calls the root reducer function to carry out the required action and give the application's next stage. The task is divided into smaller reducer functions by the root reducer, which together carry out the entire change state of the application. 
  3. The updates are then transmitted to the middleware, which tests the actions to see if they are valid for the store. The action is modified by the middleware as needed, and the updates are then sent to the store. 
  4. When a store receives information about changes in the application's state. When a state changes, the store notifies registered listeners of the change. The application is then updated in various places as necessary. 

Expect to come across this popular question in React and Redux interview questions.  

Reducers are pure functions that take an application's state and an action as inputs and output a new state. In Redux, reducers intercept the dispatched actions inside the Store and create a new State with the necessary changes to update the Redux state.

For example, let us assume we have two action objects increaseAction and decreaseAction, these actions may get dispatched and intercepted by the reducer.

const increaseAction = { 
  type'INCREASE', 
};  
const decreaseAction = { 
  type'DECREASE' 
};  

The increaseAction has a type of property ‘INCREASE,’ this signals the reducer about the kind of action that has occurred. The Reducer then executes the change and returns a new state according to the action object, i.e., increase the count by 1. Similarly, the count decrease by 1 for decreaseAction, although the action may not be from one of the expected types in such case the current state is returned.

const initialState = { count0} 
const countReducer = (state = initialState, action) => { 
  switch(action.type){ 
  case INCREASE:  
    return {...state, count: count + 1}; 
  case DECREASE :  
   return {...state, count: count - 1}; 
  default 
return state; 
  } 
};  

Using the Redux combineReducers tool, we can now combine all the reducers and create a root reducer. The combineReducers method creates an object whose values are other reducer functions. This root reducer is passed to the Redux’s createStore function to our Redux store.

import countReducer from './countReducer'; 
import songsReducer from './songsReducer'; 
import { combineReducers, createStore } from 'redux'; 
const rootReducer = combineReducers ({ 
  countReducer, 
  songsReducer 
}); 
const store = createStore(rootReducer);  

In the Redux library, reducers are used to change the registered states inside the Redux store. Thus, reducers are crucial in a Redux application and hence special attention must be paid while writing the reducer’s logic. Here are some of the things that we should never do inside a reducer in our Redux app. 

  • While writing reducer logic, we must never modify the argument of the reducer. In Redux, reducers are pure functions and inputs ought to be copied rather than changed. 
  • We must ensure that we do not carry out any side operations (or side effects), such as API requests, routing changes, etc. 
  • In a reducer, we should not use non-pure functions like Date.now(), Math.random(), etc. 

The Redux Form is a HOC (Higher Order Component) that makes use of React-Redux to allow HTML forms in React to store all of its state in Redux, with access to these states from both React and Redux. Redux Form offers great compatibility with various UI frameworks, such as Material UI, React Widgets, React Bootstrap, etc.

Redux Form is really simple to utilize and put into practice; all we need to do is wrap our form component in the HOC that Redux Form provides, and we are good to go.

Redux Form makes it very simple to apply validation to the form. We can add validations for the entire form as well as specific fields.

Redux form provides several benefits to the form component in our applications: 

  • Effortless development: Redux forms greatly simplify the process of controlling a form's state. You can simply wrap your form in HOC and utilize Redux-form Field. That is all, your Redux form is complete and ready for usage. 
  • Reusable Component: We can also create and reuse input components for the form fields. 
  • Validation: For working with validations, Redux-form is a really convenient package. You can simply create a large number of validations for various situations. It offers validation methods, allowing you to check all the data in your form at once. Additionally, you can offer unique value validation functions for every Field or FieldArray. Furthermore, you only need to define your validation routines once for your application in order to utilize them across all of your forms. 

There are several disadvantages to using a Redux form excessively in your application, such as. 

  • React is best used for the transient state that does not affect the app globally and does not alter in complicated ways. Contrarily, use Redux for a state that is important globally or is altered in complex ways. However, Redux form defies this rule of thumb and utilizes Redux for transient form data. For example, each keystroke you make while typing must be saved and modified, and you must keep track of all the field values to determine whether a field has been touched, is empty, or has the wrong value. 
  • Performance is the most crucial aspect of development, however, with Redux form, every small form state update goes through an entire Redux cycle. Every keypress costs us additional rerendering of the entire application. It causes the UI to freeze for several milliseconds, which the user will notice. It is a serious issue if you ddo notwant to ruin the performance of your application. 
  • It is significantly more challenging to manage your form in the redux-form store if it has a more complicated model or multi-step wizards. 
  • Assume we want to execute total = price + tip in our Redux form. The price and tip values saved in Redux must be fetched in order to do this straightforward calculation. After that, use dispatch to modify the value for the total that is saved in Redux. It adds a lot of boilerplates and is difficult to understand. Additionally, changing a field value or type dynamically in a Redux form will be very challenging. 

Virtual DOM, as its name suggests, is a virtual replica of the real DOM. Here, virtual refers to a much lighter version of the actual DOM that can be stored in the browser memory as an object. Therefore, it is a memory-based representation of the Real DOM. The Real DOM is synchronized with the representation of a user interface that is maintained in memory.

The actions of the virtual DOM effectively supplement or support those of the real DOM. It offers a method that enables the actual DOM to compute the bare minimum amount of DOM operations while rerendering the user interface.

As part of its declarative methodology, React implements the idea of a virtual DOM during the rendering process. This method enables us to define the state that the user interface (UI) should be in before React actually implements it. It helps to write more predictable, smoother code so we can concentrate on building components by abstracting manual DOM manipulations away from the developer.

In certain cases, a simple one-object DOM will be sufficient to meet all of the needs of your web app or web page. However, there are situations when you need external scripts to display something without interfering with your already-existing elements.

In such situations, shadow DOMs are useful. Shadow DOMs are DOMs that exist inside the original DOM, have their own set of scopes, and sit in isolation.

Shadow DOMs, which are effectively independent web components, allow modular interfaces to be built without interfering with one another. 

Shadow DOMs support encapsulation, or the ability to keep HTML structure, style, and behaviors distinct and concealed from other code to prevent conflicts. For instance, conside a range input.

<input type="range" />  

When we use the developer tools in the browser to investigate the element, all we will see is a straightforward input element. However, browsers internally encapsulate and conceal additional components and styles that make up the input slider.

To view the shadow DOM using Chrome DevTools, select "Settings," then "Show user agent shadow DOM." 

In Redux, all of your application state is stored in the store, which is an object that contains your app's entire state tree. To update the state stored inside the Redux Store, we must dispatch actions as it is the only way to interact with Redux Store.

Redux sends a dummy action to your reducer when a store is created in order to fill it with the initial state. Just keep in mind that your reducer should return some sort of initial state if the state provided to it as the first parameter is undefined. You are not supposed to handle the dummy action directly.

Since, we do not want the initial state of our application to be undefined we must initialize the state manually. The initial state can be passed directly to the createStore method.

// initial State 
const  initialState = { 
  todos: ["eat""code""sleep"], 
} 
 
function todosReducer(state, action) { 
  switch (action.type) { 
    case 'ADD_TODO': 
      return {...state, todos: [...state.todos, action.todo]} 
    default: 
      return state 
  } 
} 
 
const store = createStore(todosReducer, initialState) 

The other approach is to pass a default state parameter to the reducers so that when the dummy action is passed, the reducer will use the default initial state from its parameters.

// initial State 
const  initialState = { 
  todos: ["eat""code""sleep"], 
} 
function todosReducer(state = initialState, action) { 
  switch (action.type) { 
    case 'ADD_TODO': 
      return {...state, todos: [...state.todos, action.todo]} 
    default: 
      return state 
  } 
} 
const store = createStore(todosReducer) 

A must-know for anyone heading into a Redux interview, this question is frequently asked in React Redux interview questions.  

In Redux, all of your application state is stored in the store, which is an object that contains your app's entire state tree. The connect function from the react-redux package or the useSelector hook is the typical method for gaining access to the store from a React component. These functions let you define the state-related parts that your component needs, and if any of those state-related pieces change, they will automatically be passed along and the component will be re-rendered.

But rarely, you might need to use a component outside of React to retrieve the Redux store state. In these circumstances, you can obtain the store's current state by using the store.getState() method. The whole state object is returned by this method, which you can use to retrieve the particular values you require.

For example, in the code below, we are exporting the store object. This object can be imported at any file to access the Redux State using store.getState()

// store.js 
import { createStore } from 'redux'; 
// Create a Redux store 
export const store = createStore(reducer); 
// someFile.js 
import {store} from './store.js' 
// Get the current state of the store 
const state = store.getState(); 
// Access a specific piece of state 
const value = state.someKey;  

It's no surprise that this one pops up often in Redux interview questions.  

Selectors are Redux functions that take a Redux state as input and return a subset of it after applying various calculations, transformations, mappings, filters, etc. In this manner, a selector might contain the logic of how to retrieve data for a certain view. For example,

const data = useSelector(state => state.some.deeply.nested.field)

The state structure should ideally only be known by your reducer functions and selectors, so you would only need to update those two pieces of logic if you changed where any state is stored. Therefore, instead of always defining reusable selectors inside of a component, it is typically preferable to declare them directly inside of the slice files.

Wherever you have access to the complete Redux root state value, you can use a selector function. This covers middleware, Thunks, Sagas, the useSelector hook, and the mapState function for connect, and more.

Selector functions frequently have to carry out somewhat "expensive" calculations or create derived values that are new object and array references. Since the pricey procedures will produce the same results when called again without a state change, you should omit them.

These costly selector calculations can be memoized with the help of the Reselect library. To produce memoized selectors, Reselect offers a function named createSelector. In addition to accepting one or more "input selector" functions, createSelector also accepts an "output selector" function. It then returns a brand-new selector function for you to utilize.

When you invoke the createSelector, Reselect runs your input selectors using each of the arguments you supplied and examines the values that are returned. The output selector will be run again with those results as the arguments if any of the results are different from the previous results. It will return the cached final result from before if all of the results are identical to those from the previous run of the output selection.

A common question in React Redux interview questions, don't miss this one.  

The idea that your Redux Store should be divided into slices rather than numerous reducer files, action files, and selector files has been formalized thanks to the Redux Toolkit. Each slice is typically responsible for owning any reducers, selectors, or thunks that primarily access or manipulate its particular portion of the data.

Reducers are where the Redux State is updated, and RTK's createSlice function makes the process of creating them simpler. Each of our reducers has the opportunity to respond to an action by creating a new state when it is passed to our Redux Store. Without RTK, Redux reducers frequently have massive switch statements that scan for various action kinds. RTK allows you to design reducer methods and automatically generates the actions and action types related to those methods. 

Advanced

One of the most frequently posed Redux interview questions for experienced, be ready for it.  

Redux Thunk is a middleware that can be used to write action creators that return a function rather than an action. Middleware allows to intercept every action passed to the reducer and adds changes to it. The redux-thunk can perform conditional dispatching or delay the dispatch of an action.

In plain Redux, we cannot put complex logic inside action functions, it only allows simple synchronous updates by dispatching actions. Thunk lets you write complex logic that can interact with the store. Thunk gives us the ability to dispatch actions manually, thus allows to run some logic sync or async before dispatching an action.

When an action creator returns a function, the Redux Thunk middleware can execute the function and it is not required to be pure. In other words, the function is permitted to execute asynchronous API calls as well as additional side effects.  

A staple in Redux interview questions, be prepared to answer this one.  

Both Context API and Redux can be used for state management in a react app. On the one hand, context API is a built-in tool provided by React, while on the other hand, Redux is a framework-agnostic state management library that can be used with React, Angular, Vue, etc. However, React Redux is a very commonly used combination.

Before choosing the state management solution for your app, you must first determine the size of your project. This is necessary so that you will not have to convert everything from Context API to Redux when your application expands in scale. It also helps to know which data needs to be refreshed and how frequently.

When working on a bigger, more complicated project, we often use Redux for global state management. The Context API is worthwhile to use if the project is smaller or if we concentrate on encapsulating modules.

Redux gives you access to the powerful debugging tool Redux DevTools, allowing you to go back in time and observe changes made to your store step by step. In Context API debugging becomes complex and painful in a highly nested structure of React components.

This question is a regular feature in React Redux interview questions, be ready to tackle it.  

When we use middleware in our project, every action that we dispatch will call the middleware, which can manipulate an action in any way inside of that function, including stopping it from being sent to any of the reducers, logging it, changing it, and many other possibilities.

Let us create our custom middleware. To define a custom middleware, we must follow the syntax below,

const middleware = (store) => (next) => (action) => { 
  // your code 
};  

Let us create a custom middleware called myLoggerMiddleware. This will log the actions to the console if the action object has shouldLog property as true.

const myLoggerMiddleware = function (store) { 
  return function (next) { 
    return function (action) { 
     if (action.shouldLog) { 
     console.log("action", action) 
     } 
     next(action); 
    }; 
  }; 
}; 

To apply the middleware to our Redux store, use applyMiddleware function which returns a store enhancer that will apply the myLoggerMiddleware to the Store.

import { applyMiddleware } from "redux"; 
const middleware = applyMiddleware(myLoggerMiddleware); 

Finally, to create the Redux Store, pass the reducer, and middleware (as enhancer) to the createStore(reducer, [preloadedState], [enhancer]) function.

import {createStore} from "redux" 
 const store = createStore(reducer, middleware);  

Despite what was said above, the initial state, the second parameter, is not necessary. Instead, middleware, the third argument, is provided to the createStore method. The createStore function recognizes the provided argument as a middleware based on the type of arguments because it has the particular syntax of nested functions.

The middleware is executed prior to the action being sent to the store. The reducer will also be executed because we are sending the action when we call the next(action) function inside the myLoggerMiddleware, which causes the store to change. If we do not run the next(action) function inside the loggerMiddleware, the action will not be transmitted to the reducer, and the store will not be updated.

This is a frequently asked question in React Redux interview questions for experienced.  

In a Redux project, we are often utilizing multiple middleware functions. With no requirement for prior or subsequent knowledge, many middlewares can be combined to create new functionality.

Every action that we dispatch will call all the middlewares one after the other in the prescribed order. These middlewares can be used in a series of tasks and achieve a new functionality as a whole.

We use the applyMiddleware() function from Redux library, and pass all the middlewares as parameters. This returns an enhancer array that can be passed to createStore method to create a Redux Store with additional functionalities.

import { applyMiddleware } from "redux"; 
const middleware = applyMiddleware(myCustomMiddleware, reduxThunk, logger); 
const store = createStore(reducer, middleware); 

All the middleware passed in the applyMiddleware function gets executed one after the other according to their order as the arguments.

Expect to come across this popular question in Redux MCQ questions.  

It is quite simple to connect a React application to a Redux store thanks to the React Redux package, which offers React bindings for the Redux state container. As a result, you may divide your React application's components into presentational and container components according to how they interact with the Redux store.

Presentational components are exclusively focused on the view of the component and are kept unaware of the Redux state. These Presentational components receive their data and callback functions as props. Whereas the container components are in charge of how things operate and are fully aware of the Redux state. They are generally created using React Redux and can dispatch actions to the Redux store. Moreover, the container components also subscribe to Redux state updates.

The React Redux connect() API is used to generate the container component, which connects these components to the Redux store. You can turn a React component into a container component by enclosing it in a higher-order component produced by connect(). This can be used to retrieve data from the Redux store, execute commands on the Redux store, or both.

It's no surprise that this one pops up often in React Redux interview questions.  

You may design the container component and manually connect it to the Redux store using store.subscribe(). Albeit, React Redux provides connect()() method with additional advantages to create container components in React, such as:

  • There are some performance enhancements and optimizations provided by the connect() method that you might not be able to create manually. 
  • Another benefit of connect() is decoupling of your React components from Redux and the resultant cleaner code base. 
  • Furthermore, connect() offers more flexibility by enabling you to set container components to accept dynamic props based on the props that were first supplied to them. This is helpful for binding action creators to a specific variable from props or choosing a slice of the Redux state based on props. 
  • It is recommended to only utilize one Redux store per application, however if your project calls for several Redux stores, connect() enables you to quickly indicate which store a container component should be connected to.

In Redux architecture it is recommended to only utilize one store per application. Updates in Redux are kept modular by using a number of reducers that are further divided into a reducer tree. If we choose several stores before adequately understanding reducer composition, our Redux application may lose out on many advantages that come with using a single store, such as: 

  • By explicitly constructing a reducer that calls other reducers with additional information and in a certain order, reducer composition makes it simple to implement "dependent updates" similar to waitFor in Flux. 
  • It is relatively simple to persist, hydrate, and read the state when only one store is used. There is only one data storage that needs to be filled and rehydrated on the client, and JSON may describe its contents without caring about the store's ID or name, making server rendering and data prefetching simple. 
  • Time travel functionalities in Redux DevTools are greatly facilitated by a single store. Furthermore, because they work at the reducer level, community extensions like redux-undo and redux-optimist are simple to employ. 
  • Most important, Redux eliminates the need for multiple stores (except for performance edge cases which you are supposed to profile first anyway).

A common question in React Redux questions, don't miss this one.  

To create a React Redux container component, any component can be wrapped using the higher order component, which is returned by invoking the connect() function.

React Redux's connect() function accepts up to four optional arguments,

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])  

When the container component subscribes to Redux store updates by passing the mapStateToProps function as an argument to the connect function, the mapStateToProps function will be called each time the store updates.

The mapStateToProps is declared with two parameters, it either returns a simple object or another function. Here, the first parameter is the Redux store's current state and the second (an optional parameter) is an object of the props passed to the base component (also called container component). 

const mapStateToProps = function(state) { 
  return { 
    profile: state.user.profile, 
    loggedIn: state.auth.loggedIn 
  } 
} 
export default connect(mapStateToProps)(ProfileComponent); 

If a simple object is returned from mapStateToProps, the returned stateProps object is combined with the component's props, which can be used as normal component props in React.

function ProfileComponent(props) { 
  return ( 
    props.loggedIn 
      ? <Profile profile={props.profile} /> 
      : <div>Please login to view profile.</div> 
  ) 
}  

To the contrary, if a function is returned, it is utilized as mapStateToProps for every instance of the component. This has the potential to help with memoization and rendering efficiency.

To dispatch actions from component to the store we can use the mapDispatchToProps. This way, your components never directly contact the store while using React Redux; connect function takes care of it. 

If supplied, the mergeProps specifies how your own wrapped component's final props are determined. Your wrapped component will automatically receive { ...ownProps, ...stateProps, ...dispatchProps }  if you do not provide mergeProps. 

Both Redux and Relay are the implementations of the Flux architecture for state management of applications. They have many similarities and some differences, such as:

Redux
Relay

Redux is a universal JavaScript library that supports managing application state, and it can be used with any framework and is not dependent on React. 

A tool developed by Meta (earlier name Facebook) for react and their internal optimization. 

 Redux has a single store where the application state is kept. 

Redux and relay both use a single store, which makes them similar. 

 Each component can access the state and update it by dispatching actions. 

Relay solely manages state that came from the server, and all access to the state is done through GraphQL queries (for reading data) and mutations (for changing data).  

Redux does not automatically manage data fetching, but it is possible to do so manually by creating an action that collects data from the server and inserts it into the store. 

Relay caches the data and streamlines data retrieval for you 

Although both Redux and RxJS are universal libraries for state control in JavaScript. There are very different in terms of their principal and their uses. However, we can observe some similarities between RxJS and Redux 

A. RxJS:  

RxJS is a reactive programming library, it provides fundamental building pieces called Observables that you can use to implement the "observing from a distance" pattern. Consider using RxJS as an alternative to Promises when doing asynchronous activities in JavaScript.

RxJs is an independent javascript library that can be used with any javascript framework, or Vanilla Javascript.

B. Redux: 

Because the Store is reactive, Redux only utilizes a little bit Reactive paradigm in its architecture. Redux does not have a store.set() method since its content cannot be changed remotely. The Store alters itself from a distance when it observes action objects distance whens change events. Additionally, the Store permits remote observers to view its data.

Redux is also an independent JavaScript library it is often compatible with React but can be used with any JavaScript framework (Angular, Vue, etc.).

The frontend and backend of modern web apps are separated. Thus, to fetch data and show to the view, we have to make AJAX calls to a server and retrieve data from a remote endpoint.

For Instance, let’s create a songReducer for our Redux app,

const songsReducer = (state = {}, action) => { 
  switch (action.type) { 
    case 'GET_SONGS': 
      return action.data; 
    default: 
      return state; 
  } 
}; 
export default songsReducer;  

In the above section of the code, Redux action is being created based on a switch case condition. In our example, data will only be provided to the state if action.type is "GET SONGS"; otherwise, an empty state is returned.

Next, let us create an API call (i.e. AJAX request), getSongs() which dispatch the action creator function getSongsAction() when the fetch call is successful.

export const getSongs() { 
  return fetch('/api/data') 
    .then(response => response.json()) 
    .then(json => dispatch(getSongsAction(json))) 
} 
 
export const getSongsAction(data) { 
  return { 
    type'GET_SONGS', 
    data: data 
  } 
}

This AJAX request is intercepted by the songReducer inside the store, which then evaluates the action objects and update necessary changes to the state.

Resetting a Redux state to its initial state is something that lots of apps need to do. A common scenario where the app state needs to be reset is after the user logs out.

To reuse the logic for resetting the app in a single place, the trick is to create a root reducer over your app's root reducer. This way you are creating a root reducer in your application that assigns the task of handling an action to the reducer created by combineReducers().

Let us take rootReducer as an instance; it should return the starting state following the USER_SINGOUT event. Regardless of the operation, reducers are known to always return the initial state when called with undefined as the first argument.

const appReducer = combineReducers({ 
  /* your app's top-level reducers */ 
}) 
 
const rootReducer = (state, action) => { 
  if (action.type === 'USER_SIGNOUT') { 
    state = undefined 
  } 
 
  return appReducer(state, action) 
}  

You might also need to clean your storage if you are using redux-persist, which keeps a copy of your state in a storage engine. Before setting the state to undefined and cleaning out each storage state key, you must first import the necessary storage engine.

const appReducer = combineReducers({ 
  /* your app's top-level reducers */ 
}) 
 
const rootReducer = (state, action) => { 
  if (action.type === 'USER_SIGNOUT') { 
    Object.keys(state).forEach(key => { 
                storage.removeItem(`persist:${key}`); 
            }); 
            state = undefined; 
  } 
 
  return appReducer(state, action) 
}  

In Redux architecture it is recommended to only utilize one store per application; that said, you will run into issues if you try to have more than one store in your Redux application.

The most frequent issue is that it is difficult to subscribe to the store of top-level providers if we wrap a component in a provider and then wrap a child component in another provider.

const Component1 = () => { 
  return ( 
    <Provider store={store1}> 
      <Provider store={store2}> 
        <Component2 /> 
      </Provider> 
    </Provider> 
  ); 
};  

Let us create context for each store, we can use ReactReduxContext to make one of the stores default by importing it from react-redux.

const store1Context = React.createContext(); 
const store2Context = React.createContext(); 
 
<Provider store={store1} context={store1Context}> 
  <Provider store={store2} context={store2Context}> 
    <App/> 
  </Provider> 
</Provider>  

Additionally, we must develop our own custom selector and custom dispatch hooks. The store with default context (if any), will be used if you use the useSelector, and useDispatch default hooks.

export const useStore1Dispatch = createDispatchHook(store1Context); 
export const useStore1Selector = createSelectorHook(store1Context); 
export const useStore2Dispatch = createDispatchHook(store2Context); 
export const useStore2Selector = createSelectorHook(store2Context);  

The rest of the application can be developed normally, and we can use these custom selector and custom dispatch hooks to use the preferred store in any of the components throughout the application.

Additionally, to create a container component with Higher order component pattern, we can utilize connect() API from react-redux library.

One of the most frequently posed Redux interview questions for experienced, be ready for it.  

Immer is a small package that makes it easier to work with immutable states. Immer can be utilized in any situation that calls for the use of immutable data structures, such as it can be used with React state or React or Redux in conjunction or with configuration management.

Immutable data structures make it possible to identify changes (effectively): if the object's reference did not change, then the object did not change itself. Additionally, it lowers the cost of cloning: Unchanged sections of a data tree can be shared in memory with older iterations of the same state without needing to be replicated.

These advantages may usually be obtained by making sure that you always make an edited duplicate of an object, array, or map rather than changing any of its properties. In actuality, this can make writing code rather difficult because it is simple to violate those restrictions unintentionally.

For JavaScript applications, Webpack is a static module bundler that transforms all of your application's code into a web browser-friendly version. The JavaScript code for your app, node modules, pictures, and CSS styles are all combined to create reusable pieces of code called modules. These modules are packaged to make it simple to utilize them on your website. With this modular separation of responsibilities, Webpack isolates the code according to how it is utilized in your app, making it much simpler to manage, debug, verify, and test your code.

When Webpack analyses your application, it creates a dependency graph outlining the modules your project requires and produces one or more bundles. A bundle is a unique collection of linked code that has been produced and modified for the browser. Webpack recognizes a dependency when one file uses the code from another file, or when one file depends on another. 

A good Redux side-effects middleware should adhere to the following features: 

  • When we need to base certain decisions on the current state of our application, having access to the Redux store is useful. 
  • Ability to dispatch a distinct action from within the middleware, which will provide us the freedom to start other side effects with various business logics. 
  • Ability to execute side effects effectively. 
  • Cancel: The ability to cancel side effects to stop an asynchronous process before it completes and has an impact on the state of our application. 
  • Enable repeated action triggers from the user (like clicking a refresh button). To eliminate extra processing and to continuously correspond with the user's activities, we want to be able to cancel all past side effects while always keeping the most recent side effect active. 
  • For each different dispatched action, execute the same particular side effect. 
  • Debounce: Delay the invocation of a side effect by debouncing it until a few milliseconds have passed since the previous invocation.  
  • Throttle: Control the frequency of your application's side effects' execution, i.e., stop our side effects from occurring more frequently than once every X milliseconds.  
  • Race: At times, we would prefer to compete with several side effects, and when one of them ends, we would like to terminate the others because they are now unnecessary. 
  • All: Execute several side effects simultaneously, watch for each one to complete, and then take another action. 

Both Flux and Redux are used to construct scalable and user-friendly applications. Their approaches to application architecture differ from one another while yet sharing certain similarities. There are four major differences between Redux and Flux: 

a. Number of stores per application 

Flux creates multiple stores to control various application components. To make any changes to the application, one must ask the dispatcher to send the prior state as a return value.

Whereas there is just one store in the Redux design that can hold data for several components. A reducer can be specified by developers to effectively manage all the data in one location.

b. Architecture 

In Flux, data is transmitted through actions as users engage with applications. Communication between actions and stores is established by the dispatcher. Action handlers for both user interface and domain states are defined in Flux's store component. Additionally, it is in charge of maintaining the visual layer of apps. When the stores acquire new information, the view component of Flux interfaces with the stores to present updates.

Redux incorporates extra elements while drawing inspiration from Flux principles. In Redux architecture, a reducer is utilized in place of the dispatcher found in Flux. The reducer uses the previous state and actions to move to the next state after receiving objects from Redux's action component. The reducer also adds fresh data to the store. Following the creation of a new state, the store sends an update to view.

c. Business Logic 

Business logic is a set of rules that controls the information flow between the user interface and database for any application or software program. In Flux, according to the data provided by actions, stores manage business logic. In contrast, the reducer in the Redux design handles business logic after receiving data from the action component. 

d. Debugging 

Flux uses a Dispatcher to enable quick debugging. No cascading updates occur, and a known state makes it simple to identify the root cause.

The Redux makes debugging relatively straightforward using Reducers. There is a single object tree that reflects all of the prior changes.

Typically, programmers divide their apps into a number of pages that may be accessed from several URLs. Each of these pages transforms into a different entry point to access the application. 

Code-splitting is the technique of dividing the application bundle into more manageable pieces needed by each entry point. By only loading the code necessary to run that page, the application's initial load time will be improved. There may be contributing factors involved, such as better application caching or code encapsulation to hide the information not necessary to be displayed for the users.

The secret to code splitting is determining which areas of a page require certain JavaScript dependencies. You can intentionally exclude some dependencies from bundles and then only add them where they are required by using code splitting. This implies that they are also not loaded until they are required, which speeds up the loading of the page by only loading JavaScript when it is required.

The fact that the page is still loading the same amount of code may at first appear illogical, but the difference is that the page might not execute all of the code it loads. For instance, if a dynamic element on a page requires a large dependency, such dependency can result in a noticeable page lag. However, the dependency can be so minor that there is no lag created if it is only loaded when the dynamic element is used. 

You do not have to utilize middleware for async flow in Redux, middleware is only providing the "syntax sugar" and maintains the separation of concern.

Using middleware like Redux Thunk or Redux Promise has the advantage of shielding components from the implementation details of action creators. A component does not know whether the actions are synchronous or asynchronous, whether they are concerned with the Redux state, and whether they call other action creators. Although there is some indirection on the downside, it is worth it in practical situations.

For example: without using middleware we can pass the dispatcher as a parameter to the action creator function.

// action creator 
function loadData(dispatch, userId) { 
  return fetch(`http://someApi.com/${userId}`) 
    .then(res => res.json()) 
    .then( 
      data => dispatch({ type'LOAD_DATA_SUCCESS', data }), 
      err => dispatch({ type'LOAD_DATA_FAILURE', err }) 
    ); 
} 
 
// component 
componentWillMount() { 
  loadData(this.props.dispatch, this.props.userId);}

Whereas if we use middleware like Redux Thunk, there is no need to pass the dispatcher to the action creator

// action creator 
function loadData(userId) { 
  return dispatch => fetch(`http://data.com/${userId}`) 
    .then(res => res.json()) 
    .then( 
      data => dispatch({ type'LOAD_DATA_SUCCESS', data }), 
      err => dispatch({ type'LOAD_DATA_FAILURE', err }) 
    ); 
} 
 
// component 
componentWillMount() { 
  this.props.dispatch(loadData(this.props.userId));} 

If we use middleware in our project, every action that we dispatch will call a simple JavaScript function called a middleware. A middleware has the ability to manipulate an action in any way inside of that function, including stopping it from being sent to any of the reducers and changing it. For instance, we could design a middleware that console logs each action you execute.

Redux code splitting requires the ability to dynamically add reducers to the store, however, for Redux there can be only one root reducer function. Typically, when the application is first initialized, combineReducers() or a comparable function is called to create the root reducer. To dynamically add new reducers, we must use the combineReducers function once more to re-generate the root reducer. 

There are multiple approaches to achieve the general re-initialization of the root reducer to handle code splitting. One of the approaches from them is to call a reusable store.injectReducer() function. But first, we must define it inside our configureStore.

Let us create a createReducer function that takes all the new reducers as an array returns the root reducer by passing the new reducers along with all the static reducers to the combineReducer method of react-redux.

// reducers.js 
import { combineReducers } from 'redux'; 
import students from './reducers/students'; 
import teachers from './reducers/teachers'; 
 
export default function createReducer(asyncReducers) { 
  return combineReducers({ 
    students, 
    teachers, 
    ...asyncReducers 
  }); 
}

Next, we will define a configureStore function that accepts the initialState and returns the Redux Store by calling createReducer function (from reducers.js) as rootReducer to createStore.

Additionally, we will also define an injectAsyncReducer function that adds a new reduces to the asyncReducers object in our Store and replace the current root-reducer with a new updated root-reducer. 

// store.js 
import { createStore } from 'redux'; 
import createReducer from './reducers'; 
export default function configureStore(initialState) { 
  const store = createStore(createReducer(), initialState); 
  store.asyncReducers = {}; 
  return store; 
} 
export function injectAsyncReducer(store, name, asyncReducer) { 
  store.asyncReducers[name] = asyncReducer; 
  store.replaceReducer(createReducer(store.asyncReducers)); 
}  

The injectAsyncReducer can be called from anywhere in our React Redux application to add new reducers dynamically to the redux store. For instance, in the code below we are dynamically adding examScores reducer for the StudentInfo page.

// routes.js 
import { injectAsyncReducer } from './store'; 
function createRoutes(store) { 
  // ...  
  const StudentInfoRoute = { 
    // ... 
    getComponents(location, callback) { 
      require.ensure([ 
        './pages/StudentInfo', 
        './reducers/examScores' 
      ], function (require) { 
        const StudentInfo = require('./pages/StudentInfo').default; 
        const examScoresReducer = require('./reducers/examScores').default; 
 
        injectAsyncReducer(store, 'examScores', examScoresReducer); 
        callback(null, StudentInfo); 
      }) 
    } 
  }; 
  // ... 
}