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:
- ENABLE_FEATURE: This action should take a feature name and enable it. If the feature doesn't exist, it should be added and enabled.
- DISABLE_FEATURE: This action should take a feature name and disable it. If the feature doesn't exist, it should be added and disabled.
- 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
featureToggleReducerthat 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, andRESET_FEATURESaction types. - Actions for enabling/disabling features should accept a
payloadwhich is thefeatureName. - The reducer must be purely functional and immutable.
Expected Behavior:
- When an
ENABLE_FEATUREaction is dispatched with"newFeature", the state should update to include"newFeature": true. - When a
DISABLE_FEATUREaction is dispatched with"existingFeature", the state should update to"existingFeature": false. - When
RESET_FEATURESis dispatched, the state should become an empty object{}.
Edge Cases:
- Dispatching an
ENABLE_FEATUREfor a feature that already exists and is enabled should result in no change to that specific feature's state. - Dispatching a
DISABLE_FEATUREfor 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.