Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/susisu/use-controlled-state
A utility for managing controlled local states
https://github.com/susisu/use-controlled-state
Last synced: 2 days ago
JSON representation
A utility for managing controlled local states
- Host: GitHub
- URL: https://github.com/susisu/use-controlled-state
- Owner: susisu
- License: mit
- Created: 2021-11-06T06:11:35.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2021-11-06T10:02:28.000Z (about 3 years ago)
- Last Synced: 2024-12-06T14:16:12.630Z (20 days ago)
- Language: TypeScript
- Homepage:
- Size: 74.2 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# @susisu/use-controlled-state
[![CI](https://github.com/susisu/use-controlled-state/workflows/CI/badge.svg)](https://github.com/susisu/use-controlled-state/actions?query=workflow%3ACI)
``` shell
npm i @susisu/use-controlled-state
# or
yarn add @susisu/use-controlled-state
```## Motivation
Suppose we have the following React component ([CodeSandbox](https://codesandbox.io/s/stupefied-chaum-p2n95?file=/src/App.tsx)).``` tsx
const NumberInput: React.VFC<{
value: number;
onChange: (value: number) => void;
}> = ({ value, onChange }) => {
return (
onChange(parseFloat(event.target.value))}
/>
);
};const MyForm: React.VFC = () => {
const [number, setNumber] = useState(0);
return ;
};
```Actually this component almost does not work. For example,
- `""` is parsed as `NaN` and then printed as `"NaN"`, so you cannot clear the input
- both `"0"` and `"0."` represents the same number `0`, so you cannot input like `"0.123"`There are roughly two ways for solving this problem:
- change the parent (`MyForm`) so that it manages the state as a `string`
- change the child (`NumberInput`) so that it manages an additional `string` state and synchronize with `value`Usually the latter is preferable, because it is completely local to the child input, and the parent does not need to know how the child behaves.
`@susisu/use-controlled-state` provides a utility for managing such local states. With this utility, the above component can be fixed like this ([CodeSandbox](https://codesandbox.io/s/elated-dan-lw913?file=/src/App.tsx)):
``` tsx
import { createUseControlledState } from "@susisu/use-controlled-state";// create a hook
const useControlledNumberString = createUseControlledState(
(value: number) => value.toString(),
(state: string) => parseFloat(state)
);const NumberInput: React.VFC<{
value: number;
onChange: (value: number) => void;
}> = ({ value, onChange }) => {
// use like useState
const [string, setString] = useControlledNumberString(value, onChange);
return (
setString(event.target.value)}
/>
);
};const MyForm: React.VFC = () => {
const [number, setNumber] = useState(0);
return ;
};
```## Usage
`createUseControlledState(convert, invert, options?)`- `convert`: The function that converts a parent value into a state.
- `invert` The function that converts a state into a value for the parent.
- `options.equalValue?` The equality function for values. Default is `Object.is`.
- `options.equalState?` The equality function for states. Default is `Object.is`.### Examples
`number` and `string```` ts
const useControlledNumberString = createUseControlledState(
(value: number) => value.toString(),
(state: string) => parseFloat(state)
);
````Date` and `string`
``` ts
function stringify(date: Date): string {
try {
return date.toISOString();
} catch {
return "";
}
}function parse(text: string): Date {
if (text === "") {
return new Date(NaN);
} else {
return new Date(text);
}
}function equal(a: Date, b: Date): boolean {
return Object.is(a.getTime(), b.getTime());
}const useControlledDateString = createUseControlledState(
stringify,
parse,
{ equalValue: equal }
);
```## License
[MIT License](http://opensource.org/licenses/mit-license.php)
## Author
Susisu ([GitHub](https://github.com/susisu), [Twitter](https://twitter.com/susisu2413))