Skip to content

State Management

Cameron Rye edited this page Nov 19, 2025 · 1 revision

State Management Guide

Overview

DosKit uses React Context API for global state management. The state is organized into three separate contexts, each managing a specific domain of the application:

  1. AppStateContext - Application selection and emulator status
  2. NetworkContext - Network connectivity status
  3. PWAContext - Progressive Web App features (install, updates)

Why Context API?

We chose React Context API over external state management libraries (like Zustand or Redux) for the following reasons:

  • No additional dependencies - Uses built-in React features
  • Appropriate scale - Sufficient for DosKit's state management needs
  • Better React integration - Works seamlessly with React DevTools
  • Simpler testing - Easier to mock and test
  • Type safety - Full TypeScript support out of the box

Contexts

1. AppStateContext

Purpose: Manages application selection and emulator status

State:

interface AppState {
  currentApp: DosApp | null;      // Currently selected DOS application
  showAppSelector: boolean;       // Whether app selector is visible
  isEmulatorReady: boolean;       // Whether DOS emulator is ready
  isLoadingApp: boolean;          // Whether an app is loading
  error: string | null;           // Current error message
}

Usage:

import { useAppState, useAppStateValue, useAppStateActions } from '@/contexts';

// Get full context (state + actions)
const { currentApp, setCurrentApp } = useAppState();

// Get only state (read-only)
const { currentApp, isEmulatorReady } = useAppStateValue();

// Get only actions
const { setCurrentApp, setEmulatorReady } = useAppStateActions();

2. NetworkContext

Purpose: Manages network connectivity status

State:

interface NetworkState {
  isOnline: boolean;              // Whether browser is online
  showOfflineMessage: boolean;    // Whether to show offline message
}

Usage:

import { useNetwork, useNetworkState, useNetworkActions } from '@/contexts';

// Get full context
const { isOnline, setShowOfflineMessage } = useNetwork();

// Get only state
const { isOnline } = useNetworkState();

Features:

  • Automatically listens to browser online/offline events
  • Updates state when network status changes
  • Provides manual control over offline message visibility

3. PWAContext

Purpose: Manages Progressive Web App features

State:

interface PWAState {
  deferredPrompt: BeforeInstallPromptEvent | null;  // Install prompt event
  showInstallPrompt: boolean;                       // Show install UI
  isInstalled: boolean;                             // App is installed
  updateRegistration: ServiceWorkerRegistration | null;  // Update available
  hasUpdate: boolean;                               // Computed: update available
}

Usage:

import { usePWA, usePWAState, usePWAActions } from '@/contexts';

// Get full context
const { isInstalled, setShowInstallPrompt } = usePWA();

// Get only state
const { isInstalled, hasUpdate } = usePWAState();

Setup

Provider Hierarchy

Wrap your app with all providers in main.tsx:

import { AppStateProvider, NetworkProvider, PWAProvider } from './contexts';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <NetworkProvider>
      <PWAProvider>
        <AppStateProvider>
          <App />
        </AppStateProvider>
      </PWAProvider>
    </NetworkProvider>
  </React.StrictMode>
);

Best Practices

1. Use Specific Hooks

Prefer specific hooks over full context:

// Good - only subscribes to state changes
const { isOnline } = useNetworkState();

// Avoid - subscribes to all changes including actions
const { isOnline } = useNetwork();

2. Separate Read and Write

Components that only read state should use value hooks:

// Component that only displays status
function StatusDisplay() {
  const { isEmulatorReady } = useAppStateValue();
  return <div>Status: {isEmulatorReady ? 'Ready' : 'Loading'}</div>;
}

3. Avoid Prop Drilling

Use contexts instead of passing props through multiple levels.

4. Keep Contexts Focused

Each context should manage a single domain. Don't mix concerns.

Testing

Testing Components with Context

import { render } from '@testing-library/react';
import { AppStateProvider } from '@/contexts';

test('component uses app state', () => {
  render(
    <AppStateProvider initialApp={mockApp}>
      <MyComponent />
    </AppStateProvider>
  );
  // assertions...
});

Related Documentation


Made with ❤️ by Cameron Rye

Clone this wiki locally