Hone logo
Hone
Problems

Implementing a Feature Toggle Reducer in Angular

This challenge focuses on implementing a Redux-style reducer pattern in Angular to manage the state of application feature toggles. Effectively managing feature toggles is crucial for enabling or disabling specific functionalities without redeploying your application. You will create a reducer that handles actions to enable, disable, and reset feature toggles.

Problem Description

You need to build a reducer function in TypeScript that manages a state object representing feature toggles. This state should be a map where keys are feature names (strings) and values are booleans indicating whether the feature is enabled or disabled. The reducer should respond to three distinct action types:

  1. ENABLE_FEATURE: This action should take a feature name and enable it. If the feature doesn't exist, it should be added and enabled.
  2. DISABLE_FEATURE: This action should take a feature name and disable it. If the feature doesn't exist, it should be added and disabled.
  3. RESET_FEATURES: This action should reset all feature toggles to their initial state (an empty map).

The reducer should adhere to the principle of immutability, meaning it should always return a new state object rather than modifying the existing one.

Key Requirements:

  • A TypeScript reducer function featureToggleReducer that accepts the current state and an action.
  • The state should be an object of type { [featureName: string]: boolean }.
  • The reducer must handle ENABLE_FEATURE, DISABLE_FEATURE, and RESET_FEATURES action types.
  • Actions for enabling/disabling features should accept a payload which is the featureName.
  • The reducer must be purely functional and immutable.

Expected Behavior:

  • When an ENABLE_FEATURE action is dispatched with "newFeature", the state should update to include "newFeature": true.
  • When a DISABLE_FEATURE action is dispatched with "existingFeature", the state should update to "existingFeature": false.
  • When RESET_FEATURES is dispatched, the state should become an empty object {}.

Edge Cases:

  • Dispatching an ENABLE_FEATURE for a feature that already exists and is enabled should result in no change to that specific feature's state.
  • Dispatching a DISABLE_FEATURE for a feature that already exists and is disabled should result in no change to that specific feature's state.
  • Handling unknown action types by returning the current state.

Examples

Example 1:

// Initial State
const initialState = {};

// Action
const action1 = { type: 'ENABLE_FEATURE', payload: 'darkMode' };

// Dispatching
const newState1 = featureToggleReducer(initialState, action1);

Output:

{
  "darkMode": true
}

Explanation: The darkMode feature was not present in the initial state, so it was added and set to true.

Example 2:

// Initial State
const currentState = {
  darkMode: true,
  notifications: false
};

// Action
const action2 = { type: 'DISABLE_FEATURE', payload: 'darkMode' };

// Dispatching
const newState2 = featureToggleReducer(currentState, action2);

Output:

{
  "darkMode": false,
  "notifications": false
}

Explanation: The darkMode feature was updated to false. The notifications feature remains unchanged.

Example 3:

// Initial State
const currentState = {
  darkMode: true,
  notifications: false
};

// Action
const action3 = { type: 'RESET_FEATURES' };

// Dispatching
const newState3 = featureToggleReducer(currentState, action3);

Output:

{}

Explanation: The RESET_FEATURES action clears all feature toggles, returning an empty object.

Example 4: (Edge Case)

// Initial State
const currentState = {
  darkMode: true
};

// Action
const action4 = { type: 'ENABLE_FEATURE', payload: 'darkMode' }; // Already enabled

// Dispatching
const newState4 = featureToggleReducer(currentState, action4);

Output:

{
  "darkMode": true
}

Explanation: Attempting to enable a feature that is already enabled results in no change to the state.

Constraints

  • The reducer function should be written in TypeScript.
  • State and action objects should conform to well-defined interfaces.
  • The solution should not rely on external state management libraries like NgRx, but rather implement the reducer pattern from scratch for educational purposes.
  • The reducer must be performant, especially when dealing with a large number of feature toggles.

Notes

  • Consider defining interfaces for your actions and state to ensure type safety.
  • Think about how you will handle the initial state of the reducer.
  • The goal is to understand the core principles of reducers and immutability. You can leverage the spread operator (...) for creating new state objects.
Loading editor...
typescript