Hone logo
Hone
Problems

Minimalist Redux Implementation in React

This challenge asks you to build a simplified version of Redux from scratch, focusing on the core concepts of a store, actions, and reducers. Implementing Redux yourself provides a deep understanding of its inner workings and how state management can be effectively handled in React applications. This exercise will solidify your understanding of unidirectional data flow and immutability.

Problem Description

You are tasked with creating a basic Redux implementation consisting of a Store, an Action, and a Reducer. The Store will hold the application's state, dispatch actions, and notify subscribers of state changes. Actions are plain JavaScript objects that describe an event that occurred. Reducers are pure functions that take the current state and an action and return a new state.

Key Requirements:

  • Store:
    • Should accept a reducer function during initialization.
    • Should have a dispatch method that accepts an action and calls the reducer.
    • Should have a getState method that returns the current state.
    • Should have a subscribe method that accepts a callback function. This callback should be invoked whenever the state changes. The callback should receive the new state as an argument.
  • Action: A plain JavaScript object with a type property (required) and an optional payload property.
  • Reducer: A pure function that takes the current state and an action as arguments and returns a new state. It should handle different action types and update the state accordingly.

Expected Behavior:

  1. Creating a store with a reducer should initialize the state to the initial state returned by the reducer when called with undefined as the initial state.
  2. Dispatching an action should call the reducer with the current state and the action.
  3. The reducer should return a new state based on the action type.
  4. Subscribers should be notified of state changes after each dispatch.
  5. Multiple subscriptions should be handled correctly.
  6. Unsubscribing should prevent further notifications.

Edge Cases to Consider:

  • What happens if the reducer doesn't handle a specific action type? (Should it return the current state unchanged).
  • How to handle multiple subscribers?
  • How to unsubscribe from the store?
  • Ensure immutability of the state. The reducer must return a new state object, not modify the existing one.

Examples

Example 1:

Input:
Store Initialization:
  const store = new Store( (state = 0, action) => {
    switch (action.type) {
      case 'INCREMENT':
        return state + 1;
      case 'DECREMENT':
        return state - 1;
      default:
        return state;
    }
  });

Subscription:
  store.subscribe( (newState) => console.log("State changed:", newState));

Dispatch Actions:
  store.dispatch({ type: 'INCREMENT' });
  store.dispatch({ type: 'INCREMENT' });
  store.dispatch({ type: 'DECREMENT' });

Output:
State changed: 1
State changed: 2
State changed: 1

Example 2:

Input:
Store Initialization:
  const store = new Store( (state = { count: 0 }, action) => {
    switch (action.type) {
      case 'ADD_COUNT':
        return { ...state, count: state.count + action.payload };
      default:
        return state;
    }
  });

Dispatch Actions:
  store.dispatch({ type: 'ADD_COUNT', payload: 5 });
  store.dispatch({ type: 'ADD_COUNT', payload: 2 });

Output:
(Console logs within subscribers would show the state changes)

Example 3: (Unsubscribe)

Input:
Store Initialization:
  const store = new Store( (state = 0, action) => state + action.payload);

Subscription:
  const unsubscribe = store.subscribe( (newState) => console.log("State changed:", newState));

Dispatch Actions:
  store.dispatch({ type: 'ADD_VALUE', payload: 10 });

Unsubscribe:
  unsubscribe();

Dispatch Actions:
  store.dispatch({ type: 'ADD_VALUE', payload: 5 });

Output:
State changed: 10
(No further console logs because of unsubscribe)

Constraints

  • The Store should maintain a single state object.
  • The reducer must be a pure function.
  • The subscribe method should allow for multiple subscriptions.
  • The unsubscribe method should correctly remove a subscription.
  • The implementation should be reasonably efficient for a basic Redux implementation. Avoid unnecessary overhead.
  • The code must be written in TypeScript.

Notes

  • Focus on the core functionality of Redux. You don't need to implement middleware, combineReducers, or other advanced features.
  • Think about how to handle state immutability. Returning a new state object is crucial.
  • Consider using closures to maintain the state within the Store class.
  • Test your implementation thoroughly with different action types and state updates.
  • This is a simplified implementation. Real-world Redux has more features and optimizations. The goal here is to understand the fundamental principles.
Loading editor...
typescript