Implement a useQueue Hook in React
Create a custom React hook called useQueue that provides queue data structure functionalities. This hook will allow developers to manage a list of items in a first-in, first-out (FIFO) manner within their React components, enabling efficient management of ordered operations or data.
Problem Description
You need to implement a custom React hook, useQueue, that manages a queue of elements. The hook should expose methods to interact with the queue, such as adding elements, removing elements, peeking at the front element without removing it, and checking if the queue is empty. The hook should maintain its internal state and re-render components that use it when the queue's state changes.
Key Requirements:
- Initialization: The hook should accept an optional initial array of items to populate the queue.
enqueue: A function to add an item to the end of the queue.dequeue: A function to remove and return the item from the front of the queue. If the queue is empty, it should returnundefined.peek: A function to return the item at the front of the queue without removing it. If the queue is empty, it should returnundefined.isEmpty: A function that returnstrueif the queue is empty, andfalseotherwise.size: A property or function that returns the current number of elements in the queue.- State Management: The hook must use React's state management (e.g.,
useStateoruseReducer) to ensure re-renders when the queue is modified.
Expected Behavior:
The useQueue hook should behave like a standard FIFO queue. Items added first should be removed first. Changes to the queue should trigger re-renders in the consuming component.
Edge Cases:
- Dequeuing from an empty queue.
- Peeking at an empty queue.
- Initializing the queue with an empty array or no initial elements.
Examples
Example 1:
import React from 'react';
import { useQueue } from './useQueue'; // Assuming your hook is in useQueue.ts
function MyComponent() {
const { enqueue, dequeue, peek, isEmpty, size } = useQueue<number>([1, 2]);
const handleAdd = () => {
enqueue(3);
};
const handleRemove = () => {
const removedItem = dequeue();
console.log('Removed:', removedItem);
};
return (
<div>
<p>Queue Size: {size}</p>
<p>Is Empty: {isEmpty ? 'Yes' : 'No'}</p>
<button onClick={handleAdd}>Add 3</button>
<button onClick={handleRemove}>Dequeue</button>
<p>Front Element: {peek() ?? 'None'}</p>
</div>
);
}
// In MyComponent, after rendering and clicking "Add 3", then clicking "Dequeue":
// Console logs: "Removed: 1"
// The component will re-render to show:
// Queue Size: 2
// Is Empty: No
// Front Element: 2
Example 2:
import React from 'react';
import { useQueue } from './useQueue';
function AnotherComponent() {
const { enqueue, dequeue, peek, isEmpty, size } = useQueue<string>();
enqueue('apple');
enqueue('banana');
const firstItem = peek(); // 'apple'
const removedItem = dequeue(); // 'apple'
const secondItem = dequeue(); // 'banana'
const stillEmpty = isEmpty(); // true
const finalSize = size; // 0
return (
<div>
<p>First Item: {firstItem}</p>
<p>Removed Item: {removedItem}</p>
<p>Second Item: {secondItem}</p>
<p>Is Empty Now: {stillEmpty ? 'Yes' : 'No'}</p>
<p>Final Size: {finalSize}</p>
</div>
);
}
// Output:
// First Item: apple
// Removed Item: apple
// Second Item: banana
// Is Empty Now: Yes
// Final Size: 0
Example 3: (Edge Case: Dequeue from empty queue)
import React from 'react';
import { useQueue } from './useQueue';
function EmptyQueueComponent() {
const { dequeue, peek, isEmpty, size } = useQueue<boolean>();
const handleDequeueEmpty = () => {
const item = dequeue();
console.log('Attempted dequeue from empty:', item); // undefined
};
return (
<div>
<p>Queue Size: {size}</p>
<p>Is Empty: {isEmpty ? 'Yes' : 'No'}</p>
<button onClick={handleDequeueEmpty}>Dequeue Empty</button>
<p>Peek: {peek() ?? 'None'}</p>
</div>
);
}
// Initial render:
// Queue Size: 0
// Is Empty: Yes
// Peek: None
// After clicking "Dequeue Empty":
// Console logs: "Attempted dequeue from empty: undefined"
// Component remains unchanged as it's already empty.
Constraints
- The hook must be implemented in TypeScript.
- The hook should be generic, accepting any data type for the queue elements.
- The hook should not rely on any external state management libraries (e.g., Redux, Zustand).
- Performance: While not strictly timed, the operations should be efficient. Standard array operations for enqueueing and dequeuing are acceptable.
Notes
- Consider using
React.useStateorReact.useReducerfor managing the queue's internal state. - Think about how to expose the queue's current state (
size) and the utility functions (enqueue,dequeue, etc.) to the consuming component. - Ensure that the returned functions maintain stable identities across renders if possible, though this is less critical than ensuring correct state updates.
- The
sizecan be returned as a primitive number, or a getter function if preferred for consistency with other returned methods.