Published on

Redux: State Management in React

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

1. What is Redux?

Redux is one of the most popular libraries for state management in React applications. It helps manage the state in a predictable way, ensuring that the data flows in a consistent manner across the application.

In Redux, state is handled globally and allows you to change the state from anywhere in the application instead of creating new objects every time state is changed.

2. Key Concepts in Redux

The lifecycle in Redux: ActionDispatches ActionReducers Update StateRe-renders ComponentsComponents Update PropsDisplay Updated UI

  1. Action

    An Action describes what happens in your application. It’s an object that holds the information of the user’s event, and it communicates a change in the state. Actions are dispatched from any part of your app and are received by the store. They are plain JavaScript objects with a type property, which describes the action that occurred, but not how the state will change.

  2. Store

    The Store is the single, authoritative source of state in your application. There is only one store per Redux application, and it handles both dispatching actions and receiving updates to state.

  3. Reducers

    Reducers are pure functions responsible for specifying how the state changes in response to an action. When an action is dispatched, the store sends the action to the appropriate reducer. The reducer uses the current state and action to return a new state.

3. Async with Redux

import React, { useReducer } from 'react';
import ReactDOM from 'react-dom';

const initialState = {
  first_name: "John",
  last_name: "Smith",
  department: "operations"
};

function myReducer(state, action) {
  switch (action.type) {
    case 'changeDepartmentToIT':
      return {
        ...state,
        department: 'IT'
      };
    case 'changeDepartmentToSecurity':
      return {
        ...state,
        department: 'Security'
      };
    default:
      throw new Error();
  }
}

function ChangeUserDepartment() {
  const [state, dispatch] = useReducer(myReducer, initialState);
  
  return (
    <div>
      <p>First Name: {state.first_name}</p>
      <p>Last Name: {state.last_name}</p>
      <p>User Department: {state.department}</p>
      <button onClick={() => dispatch({type: 'changeDepartmentToIT'})}>Change User Department to IT</button>
      <button onClick={() => dispatch({type: 'changeDepartmentToSecurity'})}>Change User Department to Security</button>
    </div>
  );
}

4. Using Redux in Your Application

1. Install Redux and React-Redux

To start using Redux, you'll need to install both redux and react-redux (which binds Redux to React components):

pnpm install redux react-redux

2. Define Your Redux Store

  1. Create Action Types

    // actionTypes.ts
    export const INCREMENT = 'INCREMENT';
    export const DECREMENT = 'DECREMENT';
    
  2. Create Actions

    // actions.ts
    import { INCREMENT, DECREMENT } from './actionTypes';
    
    export const increment = () => ({
      type: INCREMENT,
    });
    
    export const decrement = () => ({
      type: DECREMENT,
    });
    
  3. Create Reducers

    Reducers define how the state should change in response to actions. Here, we’ll have a simple reducer for the counter state:

    // counterReducer.ts
    import { INCREMENT, DECREMENT } from './actionTypes';
    
    const initialState = {
      count: 0,
    };
    
    export const counterReducer = (state = initialState, action: any) => {
      switch (action.type) {
        case INCREMENT:
          return { ...state, count: state.count + 1 };
        case DECREMENT:
          return { ...state, count: state.count - 1 };
        default:
          return state;
      }
    };
    
  4. Combine Reducers

    If your application has multiple reducers, you can combine them into one using combineReducers:

    // rootReducer.ts
    import { combineReducers } from 'redux';
    import { counterReducer } from './counterReducer';
    
    export const rootReducer = combineReducers({
      counter: counterReducer,
    });
    

3. Create and Configure the Store

Now, let’s create the Redux store and pass in the rootReducer:

// store.ts
import { createStore } from 'redux';
import { rootReducer } from './rootReducer';

export const store = createStore(rootReducer);

4. Provide the Store to Your Application

In order to use Redux in your React components, you need to wrap your application with the Provider from react-redux. This makes the Redux store available to all components in the app.

// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

5. Connect Redux State and Actions to Your Components

Now, let’s connect the Redux state and actions to a React component. For example, a Counter component:

// Counter.tsx
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement } from './actions';

export const Counter = () => {
  const dispatch = useDispatch();
  const count = useSelector((state: any) => state.counter.count);

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
    </div>
  );
};
  • In this component:

    • We use useSelector to access the count state from the Redux store.
    • We use useDispatch to dispatch actions when the user clicks the buttons.