Skip to content

Commit

Permalink
useArraystate and useSetState (#1223)
Browse files Browse the repository at this point in the history
* feat(usearraystate): added a new hook to manage the contents of an array

* feat(usesetstate): manage state of a set in React hook

* chore(build): remove unnecessary import

* Update packages/rooks/src/hooks/useArrayState.ts

Co-authored-by: Qiushi Pan <[email protected]>

* chore(pr): address review

* feat(changeset): add

Co-authored-by: Qiushi Pan <[email protected]>
  • Loading branch information
imbhargav5 and qqpann authored Aug 9, 2022
1 parent 4185e59 commit 182e849
Show file tree
Hide file tree
Showing 10 changed files with 404 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-clocks-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"rooks": minor
---

add useArrayState and useSetState hooks to manage arrays and sets
36 changes: 36 additions & 0 deletions .husky/_/husky.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env sh
if [ -z "$husky_skip_init" ]; then
debug () {
if [ "$HUSKY_DEBUG" = "1" ]; then
echo "husky (debug) - $1"
fi
}

readonly hook_name="$(basename -- "$0")"
debug "starting $hook_name..."

if [ "$HUSKY" = "0" ]; then
debug "HUSKY env variable is set to 0, skipping hook"
exit 0
fi

if [ -f ~/.huskyrc ]; then
debug "sourcing ~/.huskyrc"
. ~/.huskyrc
fi

readonly husky_skip_init=1
export husky_skip_init
sh -e "$0" "$@"
exitCode="$?"

if [ $exitCode != 0 ]; then
echo "husky - $hook_name hook exited with code $exitCode (error)"
fi

if [ $exitCode = 127 ]; then
echo "husky - command not found in PATH=$PATH"
fi

exit $exitCode
fi
19 changes: 19 additions & 0 deletions data/docs/useArrayState.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
id: useArrayState
title: useArrayState
sidebar_label: useArrayState
---

## About

Array state manager hook for React. It exposes push, pop, unshift, shift, concat, fill and reverse methods to be able to easily modify the state of an array.

## Examples

```jsx
import { useArrayState } from "rooks";
export default function App() {
const [array, controls] = useArrayState([1, 2, 3]);
return null;
}
```
19 changes: 19 additions & 0 deletions data/docs/useSetState.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
id: useSetState
title: useSetState
sidebar_label: useSetState
---

## About

Manage the state of a Set in React. Exposes add, delete and clear methods along with the current set to easily manipulate values in a set.

## Examples

```jsx
import { useSetState } from "rooks";
export default function App() {
useSetState();
return null;
}
```
10 changes: 10 additions & 0 deletions data/hooks-list.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"hooks": [
{
"name": "useArrayState",
"description": "Array state manager hook for React",
"category": "state"
},
{
"name": "useAsyncEffect",
"description": "A version of useEffect that accepts an async function",
Expand Down Expand Up @@ -275,6 +280,11 @@
"description": "useState but syncs with sessionstorage",
"category": "state"
},
{
"name": "useSetState",
"description": "Manage the state of a Set in React. ",
"category": "state"
},
{
"name": "useStackState",
"description": "A React hook that manages state in the form of a stack",
Expand Down
92 changes: 92 additions & 0 deletions packages/rooks/src/__tests__/useArrayState.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { act } from "@testing-library/react";
import { renderHook } from "@testing-library/react-hooks";
import { useArrayState } from "../hooks/useArrayState";

describe("useArrayState", () => {
it("should be defined", () => {
expect.hasAssertions();
expect(useArrayState).toBeDefined();
});

it("should initialize correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

expect(result.current[0]).toEqual([1, 2, 3]);
});

it("should push correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].push(4);
});

expect(result.current[0]).toEqual([1, 2, 3, 4]);
});

it("should pop correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].pop();
});

expect(result.current[0]).toEqual([1, 2]);
});

it("should unshift correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].unshift(0);
});
expect(result.current[0]).toEqual([0, 1, 2, 3]);
});

it("should shift correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].shift();
});
expect(result.current[0]).toEqual([2, 3]);
});

it("should reverse correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].reverse();
});

expect(result.current[0]).toEqual([3, 2, 1]);
});

it("should concat correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].concat([4, 5, 6]);
});

expect(result.current[0]).toEqual([1, 2, 3, 4, 5, 6]);
});

