DEV Community

ZeeshanAli-0704
ZeeshanAli-0704

Posted on

Polyfil - useReducer

Below, I'll provide a simple, executable useReducer polyfill with detailed comments, an explanation, expected output, and key interview talking points, mirroring the structure and simplicity of the useState example.

Simple Working JavaScript Code for useReducer Polyfill

// Simple useReducer polyfill for interview explanation
function createUseReducer() {
  // Array to store state values across multiple "renders" of the component.
  // This simulates React's internal state storage for a component.
  let stateStore = [];
  // Variable to track the current index for hook calls during a single render.
  // This ensures each useReducer call maps to the same state slot every render.
  let currentIndex = 0;

  // The useReducer function, mimicking React's hook for managing complex state.
  function useReducer(reducer, initialState) {
    // Capture the current index for this specific useReducer call.
    // This index determines where in stateStore this state value lives.
    const index = currentIndex;
    // Increment currentIndex for the next useReducer/useState call in this render.
    // e.g., First call gets index 0, second gets index 1, etc.
    currentIndex++;

    // Initialize the state value at this index if it hasn't been set yet.
    // This happens during the first render or if state was cleared.
    if (stateStore[index] === undefined) {
      stateStore[index] = initialState;
    }

    // Get the current state value from stateStore at this index.
    // This is what the component will use during this render.
    const currentState = stateStore[index];

    // Define the dispatch function to update state using the reducer.
    // This mimics React's dispatch behavior to update state based on actions.
    const dispatch = function(action) {
      // Call the reducer with current state and action to get new state.
      const newState = reducer(stateStore[index], action);
      stateStore[index] = newState;
      console.log(`State updated to: ${JSON.stringify(newState)} at index ${index} with action: ${JSON.stringify(action)}`);
      // In real React, this would trigger a re-render automatically.
      // Here, we just log the update for demonstration.
    };

    // Return an array with the current state value and the dispatch function.
    // This matches React's useReducer API: [state, dispatch].
    return [currentState, dispatch];
  }

  // Function to reset the index to 0 after a render simulation.
  // This simulates the end of a render cycle, preparing for the next render.
  // In real React, hook indices reset per render to maintain call order.
  function resetIndex() {
    currentIndex = 0;
    console.log('Resetting index for next render');
  }

  // Return an object with useReducer and resetIndex functions.
  // This allows the component to use the hook and reset the index manually.
  return { useReducer, resetIndex };
}

// Create an instance of useReducer by calling createUseReducer().
// This sets up a unique state store for this simulation.
const { useReducer, resetIndex } = createUseReducer();

// Reducer function to manage state updates based on actions.
// This is a user-defined function passed to useReducer, just like in React.
function counterReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    case 'SET_NAME':
      return { ...state, name: action.payload };
    default:
      return state;
  }
}

// Simulated functional component to demonstrate useReducer usage.
// In real React, this would be a component that renders UI.
function MyComponent() {
  // Use useReducer to manage a state object with a counter and name.
  // Pass the reducer function and initial state.
  // This will map to index 0 in stateStore.
  const [state, dispatch] = useReducer(counterReducer, { count: 0, name: "Zeeshan" });

  // Log the current state values during this render.
  // This shows what the component "sees" at this moment.
  console.log('Current State - Count:', state.count, 'Name:', state.name);

  // Return the dispatch function to allow updates outside render.
  // In real React, updates might happen via events like button clicks.
  return { dispatch };
}

// Run the simulation to mimic React rendering the component multiple times.
console.log('First Call (Initial Render):');
// Call MyComponent for the first time, simulating the initial render.
// This initializes state values in stateStore.
const { dispatch } = MyComponent();
// Reset the index after the render to prepare for the next call.
// This ensures the next call to MyComponent starts at index 0 again.
resetIndex();

// Update the state using the dispatch function.
// This simulates user interaction or some event updating the state.
console.log('\nUpdating State:');
dispatch({ type: 'INCREMENT' }); // Updates state.count to 1.
dispatch({ type: 'SET_NAME', payload: 'John' }); // Updates state.name to "John".

// Run the component again to simulate a re-render after state updates.
// This mimics React re-rendering the component to reflect new state.
console.log('\nSecond Call (After Update):');
MyComponent();
// Reset the index again after this render to keep hook order consistent.
resetIndex();
Enter fullscreen mode Exit fullscreen mode

How to Execute

  1. In a Browser: Open your browser's developer tools (e.g., Chrome DevTools), go to the "Console" tab, copy-paste the code above, and press Enter. You'll see the logs showing the initial state, updates, and updated state.
  2. In Node.js: Save this code in a file (e.g., simpleUseReducer.js) and run it using node simpleUseReducer.js in your terminal. The output will appear in the console.

Expected Output

When you run this code, you'll see output similar to this:

First Call (Initial Render):
Current State - Count: 0 Name: Zeeshan
Resetting index for next render

Updating State:
State updated to: {"count":1,"name":"Zeeshan"} at index 0 with action: {"type":"INCREMENT"}
State updated to: {"count":1,"name":"John"} at index 0 with action: {"type":"SET_NAME","payload":"John"}

Second Call (After Update):
Current State - Count: 1 Name: John
Resetting index for next render
Enter fullscreen mode Exit fullscreen mode

Explanation of Code

  • Purpose: This polyfill demonstrates the basic idea of useReducer—managing complex state in a functional component by using a reducer function to update state based on dispatched actions, across "renders."
  • How It Works:
    • stateStore is a simple array that holds state values. Each useReducer call gets a unique index tracked by currentIndex during a render, ensuring consistency across calls.
    • The state is initialized the first time useReducer is called for that index with the provided initialState.
    • dispatch updates the state by calling the reducer function with the current state and an action, storing the new state in stateStore.
    • Calling MyComponent() multiple times simulates re-renders, showing the updated state.
  • Simplification: Unlike real React, there’s no automatic re-rendering or complex hook order edge cases. It’s a basic demonstration of state management with a reducer using an array.

Key Interview Talking Points

  • What is useReducer?: Explain it’s a React hook for managing state in functional components, especially for complex state logic. It uses a reducer function to update state based on actions, similar to Redux.
  • How This Polyfill Works: Walk through the code:
    • State is stored in an array (stateStore) with each useReducer call getting its own slot via currentIndex.
    • dispatch updates the state by invoking the reducer with the current state and an action, storing the result.
    • Calling the component again shows the updated state, mimicking a re-render.
  • Why Use useReducer Over useState?: Mention that useReducer is preferred for complex state updates (e.g., multiple related fields or logic-heavy transitions) as it centralizes update logic in a reducer, making it more predictable and testable.
  • Why Simple?: Note this is a basic version to show the concept. Real React uses a fiber tree and update queue for state management and rendering, which this manual simulation simplifies.
  • State Persistence: Highlight that state isn’t reset between calls to the component, just like in React, where state persists across renders.
  • Limitations: Point out that this lacks React’s automatic re-rendering, batched updates, or strict hook order rules. It’s purely for conceptual understanding.

This useReducer polyfill is intentionally minimal, mirroring the simplicity of the useState example, and focuses on the core idea of state management with a reducer for an interview. It’s easy to explain and execute, showing how state is stored and updated via actions. If you’d like to add more detail, combine it with useState, or explore more complex reducer examples, let me know!

Top comments (0)