Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .husky/.gitignore

This file was deleted.

30 changes: 30 additions & 0 deletions .husky/_/husky.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh
if [ -z "$husky_skip_init" ]; then
debug () {
[ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
}

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

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

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

exit 0
fi
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run pre-commit
3 changes: 1 addition & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
src
__tests__
coverage

.husky
tsconfig.json
.travis.yml
.prettierrc

.eslintrc.js
rollup.config.js
yarn-error.log
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

## 1.0.0 (Soon)

#### New Feature
### New Feature

- added `useGroupState` hook
- added the `useGroupState` hook
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
[![NPM downloads](https://img.shields.io/npm/dm/react-group-state?style=flat-square)](https://www.npmjs.com/package/react-group-state)
[![NPM license](https://img.shields.io/npm/l/react-group-state?style=flat-square)](https://www.npmjs.com/package/react-group-state)
[![Codecov](https://img.shields.io/codecov/c/github/cool-hooks/react-group-state?style=flat-square)](https://codecov.io/gh/cool-hooks/react-group-state)
[![Travis](https://img.shields.io/travis/com/cool-hooks/react-group-state/main?style=flat-square)](https://travis-ci.com/cool-hooks/react-group-state)
[![Travis](https://img.shields.io/travis/com/cool-hooks/react-group-state/main?style=flat-square)](https://app.travis-ci.com/github/cool-hooks/react-group-state)
[![Bundle size](https://img.shields.io/bundlephobia/min/react-group-state?style=flat-square)](https://bundlephobia.com/result?p=react-group-state)

## About

Use state management style from React class components in function components
Use the state management style from React class components in function components

### Idea

[**setState**](https://reactjs.org/docs/react-component.html#setstate) in class components in React

### Demo

**[Playground – play with library in CodeSandbox](https://codesandbox.io/s/react-group-state-q4iss)**
**[Playground – play with the library in CodeSandbox](https://codesandbox.io/s/react-group-state-q4iss)**

### Alternatives

Expand All @@ -39,7 +39,7 @@ $ yarn add react-group-state

## Getting Started

• Import hook in React application file:
• Import a hook in a React application file:

```js
import { useGroupState } from 'react-group-state';
Expand All @@ -58,11 +58,11 @@ import { useGroupState } from 'react-group-state';
| Name | Type | Description |
| ------------ | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **state** | object | State |
| **setState** | (object or (prevState) => object, (newState) => void) | Function to set new state where **the first parameter** is new object or function with previous state that returns object and **second parameter** where value passed to function is updated state |
| **setState** | (object or (prevState) => object, (newState) => void) | A function to set a new state, where the **first parameter** is a new object or a function with the previous state, which returns the object and **the second parameter**, where the value passed to the function is the updated state |

## Example

**• Use `useGroupState` Hook:**
**• Use the `useGroupState` Hook:**

```jsx
import React from 'react';
Expand Down
4 changes: 4 additions & 0 deletions __tests__/useGroupState.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,8 @@ describe('useGroupState', () => {

expect(state).toBe(initialState);
});

it.todo('should extend default object structure');
it.todo('should strict');
it.todo('should overwrite schema');
});
45 changes: 21 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-group-state",
"version": "1.0.0-beta.3",
"description": "Use state management style from React class components in function components",
"description": "Use the state management style from React class components in function components",
"author": "Jakub Biesiada",
"license": "MIT",
"main": "lib/react-group-state.cjs.js",
Expand All @@ -17,7 +17,9 @@
"lint": "eslint 'src/**/*.ts' --fix",
"prettier": "prettier --write 'src/**/*.ts'",
"commit": "git-cz",
"semantic-release": "semantic-release"
"semantic-release": "semantic-release",
"prepare": "husky install",
"pre-commit": "lint-staged"
},
"repository": {
"type": "git",
Expand All @@ -35,32 +37,32 @@
},
"homepage": "https://github.com/cool-hooks/react-group-state#readme",
"devDependencies": {
"@rollup/plugin-node-resolve": "^13.0.0",
"@semantic-release/git": "^9.0.0",
"@testing-library/react-hooks": "^7.0.0",
"@types/jest": "^26.0.23",
"@types/react": "^17.0.8",
"@typescript-eslint/parser": "^4.25.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@semantic-release/git": "^10.0.0",
"@testing-library/react-hooks": "^7.0.2",
"@types/jest": "^27.0.2",
"@types/react": "^17.0.27",
"@typescript-eslint/parser": "^4.33.0",
"cz-conventional-changelog": "3.3.0",
"cz-emoji": "^1.3.1",
"eslint": "^7.27.0",
"eslint": "^8.0.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.26.1",
"eslint-plugin-react-hooks": "^4.2.0",
"husky": "^6.0.0",
"jest": "^26.6.3",
"lint-staged": "^11.0.0",
"prettier": "^2.3.0",
"husky": "^7.0.2",
"jest": "^27.2.5",
"lint-staged": "^11.2.2",
"prettier": "^2.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-test-renderer": "^17.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.30.0",
"semantic-release": "^17.4.3",
"semantic-release-gitmoji": "^1.3.4",
"ts-jest": "^26.5.4",
"typescript": "^4.3.2"
"semantic-release": "^18.0.0",
"semantic-release-gitmoji": "^1.4.2",
"ts-jest": "^27.0.5",
"typescript": "^4.4.3"
},
"peerDependencies": {
"react": ">=16.8.0"
Expand All @@ -75,11 +77,6 @@
"^.+\\.tsx?$": "ts-jest"
}
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.ts": [
"npm run prettier",
Expand Down
35 changes: 26 additions & 9 deletions src/useGroupState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,59 @@ type UpdateStateCallback<T> = (
) => void;

const defaultOptions = {
extendable: false, // new fields
strict: true, // other types for field
extendable: true, // new fields
strict: false, // other types for field
overwrite: false,
// fallback: undefined, // -> fallback for overwrite
};

export function useGroupState<T extends object>(
group: T,
options?: typeof defaultOptions
) {
// TODO add logic for options
const { extendable, strict } = {
const { extendable, strict, overwrite } = {
...defaultOptions,
...options,
};

const [state, setState] = useState(group);

const updateState = useCallback<UpdateStateCallback<T>>(
(data, callback) => {
(data, callback, _options) => {
const updatedState = state; // TODO? replace with prev state

const mergeState = (data: Partial<T>) => {
(Object.keys(data) as Array<keyof T>).map((key) => {
if (Object.prototype.hasOwnProperty.call(updatedState, key)) {
updatedState[key] = data[key] as T[keyof T];
if (
extendable ||
(!extendable &&
Object.prototype.hasOwnProperty.call(updatedState, key))
) {
// TODO compare objects/arrays/etc
if (
!strict ||
(strict && typeof updatedState[key] === typeof data[key])
) {
updatedState[key] = data[key] as T[keyof T];
}
}
});

setState((prevState) => ({ ...prevState, ...updatedState }));
// setState({ ...updatedState });
setState((prevState) => {
if (overwrite) {
return data;
}

return { ...prevState, ...updatedState };
});

callback?.(updatedState);
};

mergeState(typeof data === 'function' ? data(state) : data);
},
[state]
[extendable, overwrite, state, strict]
);

return [state, updateState] as const;
Expand Down