Skip to main content

Overview

Web XP uses React’s useReducer hook for centralized state management. This pattern provides predictable state updates through dispatched actions, similar to Redux but with less boilerplate. Implementation: src/WinXP/index.jsx:55 and src/WinXP/reducer.js:1

The useReducer Pattern

The WinXP component initializes state with:
const [state, dispatch] = useReducer(reducer, initState);
This creates:
  • state: The current state object
  • dispatch: Function to dispatch actions to update state

Initial State

The initial state is defined in src/WinXP/reducer.js:18:
export const initState = {
  apps: defaultAppState,           // Array of open applications
  nextAppID: defaultAppState.length, // Counter for unique app IDs
  nextZIndex: defaultAppState.length, // Counter for window stacking
  focusing: FOCUSING.WINDOW,        // What has focus (WINDOW/ICON/DESKTOP)
  icons: defaultIconState,          // Array of desktop icons
  selecting: null,                  // Drag-select start position or null
  powerState: POWER_STATE.START,    // Power state (START/LOG_OFF/TURN_OFF)
};

State Shape

apps

Array of open application instances:
{
  id: number,              // Unique app instance ID
  name: string,            // App name
  component: React.Component, // App component
  header: {                // Window header config
    icon: string,          // Header icon path
    title: string,         // Window title
    buttons: string[],     // Optional: visible buttons
    noFooterWindow: boolean, // Optional: hide from taskbar
    invisible: boolean,    // Optional: hide window chrome
  },
  defaultSize: {           // Initial window size
    width: number,
    height: number,
  },
  defaultOffset: {         // Initial window position
    x: number,
    y: number,
  },
  resizable: boolean,      // Can window be resized?
  minimized: boolean,      // Is window minimized?
  maximized: boolean,      // Is window maximized?
  multiInstance: boolean,  // Allow multiple instances?
  zIndex: number,          // Window stacking order
  injectProps: object,     // Optional: props to pass to component
  minWidth: number,        // Optional: minimum width
  minHeight: number,       // Optional: minimum height
}

icons

Array of desktop icons:
{
  id: number,              // Unique icon ID
  icon: string,            // Icon image path
  title: string,           // Icon label
  component: React.Component, // App to launch on double-click
  isFocus: boolean,        // Is icon selected/focused?
  appName: string,         // App name key
}

focusing

Indicates what element type currently has focus:
FOCUSING.WINDOW   // A window has focus
FOCUSING.ICON     // Desktop icon(s) have focus
FOCUSING.DESKTOP  // Desktop has focus (nothing selected)
Constants: src/WinXP/constants/index.js:1

selecting

Drag-to-select state:
null  // Not selecting
{ x: number, y: number }  // Selection start position

powerState

Current power operation state:
POWER_STATE.START     // Normal operation
POWER_STATE.LOG_OFF   // Log off dialog shown
POWER_STATE.TURN_OFF  // Shutdown dialog shown
Constants: src/WinXP/constants/index.js:6

Action Types

All action types are defined in src/WinXP/constants/actions.js:1:
// Application actions
export const ADD_APP = 'ADD_APP';
export const DEL_APP = 'DEL_APP';
export const FOCUS_APP = 'FOCUS_APP';
export const MINIMIZE_APP = 'MINIMIZE_APP';
export const TOGGLE_MAXIMIZE_APP = 'TOGGLE_MAXIMIZE_APP';

// Icon actions
export const FOCUS_ICON = 'FOCUS_ICON';
export const SELECT_ICONS = 'SELECT_ICONS';

// Desktop actions
export const FOCUS_DESKTOP = 'FOCUS_DESKTOP';
export const START_SELECT = 'START_SELECT';
export const END_SELECT = 'END_SELECT';

// Power actions
export const POWER_OFF = 'POWER_OFF';
export const CANCEL_POWER_OFF = 'CANCEL_POWER_OFF';

Reducer Implementation

