Hone logo
Hone
Problems

Custom React Hook: useList

Develop a custom React hook, useList, that simplifies common list manipulation operations. This hook will provide a stateful list and functions to add, remove, update, and clear elements, making it easier to manage dynamic lists within React components.

Problem Description

You are tasked with creating a custom React hook named useList. This hook should manage an array of any type and expose methods to interact with that array. The goal is to abstract away the common patterns of list management in React, such as adding new items, removing existing ones, updating specific items, and clearing the entire list, into a reusable and declarative hook.

Key Requirements:

  1. State Management: The hook must maintain a stateful list (an array).
  2. Initial State: The hook should accept an optional initial list value. If not provided, it should default to an empty array.
  3. Core Operations: The hook must expose the following functions:
    • add(item): Adds a new item to the end of the list.
    • remove(index): Removes the item at the given index from the list.
    • update(index, newItem): Replaces the item at the given index with newItem.
    • clear(): Removes all items from the list.
    • set(newList): Replaces the entire current list with newList.
  4. Return Value: The hook should return an object containing the current list state and all the operation functions.
  5. Type Safety: The hook should be implemented in TypeScript and be generic to work with lists of any data type.

Expected Behavior:

When the hook is used in a React component, the component should re-render whenever the list state changes due to any of the provided operations. The operations should modify the internal state immutably.

Edge Cases:

  • Handling invalid indices for remove and update operations (e.g., negative indices, indices out of bounds). The hook should ideally not throw errors but rather do nothing or handle them gracefully (e.g., no change to the list).
  • Adding undefined or null to the list.

Examples

Example 1:

import { useList } from './useList'; // Assuming your hook is in useList.ts

function MyComponent() {
  const { list, add, remove, update, clear } = useList<string>(['apple', 'banana']);

  return (
    <div>
      <ul>
        {list.map((item, index) => (
          <li key={index}>
            {item}
            <button onClick={() => remove(index)}>Remove</button>
            <button onClick={() => update(index, item.toUpperCase())}>Uppercase</button>
          </li>
        ))}
      </ul>
      <button onClick={() => add('orange')}>Add Orange</button>
      <button onClick={clear}>Clear List</button>
    </div>
  );
}

Output (after initial render):

<div>
  <ul>
    <li>apple<button>Remove</button><button>Uppercase</button></li>
    <li>banana<button>Remove</button><button>Uppercase</button></li>
  </ul>
  <button>Add Orange</button>
  <button>Clear List</button>
</div>

Explanation:

The useList hook is initialized with ['apple', 'banana']. The component renders the initial list and buttons to manipulate it.

Example 2:

User clicks "Add Orange"

State Change: list becomes ['apple', 'banana', 'orange']

Example 3:

User clicks "Remove" for "apple" (index 0)

State Change: list becomes ['banana', 'orange']

Example 4:

User clicks "Uppercase" for "banana" (index 0)

State Change: list becomes ['BANANA', 'orange']

Example 5:

User clicks "Clear List"

State Change: list becomes []

Example 6: (Edge Case - Invalid Index)

// ... inside MyComponent
// Assume list is ['apple', 'banana']
remove(5); // Calling remove with an index out of bounds

State Change: No change to the list. The hook gracefully handles the out-of-bounds index.

Constraints

  • The hook must be implemented in TypeScript.
  • The hook should accept a generic type <T> for the list elements.
  • The initial list provided to the hook can be undefined.
  • All list modifications must be performed immutably.
  • Performance: The hook should be efficient for typical list sizes (up to a few hundred items). For extremely large lists, further optimizations might be considered, but standard useState and immutable updates are expected.

Notes

  • Consider using React.useState internally to manage the list state.
  • When implementing remove and update, think about how to create a new array without mutating the original. Array methods like slice, filter, map, and the spread syntax (...) can be useful.
  • Ensure that the functions returned by the hook are stable references (e.g., using useCallback if necessary, though for simple functions like these, it might not be strictly required unless they are passed as dependencies to other hooks or components).
  • Pay attention to how you handle array indices to prevent unexpected behavior.
Loading editor...
typescript