Skip to main content

Overview

Creates a stable Map instance and forces a re-render when mutating methods (set, delete, clear) are called.

Import

import { useMap } from '@kuzenbo/hooks';

Signature

function useMap<T, V>(
  initialState?: [T, V][]
): Map<T, V>;

Parameters

initialState
[T, V][]
Optional key-value entries used to initialize the map

Return Value

map
Map<T, V>
A stable Map instance that triggers re-renders on mutations

Usage

Basic Map Usage

import { useMap } from '@kuzenbo/hooks';

function UserCache() {
  const users = useMap<string, { name: string; age: number }>([
    ['user1', { name: 'John', age: 30 }],
    ['user2', { name: 'Jane', age: 25 }],
  ]);

  const addUser = (id: string, name: string, age: number) => {
    users.set(id, { name, age });
  };

  const removeUser = (id: string) => {
    users.delete(id);
  };

  return (
    <div>
      <h3>Users ({users.size})</h3>
      {Array.from(users.entries()).map(([id, user]) => (
        <div key={id}>
          {user.name} - {user.age}
          <button onClick={() => removeUser(id)}>Remove</button>
        </div>
      ))}
      <button onClick={() => addUser(`user${users.size + 1}`, 'New User', 20)}>
        Add User
      </button>
      <button onClick={() => users.clear()}>Clear All</button>
    </div>
  );
}

Form Field Errors

import { useMap } from '@kuzenbo/hooks';

function FormWithValidation() {
  const errors = useMap<string, string>();
  const [formData, setFormData] = React.useState({
    email: '',
    password: '',
  });

  const validate = (field: string, value: string) => {
    if (field === 'email' && !value.includes('@')) {
      errors.set('email', 'Invalid email address');
    } else if (field === 'password' && value.length < 8) {
      errors.set('password', 'Password must be at least 8 characters');
    } else {
      errors.delete(field);
    }
  };

  const handleChange = (field: string, value: string) => {
    setFormData((prev) => ({ ...prev, [field]: value }));
    validate(field, value);
  };

  return (
    <form>
      <div>
        <input
          type="email"
          value={formData.email}
          onChange={(e) => handleChange('email', e.target.value)}
          placeholder="Email"
        />
        {errors.has('email') && (
          <span className="error">{errors.get('email')}</span>
        )}
      </div>
      
      <div>
        <input
          type="password"
          value={formData.password}
          onChange={(e) => handleChange('password', e.target.value)}
          placeholder="Password"
        />
        {errors.has('password') && (
          <span className="error">{errors.get('password')}</span>
        )}
      </div>
      
      <button type="submit" disabled={errors.size > 0}>
        Submit
      </button>
    </form>
  );
}

Key-Value Settings

import { useMap } from '@kuzenbo/hooks';

function SettingsEditor() {
  const settings = useMap<string, string | number | boolean>([
    ['theme', 'dark'],
    ['fontSize', 16],
    ['notifications', true],
  ]);

  const updateSetting = (key: string, value: string | number | boolean) => {
    settings.set(key, value);
  };

  return (
    <div>
      <h3>Settings</h3>
      {Array.from(settings.entries()).map(([key, value]) => (
        <div key={key}>
          <strong>{key}:</strong> {String(value)}
        </div>
      ))}
      
      <button onClick={() => updateSetting('theme', 'light')}>
        Set Light Theme
      </button>
      <button onClick={() => updateSetting('fontSize', 18)}>
        Increase Font Size
      </button>
      <button onClick={() => settings.clear()}>
        Reset All Settings
      </button>
    </div>
  );
}

Component State Registry

import { useMap } from '@kuzenbo/hooks';

function TabManager() {
  const tabStates = useMap<string, { active: boolean; data: any }>();

  const activateTab = (tabId: string) => {
    // Deactivate all tabs
    tabStates.forEach((state, id) => {
      tabStates.set(id, { ...state, active: false });
    });
    // Activate the selected tab
    const current = tabStates.get(tabId);
    if (current) {
      tabStates.set(tabId, { ...current, active: true });
    } else {
      tabStates.set(tabId, { active: true, data: null });
    }
  };

  return (
    <div>
      <button onClick={() => activateTab('tab1')}>Tab 1</button>
      <button onClick={() => activateTab('tab2')}>Tab 2</button>
      <button onClick={() => activateTab('tab3')}>Tab 3</button>
      
      {Array.from(tabStates.entries()).map(([id, state]) => (
        state.active && <div key={id}>Content for {id}</div>
      ))}
    </div>
  );
}

Notes

  • The returned Map instance is stable across renders
  • Calling set, delete, or clear will trigger a component re-render
  • All other Map methods (get, has, entries, etc.) work normally but don’t trigger re-renders
  • Useful for managing key-value state that needs frequent lookups and updates

Build docs developers (and LLMs) love