The reducer is defined in src/WinXP/reducer.js:28:
export const reducer = (state, action = { type: '' }) => {
  switch (action.type) {
    case ADD_APP:
      // Add new app or focus existing single-instance app
      
    case DEL_APP:
      // Remove app and update focus
      
    case FOCUS_APP:
      // Bring app to front and restore if minimized
      
    case MINIMIZE_APP:
      // Minimize app and update focus
      
    case TOGGLE_MAXIMIZE_APP:
      // Toggle app maximized state
      
    case FOCUS_ICON:
      // Focus single icon
      
    case SELECT_ICONS:
      // Focus multiple icons (from drag-select)
      
    case FOCUS_DESKTOP:
      // Clear icon focus
      
    case START_SELECT:
      // Begin drag-select
      
    case END_SELECT:
      // End drag-select
      
    case POWER_OFF:
      // Show power dialog
      
    case CANCEL_POWER_OFF:
      // Close power dialog
      
    default:
      return state;
  }
};

Dispatching Actions

Actions are dispatched throughout the WinXP component:

Adding an Application

dispatch({
  type: ADD_APP,
  payload: appSettings.Notepad
});
Implementation: src/WinXP/index.jsx:149

Closing an Application

dispatch({
  type: DEL_APP,
  payload: appId
});
Implementation: src/WinXP/index.jsx:108

Focusing an Application

dispatch({
  type: FOCUS_APP,
  payload: appId
});
Implementation: src/WinXP/index.jsx:96

Minimizing an Application

dispatch({
  type: MINIMIZE_APP,
  payload: appId
});
Implementation: src/WinXP/index.jsx:104

Toggling Maximize

dispatch({
  type: TOGGLE_MAXIMIZE_APP,
  payload: appId
});
Implementation: src/WinXP/index.jsx:100

Focusing an Icon

dispatch({
  type: FOCUS_ICON,
  payload: iconId
});
Implementation: src/WinXP/index.jsx:124

Selecting Multiple Icons

dispatch({
  type: SELECT_ICONS,
  payload: [iconId1, iconId2, iconId3]
});
Implementation: src/WinXP/index.jsx:207

Focusing Desktop

dispatch({
  type: FOCUS_DESKTOP
});
Implementation: src/WinXP/index.jsx:135

Starting Drag-Select

dispatch({
  type: START_SELECT,
  payload: { x: mouse.docX, y: mouse.docY }
});
Implementation: src/WinXP/index.jsx:196

Ending Drag-Select

dispatch({
  type: END_SELECT
});
Implementation: src/WinXP/index.jsx:203

Power Operations

// Show log off dialog
dispatch({
  type: POWER_OFF,
  payload: POWER_STATE.LOG_OFF
});

// Show shutdown dialog
dispatch({
  type: POWER_OFF,
  payload: POWER_STATE.TURN_OFF
});

// Cancel power operation
dispatch({
  type: CANCEL_POWER_OFF
});
Implementation: src/WinXP/index.jsx:174 and src/WinXP/index.jsx:177

Action Details

ADD_APP

Adds a new application window or focuses an existing single-instance app: Logic:
  1. Check if app already exists and is single-instance
  2. If exists and single-instance: bring to front and restore
  3. If new or multi-instance: create new app with unique ID
  4. Increment nextAppID and nextZIndex
  5. Set focusing to FOCUSING.WINDOW
Implementation: src/WinXP/reducer.js:30

DEL_APP

Removes an application and updates focus: Logic:
  1. Filter out app with matching ID
  2. If remaining apps exist: set focusing to FOCUSING.WINDOW
  3. Else if focused icons exist: set focusing to FOCUSING.ICON
  4. Else: set focusing to FOCUSING.DESKTOP
Implementation: src/WinXP/reducer.js:64

FOCUS_APP

