useQueue Tested
A hook that allows to handle queue while maintaining the necessary state updates.
Example
import useQueue from "@/hooks/useQueue";
export default function UseQueueExample() {
const [queue, { add, remove, clear, first, last, size }] = useQueue([
1, 2, 3,
]);
return (
<div className="flex flex-col items-center justify-center gap-4 text-ctp-text">
<div className="flex gap-4">
<button
className="rounded bg-ctp-green px-4 py-2 text-ctp-base"
onClick={() => add(4)}
>
Add 4
</button>
<button
className="rounded bg-ctp-pink px-4 py-2 text-ctp-base"
onClick={() => remove()}
>
Remove
</button>
<button
className="rounded bg-ctp-red px-4 py-2 text-ctp-base"
onClick={() => clear()}
>
Clear
</button>
</div>
<div className="flex gap-4">
<p> First: {first} </p>
<p> Last: {last} </p>
<p> Size: {size} </p>
</div>
<div className="flex gap-4">
{queue.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
</div>
);
}
Code
import { useCallback, useState } from "react";
type UseQueueActions<T> = {
add: (element: T) => void;
remove: () => T | undefined;
clear: () => void;
first: T | undefined;
last: T | undefined;
size: number;
};
type UseQueueReturn<T> = [Array<T>, UseQueueActions<T>];
/**
* A hook that allows to handle queue while maintaining the necessary state updates.
* @param {Array<T>} initialValue - The optional initial value of the queue.
* @returns {UseQueueReturn} An array containing the queue and an object with the following properties:
* properties:
* 1. add - A function to add an element to the queue.
* 2. remove - A function to remove an element from the queue.
* 3. clear - A function to clear the queue.
* 4. first - The first element in the queue.
* 5. last - The last element in the queue.
*/
export default function useQueue<T>(
initialValue: Array<T> = [],
): UseQueueReturn<T> {
const [queue, setQueue] = useState(initialValue);
const add = useCallback((element: T) => {
setQueue((q) => [...q, element]);
}, []);
const remove = useCallback(() => {
let removedElement;
setQueue(([first, ...q]) => {
removedElement = first;
return q;
});
return removedElement;
}, []);
const clear = useCallback(() => {
setQueue([]);
}, []);
return [
queue,
{
add,
remove,
clear,
first: queue[0],
last: queue[queue.length - 1],
size: queue.length,
},
];
}
import useQueue from "@/hooks/useQueue";
import { renderHook, act, cleanup, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it } from "vitest";
describe("useQueue", () => {
afterEach(() => {
cleanup();
});
it("should initialize without values", () => {
const { result } = renderHook(() => useQueue());
const [queue, actions] = result.current;
expect(queue).toStrictEqual([]);
expect(actions.size).toBe(0);
});
it("should initialize with values", () => {
const { result } = renderHook(() => useQueue([1, 2, 3]));
const [queue, actions] = result.current;
expect(queue).toStrictEqual([1, 2, 3]);
expect(actions.size).toBe(3);
});
it("should add an element to the queue", () => {
const { result } = renderHook(() => useQueue([1, 2, 3]));
act(() => {
result.current[1].add(4);
});
const [queue, actions] = result.current;
expect(queue).toStrictEqual([1, 2, 3, 4]);
expect(actions.size).toBe(4);
expect(actions.first).toBe(1);
expect(actions.last).toBe(4);
});
it("should remove an element from the queue", () => {
const { result } = renderHook(() => useQueue([1, 2, 3]));
act(() => {
result.current[1].remove();
});
const [queue, actions] = result.current;
expect(queue).toStrictEqual([2, 3]);
expect(actions.size).toBe(2);
expect(actions.first).toBe(2);
expect(actions.last).toBe(3);
});
it("should clear the queue", () => {
const { result } = renderHook(() => useQueue([1, 2, 3]));
act(() => {
result.current[1].clear();
});
const [queue, actions] = result.current;
expect(queue).toStrictEqual([]);
expect(actions.size).toBe(0);
});
});