Repository: streamich/react-use
Pull request #613 (2019-09-18 12:52:38)

useQueue hook
implements simple queue


Patch:
diff --git a/README.md b/README.md
index 120c86f4..ffde67da 100644
--- a/README.md
+++ b/README.md
@@ -134,8 +134,11 @@
   - [`useStateList`](./docs/useStateList.md) &mdash; circularly iterates over an array. [![][img-demo]](https://codesandbox.io/s/bold-dewdney-pjzkd)
   - [`useToggle` and `useBoolean`](./docs/useToggle.md) &mdash; tracks state of a boolean. [![][img-demo]](https://codesandbox.io/s/focused-sammet-brw2d)
   - [`useCounter` and `useNumber`](./docs/useCounter.md) &mdash; tracks state of a number. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usecounter--demo)
+  - [`useList`](./docs/useList.md) &mdash; tracks state of an array.
+  - [`useMap`](./docs/useMap.md) &mdash; tracks state of an object.
   - [`useList`](./docs/useList.md) and [`useUpsert`](./docs/useUpsert.md) &mdash; tracks state of an array. [![][img-demo]](https://codesandbox.io/s/wonderful-mahavira-1sm0w)
   - [`useMap`](./docs/useMap.md) &mdash; tracks state of an object. [![][img-demo]](https://codesandbox.io/s/quirky-dewdney-gi161)
+  - [`useQueue`](./docs/useQueue.md) &mdash; implements simple queue.
   - [`useStateValidator`](./docs/useStateValidator.md) &mdash; tracks state of an object. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usestatevalidator--demo)
   - [`useMultiStateValidator`](./docs/useMultiStateValidator.md) &mdash; alike the `useStateValidator`, but tracks multiple states at a time. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usemultistatevalidator--demo)
   - [`useMediatedState`](./docs/useMediatedState.md) &mdash; like the regular `useState` but with mediation by custom function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usemediatedstate--demo)
diff --git a/docs/useQueue.md b/docs/useQueue.md
new file mode 100644
index 00000000..ab05d2f7
--- /dev/null
+++ b/docs/useQueue.md
@@ -0,0 +1,26 @@
+# `useQueue`
+
+React state hook implements simple FIFO queue.
+
+
+## Usage
+
+```jsx
+import { useQueue } from 'react-use';
+
+const Demo = () => {
+  const { add, remove, first, last, size } = useQueue();
+
+  return (
+    <div>
+      <ul>
+        <li>first: {first}</li>
+        <li>last: {last}</li>
+        <li>size: {size}</li>
+      </ul>
+      <button onClick={() => add((last || 0) + 1)}>Add</button>
+      <button onClick={() => pop()}>Remove</button>
+    </div>
+  );
+};
+```
diff --git a/src/__stories__/useQueue.story.tsx b/src/__stories__/useQueue.story.tsx
new file mode 100644
index 00000000..ecb7ab43
--- /dev/null
+++ b/src/__stories__/useQueue.story.tsx
@@ -0,0 +1,23 @@
+import { storiesOf } from '@storybook/react';
+import * as React from 'react';
+import { useQueue } from '..';
+import ShowDocs from './util/ShowDocs';
+
+const Demo = () => {
+  const { add, remove, first, last, size } = useQueue();
+  return (
+    <div>
+      <ul>
+        <li>first: {first}</li>
+        <li>last: {last}</li>
+        <li>size: {size}</li>
+      </ul>
+      <button onClick={() => add((last || 0) + 1)}>Add</button>
+      <button onClick={() => remove()}>Removw</button>
+    </div>
+  );
+};
+
+storiesOf('State|useQueue', module)
+  .add('Docs', () => <ShowDocs md={require('../../docs/useQueue.md')} />)
+  .add('Demo', () => <Demo />);
diff --git a/src/index.ts b/src/index.ts
index 40a4fb2e..6ffced21 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -61,6 +61,7 @@ export { default as usePermission } from './usePermission';
 export { default as usePrevious } from './usePrevious';
 export { default as usePreviousDistinct } from './usePreviousDistinct';
 export { default as usePromise } from './usePromise';
+export { default as useQueue } from './useQueue';
 export { default as useRaf } from './useRaf';
 export { default as useRafLoop } from './useRafLoop';
 export { default as useRafState } from './useRafState';
diff --git a/src/useQueue.ts b/src/useQueue.ts
new file mode 100644
index 00000000..97a8372d
--- /dev/null
+++ b/src/useQueue.ts
@@ -0,0 +1,37 @@
+import { useState } from 'react';
+
+export interface QueueMethods<T> {
+  add: (item: T) => void;
+  remove: () => T;
+  first: T;
+  last: T;
+  size: number;
+}
+
+const useQueue = <T>(initialValue: T[] = []): QueueMethods<T> => {
+  const [state, set] = useState(initialValue);
+  return {
+    add: value => {
+      set(queue => [...queue, value]);
+    },
+    remove: () => {
+      let result;
+      set(([first, ...rest]) => {
+        result = first;
+        return rest;
+      });
+      return result;
+    },
+    get first() {
+      return state[0];
+    },
+    get last() {
+      return state[state.length - 1];
+    },
+    get size() {
+      return state.length;
+    },
+  };
+};
+
+export default useQueue;


Test patch:
diff --git a/src/__tests__/useQueue.test.ts b/src/__tests__/useQueue.test.ts
new file mode 100644
index 00000000..4a155ff3
--- /dev/null
+++ b/src/__tests__/useQueue.test.ts
@@ -0,0 +1,33 @@
+import { act, renderHook } from '@testing-library/react-hooks';
+import useQueue from '../useQueue';
+
+const setUp = (initialQueue?: any[]) => renderHook(() => useQueue(initialQueue));
+
+it('takes initial state', () => {
+  const { result } = setUp([1, 2, 3]);
+  const { first, last, size } = result.current;
+  expect(first).toEqual(1);
+  expect(last).toEqual(3);
+  expect(size).toEqual(3);
+});
+
+it('appends new member', () => {
+  const { result } = setUp([1, 2]);
+  act(() => {
+    result.current.add(3);
+  });
+  const { first, last, size } = result.current;
+  expect(first).toEqual(1);
+  expect(last).toEqual(3);
+  expect(size).toEqual(3);
+});
+
+it('pops oldest member', () => {
+  const { result } = setUp([1, 2]);
+  act(() => {
+    result.current.remove();
+  });
+  const { first, size } = result.current;
+  expect(first).toEqual(2);
+  expect(size).toEqual(1);
+});