Brings an app window to the front: Logic:
  1. Find app with matching ID
  2. Set app’s zIndex to nextZIndex
  3. Set app’s minimized to false
  4. Increment nextZIndex
  5. Set focusing to FOCUSING.WINDOW
Implementation: src/WinXP/reducer.js:78

MINIMIZE_APP

Minimizes an application window: Logic:
  1. Find app and set minimized to true
  2. If other non-minimized apps exist: set focusing to FOCUSING.WINDOW
  3. Else if focused icons exist: set focusing to FOCUSING.ICON
  4. Else: set focusing to FOCUSING.DESKTOP
Implementation: src/WinXP/reducer.js:91

TOGGLE_MAXIMIZE_APP

Toggles window maximized state: Logic:
  1. Find app and toggle maximized boolean
  2. Set focusing to FOCUSING.WINDOW
Implementation: src/WinXP/reducer.js:108

FOCUS_ICON

Focuses a single desktop icon: Logic:
  1. Set isFocus to true for matching icon
  2. Set isFocus to false for all other icons
  3. Set focusing to FOCUSING.ICON
Implementation: src/WinXP/reducer.js:118

SELECT_ICONS

Focuses multiple icons (from drag-select): Logic:
  1. Set isFocus to true for icons in payload array
  2. Set isFocus to false for all other icons
  3. Set focusing to FOCUSING.ICON
Implementation: src/WinXP/reducer.js:129

FOCUS_DESKTOP

Clears all icon focus: Logic:
  1. Set isFocus to false for all icons
  2. Set focusing to FOCUSING.DESKTOP
Implementation: src/WinXP/reducer.js:140

START_SELECT

Begins drag-to-select operation: Logic:
  1. Set isFocus to false for all icons
  2. Set selecting to payload position { x, y }
  3. Set focusing to FOCUSING.DESKTOP
Implementation: src/WinXP/reducer.js:149

END_SELECT

Ends drag-to-select operation: Logic:
  1. Set selecting to null
Implementation: src/WinXP/reducer.js:159

POWER_OFF

Shows power operation dialog: Logic:
  1. Set powerState to payload (POWER_STATE.LOG_OFF or POWER_STATE.TURN_OFF)
Implementation: src/WinXP/reducer.js:164

CANCEL_POWER_OFF

Closes power operation dialog: Logic:
  1. Set powerState to POWER_STATE.START
Implementation: src/WinXP/reducer.js:169

Benefits of This Pattern

Predictable State Updates

All state changes go through the reducer, making it easy to trace how state changes in response to actions.

Centralized Logic

Complex state update logic is centralized in the reducer rather than scattered throughout components.

Easy Debugging

You can log actions and state to see exactly what happened:
export const reducer = (state, action = { type: '' }) => {
  console.log('Action:', action);
  console.log('Previous State:', state);
  
  // ... reducer logic ...
  
  console.log('Next State:', newState);
  return newState;
};

Testable

Reducer is a pure function that’s easy to test:
const prevState = { apps: [], nextAppID: 0 };
const action = { type: ADD_APP, payload: appSettings.Notepad };
const nextState = reducer(prevState, action);

expect(nextState.apps.length).toBe(1);
expect(nextState.nextAppID).toBe(1);

Comparison to useState

Instead of managing multiple useState calls:
// Without useReducer (harder to manage)
const [apps, setApps] = useState([]);
const [nextAppID, setNextAppID] = useState(0);
const [nextZIndex, setNextZIndex] = useState(0);
const [focusing, setFocusing] = useState(FOCUSING.DESKTOP);
const [icons, setIcons] = useState(defaultIconState);
const [selecting, setSelecting] = useState(null);
const [powerState, setPowerState] = useState(POWER_STATE.START);
Use a single reducer:
// With useReducer (cleaner and more maintainable)
const [state, dispatch] = useReducer(reducer, initState);
This is especially beneficial when state updates involve multiple pieces of state or complex logic.

Build docs developers (and LLMs) love