it("should fill correctly", () => {
expect.hasAssertions();
const { result } = renderHook(() => useArrayState([1, 2, 3]));

act(() => {
result.current[1].fill(0);
});

expect(result.current[0]).toEqual([0, 0, 0]);
});
});
47 changes: 47 additions & 0 deletions packages/rooks/src/__tests__/useSetState.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { renderHook, act } from "@testing-library/react";
import { useSetState } from "../hooks/useSetState";

describe("useSetState", () => {
it("should be defined", () => {
expect.hasAssertions();
expect(useSetState).toBeDefined();
});

it("should initialize the state with the initialSetValue", () => {
expect.hasAssertions();
const { result } = renderHook(() => useSetState(new Set([1, 2, 3])));

expect(result.current[0]).toEqual(new Set([1, 2, 3]));
});

it("should add values to the set", () => {
expect.hasAssertions();
const { result } = renderHook(() => useSetState(new Set([1, 2, 3])));

act(() => {
result.current[1].add(4);
});
expect(result.current[0]).toEqual(new Set([1, 2, 3, 4]));
});

it("should delete values from the set", () => {
expect.hasAssertions();
const { result } = renderHook(() => useSetState(new Set([1, 2, 3])));

act(() => {
result.current[1].delete(2);
});

expect(result.current[0]).toEqual(new Set([1, 3]));
});

it("should clear the set", () => {
expect.hasAssertions();
const { result } = renderHook(() => useSetState(new Set([1, 2, 3])));

act(() => {
result.current[1].clear();
});
expect(result.current[0]).toEqual(new Set());
});
});
111 changes: 111 additions & 0 deletions packages/rooks/src/hooks/useArrayState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useCallback, useMemo, useState } from "react";

type Push<T> = (...args: Parameters<Array<T>["push"]>) => void;
type Pop = () => void;
type Unshift<T> = (...args: Parameters<Array<T>["unshift"]>) => void;
type Shift = () => void;
type Reverse = () => void;
type Concat<T> = (value: T[]) => void;
type Fill<T> = (value: T, start?: number, end?: number) => void;
type Clear = () => void;

export type UseArrayStateControls<T> = {
push: Push<T>;
pop: Pop;
clear: Clear;
unshift: Unshift<T>;
shift: Shift;
reverse: Reverse;
concat: Concat<T>;
fill: Fill<T>;
};

export type UseArrayStateReturnValue<T> = [T[], UseArrayStateControls<T>];

/**
* useArrayState
* @description Array state manager hook for React
* @param {Array<T>} initialState Initial state of the array
* @returns {UseArrayStateReturnValue<T>} Array state manager hook for React
* @see {@link https://react-hooks.org/docs/useArrayState}
*
* @example
*
* const [array, controls] = useArrayState([1, 2, 3]);
*
* controls.push(4); // [1, 2, 3, 4]
* controls.pop(); // [1, 2, 3]
* controls.unshift(0); // [0, 1, 2, 3]
* controls.shift(); // [1, 2, 3]
* controls.reverse(); // [3, 2, 1]
* controls.concat([4, 5, 6]); // [3, 2, 1, 4, 5, 6]
* controls.fill(0); // [0, 0, 0, 0, 0, 0]
*/
function useArrayState<T>(initialArray: T[] = []): UseArrayStateReturnValue<T> {
const [array, setArray] = useState(initialArray);

const push = useCallback<Push<T>>(
(value) => {
setArray([...array, value]);
},
[array]
);

const pop = useCallback<Pop>(() => {
setArray(array.slice(0, array.length - 1));
}, [array]);

const clear = useCallback<Clear>(() => {
setArray([]);
}, []);

const unshift = useCallback<Unshift<T>>(
(value) => {
setArray([value, ...array]);
},
[array]
);

const shift = useCallback<Shift>(() => {
setArray(array.slice(1));
}, [array]);

const reverse = useCallback<Reverse>(() => {
setArray([...array].reverse());
}, [array]);

const concat = useCallback<Concat<T>>(
(value: T[]) => {
setArray([...array, ...value]);
},
[array]
);

const fill = useCallback<Fill<T>>(
(value: T, start?: number, end?: number) => {
setArray([...array].fill(value, start, end));
},
[array]
);

const controls = useMemo<UseArrayStateControls<T>>(() => {
return {
push,
pop,
clear,
unshift,
shift,
reverse,
concat,
fill,
};
}, [push, pop, clear, unshift, shift, reverse, concat, fill]);

const returnValue = useMemo<UseArrayStateReturnValue<T>>(() => {
return [array, controls];
}, [array, controls]);

return returnValue;
}

export { useArrayState };
Loading

1 comment on commit 182e849

@vercel
Copy link

@vercel vercel bot commented on 182e849 Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.