https://github.com/beenotung/use-state-proxy
Using Proxy API to auto dispatch React.useState(). Inspired from @State() in @stencil/core.
https://github.com/beenotung/use-state-proxy
proxy react react-hooks state-management typescript
Last synced: 4 months ago
JSON representation
Using Proxy API to auto dispatch React.useState(). Inspired from @State() in @stencil/core.
- Host: GitHub
- URL: https://github.com/beenotung/use-state-proxy
- Owner: beenotung
- License: bsd-2-clause
- Created: 2020-12-28T17:12:05.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2024-12-15T04:09:32.000Z (10 months ago)
- Last Synced: 2025-06-09T13:13:51.634Z (4 months ago)
- Topics: proxy, react, react-hooks, state-management, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/use-state-proxy
- Size: 294 KB
- Stars: 14
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# use-state-proxy
Using Proxy API to auto dispatch `React.useState()`.
Inspired from `@State()` in `@stencil/core`.[](https://www.npmjs.com/package/use-state-proxy)
## Installation
```bash
## using npm
npm install use-state-proxy## or using yarn
yarn add use-state-proxy## or using pnpm
pnpm install use-state-proxy
```## Typescript Signature
```typescript
type StateProxy = T// auto trigger re-render when in-place update occurs
export function useStateProxy(initialValue: T): StateProxy// return the object reference to the initialValue
export function unProxy(proxy: StateProxy): T
```## Features
- [x] Auto trigger re-render when invoking mutating methods on state fields
- [x] Array
- [x] Map
- [x] Set
- [x] Date
- [x] Object
- [x] Custom Classes
- [ ] Create a variant for shared state, as simpler alternative to redux store (using redux or context)
- [x] Tested with `@testing-library/jest-dom`## Comparison
### With use-state-proxy
You can get/set the values, and call mutating methods (e.g. `array.push()`) directly.
The 'setState' action is auto dispatched when the value is changed, which auto trigger re-rendering.
**Usage Example**:
```typescript jsx
import React from 'react'
import { useStateProxy } from 'use-state-proxy'function DemoUseStateProxy() {
const state = useStateProxy({
text: '',
list: ['init'],
})
const { list } = state
return (
<>
(state.text = e.target.value)} />
{
list.push(state.text)
state.text = ''
}}
>
Save
-
list.splice(i, 1)}>Delete
{item}
{
state.list[i] = e.target.value
state.list = state.list
}}
/>
{list.map((item, i) => (
))}
>
)
}
export default DemoUseStateProxy
```
Using `useStateProxy()`, the array can be updated with `state.list.push(state.text)` and `state.list.splice(i, 1)` directly.
This invokes proxied methods, and it will auto trigger re-rendering.
### Without use-state-proxy
You need to set up the states one-by-one, and explicitly call the setHooks to trigger re-rendering.
Moreover, there is syntax noise when updating complex data type, e.g. Array, Map, Set, and Object.
```typescript jsx
import React, { useState } from 'react'
function DemoUseState() {
const [text, setText] = useState('')
const [list, setList] = useState(['init'])
return (
<>
setText(e.target.value)} />
{
setList([...list, text])
setText('')
}}
>
Save
-
setList(list.filter((_, j) => i !== j))}>
Delete
{item}
{
const newList = [...list]
newList[i] = e.target.value
setList(newList)
}}
/>
{list.map((item, i) => (
))}
>
)
}
export default DemoUseState
```
In this example, in order to 'push' an item to the list, it manually destructs the original array with spread syntax `...` then append the new item at the end.
Also, to remove an item from the list, it constructs a new array with `list.filter()`, involving multiple levels of array indices, which is error-prone.
The same hurdles applies to object as well, and it gets even worse when it comes to `Set`\* and `Map`\*\*.
\*: To update a `Set`, we can run `setList(new Set([...list, item]))` or `setList(new Set([...list].filter(x => x !== target)))`
\*\*: To update a `Map`, we can run `setList(new Map([...list, [key, value]]))` or `setList(new Map([...list].filter(([key]) => key !== target)))`
## Register Mutating Methods on Custom Classes
The mutating methods of custom classes can be registered.
This mechanism allows use-state-proxy to auto trigger re-rendering even when the state consists of non-json values.
(Array, Map, Set and Date are supported by default.)
Below demo how to register mutating methods on WeakSet with `registerMutableMethodsByClassConstructor()` and `registerMutableMethodsByClassName()`:
```typescript
import {
registerMutableMethodsByClassConstructor,
registerMutableMethodsByClassName,
} from 'use-state-proxy'
let mutableWeakSetMethods: Array = [
'add',
'delete',
]
registerMutableMethodsByClassConstructor(WeakSet, methods)
registerMutableMethodsByClassName('[object WeakSet]', methods) // to support cross-frame objects
```
You can also use helper functions `getClassName()` and `registerPrimitiveMutableClass()` to avoid typo mistakes.
```typescript
import { getClassName, registerPrimitiveMutableClass } from 'use-state-proxy'
registerPrimitiveMutableClass(WeakSet, getClassName(new WeakSet()), [
'add',
'delete',
])
```
Details see [demo-custom-mutable-class.ts](./src/demo-custom-mutable-class.ts)
## Todo
- [ ] Refactor into typical react-library structure
https://medium.com/better-programming/build-your-very-own-react-component-library-and-publish-it-to-github-package-registry-192a688a51fd
## License
[BSD-2-Clause](./LICENSE) (Free Open Source Software)