Skip to main content

Overview

Manages an array in React state and exposes immutable helpers for common list operations like append, prepend, insert, remove, reorder, swap, and targeted item updates.

Import

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

Signature

function useListState<T>(
  initialValue?: T[] | (() => T[])
): UseListStateReturnValue<T>;

Parameters

initialValue
T[] | (() => T[])
default:"[]"
Initial list value or lazy initializer function

Return Value

[state, handlers]
UseListStateReturnValue<T>
A tuple containing the current list and handler functions
state
T[]
Current array state
handlers
UseListStateHandlers<T>
Object containing list manipulation functions
handlers.setState
Dispatch<SetStateAction<T[]>>
Direct state setter (supports updater functions)
handlers.append
(...items: T[]) => void
Appends items to the end of the list
handlers.prepend
(...items: T[]) => void
Prepends items to the start of the list
handlers.insert
(index: number, ...items: T[]) => void
Inserts items at the specified index
handlers.pop
() => void
Removes the last item from the list
handlers.shift
() => void
Removes the first item from the list
handlers.apply
(fn: (item: T, index?: number) => T) => void
Applies a function to all items
handlers.applyWhere
(condition: (item: T, index: number) => boolean, fn: (item: T, index?: number) => T) => void
Applies a function to items matching a condition
handlers.remove
(...indices: number[]) => void
Removes items at the specified indices
handlers.reorder
(payload: { from: number; to: number }) => void
Moves an item from one position to another
handlers.swap
(payload: { from: number; to: number }) => void
Swaps two items at the specified positions
handlers.setItem
(index: number, item: T) => void
Sets an item at the specified index
handlers.setItemProp
<K extends keyof T>(index: number, prop: K, value: T[K]) => void
Sets a property of an item at the specified index
handlers.filter
(fn: (item: T, i: number) => boolean) => void
Filters the list using a predicate function

Usage

Todo List

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

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

function TodoList() {
  const [todos, handlers] = useListState<Todo>([
    { id: '1', text: 'Learn React', completed: false },
    { id: '2', text: 'Build an app', completed: false },
  ]);

  const addTodo = (text: string) => {
    handlers.append({
      id: Date.now().toString(),
      text,
      completed: false,
    });
  };

  const toggleTodo = (index: number) => {
    handlers.setItemProp(index, 'completed', !todos[index].completed);
  };

  return (
    <div>
      {todos.map((todo, index) => (
        <div key={todo.id}>
          <input
            type="checkbox"
            checked={todo.completed}
            onChange={() => toggleTodo(index)}
          />
          {todo.text}
          <button onClick={() => handlers.remove(index)}>Delete</button>
        </div>
      ))}
    </div>
  );
}

Reorderable List

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

function ReorderableList() {
  const [items, handlers] = useListState(['Apple', 'Banana', 'Cherry']);

  return (
    <div>
      {items.map((item, index) => (
        <div key={item}>
          <span>{item}</span>
          {index > 0 && (
            <button onClick={() => handlers.swap({ from: index, to: index - 1 })}>
              Move Up
            </button>
          )}
          {index < items.length - 1 && (
            <button onClick={() => handlers.swap({ from: index, to: index + 1 })}>
              Move Down
            </button>
          )}
        </div>
      ))}
    </div>
  );
}

Bulk Operations

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

interface User {
  name: string;
  age: number;
}

function UserList() {
  const [users, handlers] = useListState<User>([
    { name: 'John', age: 23 },
    { name: 'Amy', age: 21 },
    { name: 'Bill', age: 36 },
  ]);

  const incrementAllAges = () => {
    handlers.apply((user) => ({ ...user, age: user.age + 1 }));
  };

  const incrementSeniorAges = () => {
    handlers.applyWhere(
      (user) => user.age > 30,
      (user) => ({ ...user, age: user.age + 1 })
    );
  };

  const removeMinors = () => {
    handlers.filter((user) => user.age >= 18);
  };

  return (
    <div>
      <button onClick={incrementAllAges}>Increment All Ages</button>
      <button onClick={incrementSeniorAges}>Increment Senior Ages</button>
      <button onClick={removeMinors}>Remove Minors</button>
      {users.map((user, index) => (
        <div key={index}>
          {user.name} - {user.age}
        </div>
      ))}
    </div>
  );
}

Stack and Queue Operations

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

function StackQueue() {
  const [stack, stackHandlers] = useListState<number>([]);
  const [queue, queueHandlers] = useListState<number>([]);

  return (
    <div>
      <div>
        <h3>Stack (LIFO)</h3>
        <button onClick={() => stackHandlers.append(Date.now())}>Push</button>
        <button onClick={() => stackHandlers.pop()}>Pop</button>
        <p>Items: {stack.join(', ')}</p>
      </div>
      
      <div>
        <h3>Queue (FIFO)</h3>
        <button onClick={() => queueHandlers.append(Date.now())}>Enqueue</button>
        <button onClick={() => queueHandlers.shift()}>Dequeue</button>
        <p>Items: {queue.join(', ')}</p>
      </div>
    </div>
  );
}

Type Definitions

export interface UseListStateHandlers<T> {
  setState: Dispatch<SetStateAction<T[]>>;
  append: (...items: T[]) => void;
  prepend: (...items: T[]) => void;
  insert: (index: number, ...items: T[]) => void;
  pop: () => void;
  shift: () => void;
  apply: (fn: (item: T, index?: number) => T) => void;
  applyWhere: (
    condition: (item: T, index: number) => boolean,
    fn: (item: T, index?: number) => T
  ) => void;
  remove: (...indices: number[]) => void;
  reorder: ({ from, to }: { from: number; to: number }) => void;
  swap: ({ from, to }: { from: number; to: number }) => void;
  setItem: (index: number, item: T) => void;
  setItemProp: <K extends keyof T, U extends T[K]>(
    index: number,
    prop: K,
    value: U
  ) => void;
  filter: (fn: (item: T, i: number) => boolean) => void;
}

export type UseListStateReturnValue<T> = [T[], UseListStateHandlers<T>];

Build docs developers (and LLMs) love