React UseStack Hook: Managing a Stack Data Structure
This challenge asks you to create a custom React hook, useStack, that provides functionality for managing a stack data structure. A stack follows the Last-In, First-Out (LIFO) principle, and this hook will allow components to easily push, pop, peek, and check the size of a stack. This is useful for scenarios like undo/redo functionality, expression evaluation, or managing function call history.
Problem Description
You need to implement a useStack hook in TypeScript that provides the following functionalities:
- Initialization: The hook should accept an initial value as an optional argument. This initial value will be the first element in the stack.
push(item: T): Adds an item to the top of the stack.pop(): Removes and returns the item at the top of the stack. Returnsundefinedif the stack is empty.peek(): Returns the item at the top of the stack without removing it. Returnsundefinedif the stack is empty.size(): Returns the number of items in the stack.isEmpty(): Returnstrueif the stack is empty,falseotherwise.
The hook should manage the stack internally and return an object containing the functions described above. The type T represents the type of the items stored in the stack.
Key Requirements:
- The hook must be written in TypeScript.
- The hook must correctly implement the LIFO behavior of a stack.
- The hook must handle edge cases such as an empty stack gracefully.
- The hook should be efficient in terms of performance.
Expected Behavior:
The hook should maintain the stack's state and update it correctly with each operation. The returned functions should accurately reflect the current state of the stack.
Examples
Example 1:
Input: Initial value: [1, 2, 3]
Output:
push(4) -> stack: [1, 2, 3, 4]
peek() -> 4
pop() -> 4, stack: [1, 2, 3]
size() -> 3
isEmpty() -> false
Explanation: The stack is initialized with [1, 2, 3]. Pushing 4 adds it to the top. Peeking returns the top element (4). Popping removes and returns 4. Size returns the current number of elements (3). isEmpty returns false.
Example 2:
Input: Initial value: []
Output:
push("a") -> stack: ["a"]
push("b") -> stack: ["a", "b"]
pop() -> "b", stack: ["a"]
peek() -> "a"
size() -> 1
isEmpty() -> false
pop() -> "a", stack: []
isEmpty() -> true
pop() -> undefined, stack: []
peek() -> undefined
Explanation: The stack starts empty. "a" and "b" are pushed. "b" is popped. "a" is peeked. The stack is then emptied, and subsequent pop and peek operations return undefined.
Example 3: (Edge Case - Empty Stack)
Input: Initial value: []
Output:
pop() -> undefined
peek() -> undefined
size() -> 0
isEmpty() -> true
Explanation: Demonstrates the behavior of the hook when initialized with an empty stack and attempting to pop or peek.
Constraints
- The stack can hold any type of data (represented by the generic type
T). - The
pushoperation should be efficient (O(1) time complexity). - The
pop,peek,size, andisEmptyoperations should also be efficient (O(1) time complexity). - The hook should not cause unnecessary re-renders of the component using it.
Notes
- Consider using a simple JavaScript array internally to represent the stack.
- Remember to use the
useStatehook to manage the stack's state. - Think about how to handle the initial value correctly when initializing the stack.
- Ensure your code is well-typed and handles potential errors gracefully.
- Focus on creating a clean, reusable, and efficient hook.