Hone logo
Hone
Problems

Building a Custom Redux Store Enhancer in React with TypeScript

This challenge focuses on understanding and implementing Redux store enhancers. Store enhancers are a powerful feature in Redux that allow you to customize the store's functionality. You'll create a custom enhancer that adds logging capabilities to your Redux store, demonstrating how to intercept actions and state changes.

Problem Description

Your task is to create a custom Redux store enhancer in TypeScript. This enhancer should log every action dispatched to the store and the resulting state change.

Key Requirements:

  1. Create a TypeScript function createLoggerEnhancer: This function will act as your store enhancer.
  2. The enhancer should accept a loggerOptions object: This object might contain configuration for the logger (e.g., to enable/disable logging, specify log level, etc. - for this challenge, assume it's just a placeholder or can be used for future extensions).
  3. The enhancer should wrap the createStore function: It needs to intercept dispatch and getState calls.
  4. Log Actions: When any action is dispatched, log the action type and its payload (if any) to the console.
  5. Log State Changes: After each action is dispatched and the reducer has updated the state, log the old state and the new state to the console.
  6. Return the Enhanced Store: The enhancer should return a new store object with the enhanced functionality.

Expected Behavior:

When you apply your createLoggerEnhancer to a Redux store, every action dispatched will be visible in your browser's developer console, along with the state before and after the action was processed.

Edge Cases:

  • Initial state: The enhancer should gracefully handle the initial state setup.
  • No actions dispatched: The enhancer should not cause any errors if no actions are dispatched.

Examples

Let's consider a simple Redux store setup.

Example 1: Basic Setup and Action Dispatch

Initial Store Setup:

// Assume a simple reducer and initial state
interface AppState {
  count: number;
}

const initialState: AppState = { count: 0 };

function counterReducer(state: AppState = initialState, action: { type: string; payload?: any }): AppState {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}

// Assume createStore is imported from 'redux'
// import { createStore } from 'redux';

// Your custom enhancer will be applied here
// const store = createStore(counterReducer, applyMiddleware(...), /* enhancers */);
// For this example, we'll simulate applying it manually to show the logs.

Applying the createLoggerEnhancer and Dispatching an Action:

// Hypothetical output if createLoggerEnhancer is applied and 'INCREMENT' is dispatched:

// Console Output:
// DISPATCHED ACTION: { type: 'INCREMENT' }
// OLD STATE: { count: 0 }
// NEW STATE: { count: 1 }

// After dispatching 'INCREMENT', the store's state would be { count: 1 }.

Example 2: Dispatching Multiple Actions

Applying the createLoggerEnhancer and Dispatching Multiple Actions:

// Hypothetical output if createLoggerEnhancer is applied and 'INCREMENT' then 'DECREMENT' are dispatched:

// Console Output:
// DISPATCHED ACTION: { type: 'INCREMENT' }
// OLD STATE: { count: 0 }
// NEW STATE: { count: 1 }

// DISPATCHED ACTION: { type: 'DECREMENT' }
// OLD STATE: { count: 1 }
// NEW STATE: { count: 0 }

// After dispatching 'INCREMENT' and then 'DECREMENT', the store's state would be { count: 0 }.

Constraints

  • Your createLoggerEnhancer function must be a pure function that takes createStore as an argument and returns a new createStore function.
  • The logging should be done using console.log().
  • The solution must be written in TypeScript.
  • You should not use any external Redux middleware libraries for the core functionality of this challenge (you can use applyMiddleware conceptually if needed for how enhancers are typically applied, but the logging logic itself must be custom).

Notes

  • A store enhancer is a higher-order function that wraps the store's dispatch and getState methods.
  • The signature of a store enhancer is typically (next: StoreEnhancerStoreCreator) => (reducer: Reducer, preloadedState: any, enhancer: StoreEnhancer) => Store. However, for simpler enhancers that just modify dispatch, you can often think of it as a function that takes your original createStore and returns a new one that calls the original createStore with its own modified dispatch and getState.
  • The Redux createStore function signature is createStore(reducer, [preloadedState], [enhancer]). When you apply enhancers, they are applied in order.
  • Consider the flow: action is dispatched -> enhancer intercepts -> logs action -> calls original dispatch -> reducer runs -> state updates -> enhancer intercepts again -> logs old and new state -> returns action result.
  • You'll need to simulate the createStore and Store types from Redux for your TypeScript project. You can define these interfaces yourself for the purpose of this challenge.
Loading editor...
typescript