Swiftorial Logo
Home
Swift Lessons
Tutorials
Learn More
Career
Resources

React FAQ: Top 75 Questions

9. What is Redux and why is it used with React?

Redux is an open-source JavaScript library for managing and centralizing application state. It serves as a predictable state container for JavaScript applications. While it can be used with any JavaScript framework or library, it is most commonly associated with React due to React's component-based architecture and unidirectional data flow, which can lead to complex state management challenges in larger applications.

Why is Redux used with React (especially in large applications)?

  • Centralized State: In a large React application, state can be scattered across many components, leading to prop-drilling (passing props down many levels) or complex parent-child communication. Redux provides a single, centralized "store" for the entire application's state, making it easier to manage and debug.
  • Predictable State Changes: Redux follows a strict unidirectional data flow and three core principles that ensure state changes are predictable and traceable:
    1. Single Source of Truth: The state of your whole application is stored in an object tree within a single store.
    2. State is Read-Only: The only way to change the state is to emit an action, an object describing what happened.
    3. Changes are Made with Pure Functions (Reducers): To specify how the state tree is transformed by actions, you write pure reducers.
  • Easier Debugging: Because all state changes go through a predictable flow of actions and reducers, debugging becomes much simpler. Tools like Redux DevTools allow you to "time-travel" through state changes, inspect actions, and understand how the state evolved.
  • Performance Optimization: With careful implementation (e.g., using `reselect` for memoized selectors), Redux can help optimize performance by preventing unnecessary re-renders of components that don't depend on changed state.
  • Scalability: Redux provides a structured and scalable way to manage state as the application grows in size and complexity, making it easier for large teams to collaborate.
  • Server-Side Rendering (SSR): Redux makes it straightforward to initialize the application state on the server and pass it to the client, facilitating server-side rendering.

It's important to note that for small to medium-sized React applications, built-in React features like `useState`, `useContext`, and `useReducer` might be sufficient for state management, making Redux an optional choice depending on the application's needs.


// 1. Redux Store Setup (store.js)
import { createStore } from 'redux';

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

const store = createStore(counterReducer);

export default store;

// 2. React Component (CounterComponent.js)
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'; // Hooks for React-Redux

function CounterComponent() {
  // useSelector to get state from the Redux store
  const count = useSelector(state => state.count);

  // useDispatch to dispatch actions
  const dispatch = useDispatch();

  return (
    <div>
      <h3>Redux Counter</h3>
      <p>Count: {count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>
        Decrement
      </button>
    </div>
  );
}

export default CounterComponent;

// 3. Application Root (index.js or App.js)
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux'; // Provider to connect React to Redux
import store from './store'; // Import the Redux store
import CounterComponent from './CounterComponent';

function App() {
  return (
    // <Provider> makes the Redux store available to any nested components
    <Provider store={store}>
      <CounterComponent />
    </Provider>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root-redux'));
root.render(<App />);
                    

Explanation of the Code:

  • `store.js`:
    • Defines `counterReducer`, a pure function that takes the current `state` and an `action` and returns a new `state`. It handles `INCREMENT` and `DECREMENT` actions.
    • `createStore(counterReducer)` creates the Redux store, holding the application's state.
  • `CounterComponent.js`:
    • `useSelector` Hook: Used to extract data from the Redux store's state. Here, it gets the `count`.
    • `useDispatch` Hook: Used to get the `dispatch` function, which is how you send actions to the Redux store to trigger state changes.
    • When buttons are clicked, actions `{ type: 'INCREMENT' }` or `{ type: 'DECREMENT' }` are dispatched. These actions are processed by `counterReducer`, updating the state in the store.
  • `index.js` (or `App.js`):
    • The `<Provider>` component from `react-redux` wraps the root of your React application. It takes the Redux `store` as a prop, making the store accessible to all nested components without explicitly passing it down as props.

This example demonstrates Redux's core flow: a single store, actions describing events, and reducers handling state updates predictably. React components connect to this store using `react-redux` hooks to read state and dispatch actions.