mirror of
https://github.com/streamich/react-use.git
synced 2026-01-18 14:06:52 +00:00
feat: add min/max to useNumber
This commit is contained in:
commit
586faabe74
@ -127,7 +127,7 @@
|
||||
- [`useObservable`](./docs/useObservable.md) — tracks latest value of an `Observable`.
|
||||
- [`useSetState`](./docs/useSetState.md) — creates `setState` method which works like `this.setState`. [![][img-demo]](https://codesandbox.io/s/n75zqn1xp0)
|
||||
- [`useToggle` and `useBoolean`](./docs/useToggle.md) — tracks state of a boolean.
|
||||
- [`useCounter` and `useNumber`](./docs/useCounter.md) — tracks state of a number.
|
||||
- [`useCounter` and `useNumber`](./docs/useCounter.md) — tracks state of a number. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/state-usecounter--demo)
|
||||
- [`useList`](./docs/useList.md) — tracks state of an array.
|
||||
- [`useMap`](./docs/useMap.md) — tracks state of an object.
|
||||
|
||||
|
||||
@ -11,19 +11,50 @@ React state hook that tracks a numeric value.
|
||||
import {useCounter, useNumber} from 'react-use';
|
||||
|
||||
const Demo = () => {
|
||||
const [value, {inc, dec, get, set, reset}] = useCounter(5);
|
||||
const [min, { inc: incMin, dec: decMin }] = useCounter(1);
|
||||
const [max, { inc: incMax, dec: decMax }] = useCounter(10);
|
||||
const [value, { inc, dec, set, reset }] = useCounter(5, max, min);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>{value} is {get()}</div>
|
||||
<button onClick={() => inc()}>Increment</button>
|
||||
<button onClick={() => dec()}>Decrement</button>
|
||||
<button onClick={() => inc(5)}>Increment (+5)</button>
|
||||
<button onClick={() => dec(5)}>Decrement (-5)</button>
|
||||
<button onClick={() => set(100)}>Set 100</button>
|
||||
<button onClick={() => reset()}>Reset</button>
|
||||
<button onClick={() => reset(25)}>Reset (25)</button>
|
||||
<div>
|
||||
current: { value } [min: { min }; max: { max }]
|
||||
</div>
|
||||
|
||||
<br />
|
||||
Current value: <button onClick={ () => inc() }>Increment</button>
|
||||
<button onClick={ () => dec() }>Decrement</button>
|
||||
<button onClick={ () => inc(5) }>Increment (+5)</button>
|
||||
<button onClick={ () => dec(5) }>Decrement (-5)</button>
|
||||
<button onClick={ () => set(100) }>Set 100</button>
|
||||
<button onClick={ () => reset() }>Reset</button>
|
||||
<button onClick={ () => reset(25) }>Reset (25)</button>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
Min value:
|
||||
<button onClick={ () => incMin() }>Increment</button>
|
||||
<button onClick={ () => decMin() }>Decrement</button>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
Max value:
|
||||
<button onClick={ () => incMax() }>Increment</button>
|
||||
<button onClick={ () => decMax() }>Decrement</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Reference
|
||||
|
||||
```ts
|
||||
const [ current, { inc, dec, get, set, reset } ] = useCounter(initial: number, max: number | null = null, 20: number | null = null);
|
||||
```
|
||||
- `current` - current counter value;
|
||||
- `get(): number` - getter of current counter value;
|
||||
- `inc(delta: number): void` - increment current value;
|
||||
- `dec(delta: number): void` - decrement current value;
|
||||
- `set(value: number): void` - set arbitrary value;
|
||||
- `reset(value: number): void` - as the `set`, but also will assign value by reference to the `initial` parameter;
|
||||
|
||||
@ -4,20 +4,33 @@ import { useCounter } from '..';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
|
||||
const Demo = () => {
|
||||
const [value, { inc, dec, get, set, reset }] = useCounter(5);
|
||||
const [min, { inc: incMin, dec: decMin }] = useCounter(1);
|
||||
const [max, { inc: incMax, dec: decMax }] = useCounter(10);
|
||||
const [value, { inc, dec, set, reset }] = useCounter(5, max, min);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{value} is {get()}
|
||||
current: {value} [min: {min}; max: {max}]
|
||||
</div>
|
||||
<button onClick={() => inc()}>Increment</button>
|
||||
<br />
|
||||
Current value: <button onClick={() => inc()}>Increment</button>
|
||||
<button onClick={() => dec()}>Decrement</button>
|
||||
<button onClick={() => inc(5)}>Increment (+5)</button>
|
||||
<button onClick={() => dec(5)}>Decrement (-5)</button>
|
||||
<button onClick={() => set(100)}>Set 100</button>
|
||||
<button onClick={() => reset()}>Reset</button>
|
||||
<button onClick={() => reset(25)}>Reset (25)</button>
|
||||
<br />
|
||||
<br />
|
||||
Min value:
|
||||
<button onClick={() => incMin()}>Increment</button>
|
||||
<button onClick={() => decMin()}>Decrement</button>
|
||||
<br />
|
||||
<br />
|
||||
Max value:
|
||||
<button onClick={() => incMax()}>Increment</button>
|
||||
<button onClick={() => decMax()}>Decrement</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import useCounter from '../useCounter';
|
||||
|
||||
const setUp = (initialValue?: number) => renderHook(() => useCounter(initialValue));
|
||||
const setUp = (initialValue?: number, max: number | null = null, min: number | null = null) =>
|
||||
renderHook(() => useCounter(initialValue, max, min));
|
||||
|
||||
it('should init counter and utils', () => {
|
||||
const { result } = setUp(5);
|
||||
@ -121,8 +122,81 @@ it('should reset and set new original value', () => {
|
||||
expect(get()).toBe(8);
|
||||
});
|
||||
|
||||
it.todo('should log an error if initial value is other than a number');
|
||||
it.todo('should log an error if increment value is other than a number');
|
||||
it.todo('should log an error if increment value is a negative number');
|
||||
it.todo('should log an error if decrement value is other than a number');
|
||||
it.todo('should log an error if decrement value is a negative number');
|
||||
it('should not exceed max value', () => {
|
||||
const { result } = setUp(10, 5);
|
||||
expect(result.current[0]).toBe(5);
|
||||
|
||||
const { get, inc, reset } = result.current[1];
|
||||
|
||||
act(() => reset(10));
|
||||
expect(get()).toBe(5);
|
||||
|
||||
act(() => reset(4));
|
||||
expect(get()).toBe(4);
|
||||
|
||||
act(() => inc());
|
||||
expect(get()).toBe(5);
|
||||
|
||||
act(() => inc());
|
||||
expect(get()).toBe(5);
|
||||
});
|
||||
|
||||
it('should not exceed min value', () => {
|
||||
const { result } = setUp(3, null, 5);
|
||||
expect(result.current[0]).toBe(5);
|
||||
|
||||
const { get, dec, reset } = result.current[1];
|
||||
|
||||
act(() => reset(4));
|
||||
expect(get()).toBe(5);
|
||||
|
||||
act(() => reset(6));
|
||||
expect(get()).toBe(6);
|
||||
|
||||
act(() => dec());
|
||||
expect(get()).toBe(5);
|
||||
|
||||
act(() => dec());
|
||||
expect(get()).toBe(5);
|
||||
});
|
||||
|
||||
describe('should `console.error` on unexpected inputs', () => {
|
||||
it('on any of call parameters', () => {
|
||||
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
// @ts-ignore
|
||||
setUp(false);
|
||||
expect(spy.mock.calls[0][0]).toBe('initialValue has to be a number, got boolean');
|
||||
|
||||
// @ts-ignore
|
||||
setUp(10, false);
|
||||
expect(spy.mock.calls[1][0]).toBe('max has to be a number, got boolean');
|
||||
|
||||
// @ts-ignore
|
||||
setUp(10, 5, {});
|
||||
expect(spy.mock.calls[2][0]).toBe('min has to be a number, got object');
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('on any of returned methods has unexpected input', () => {
|
||||
const { result } = setUp(10);
|
||||
const { inc, dec, reset } = result.current[1];
|
||||
|
||||
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
// @ts-ignore
|
||||
act(() => inc(false));
|
||||
expect(spy.mock.calls[0][0]).toBe('delta has to be a number, got boolean');
|
||||
|
||||
// @ts-ignore
|
||||
act(() => dec(false));
|
||||
expect(spy.mock.calls[1][0]).toBe('delta has to be a number, got boolean');
|
||||
|
||||
// @ts-ignore
|
||||
act(() => reset({}));
|
||||
expect(spy.mock.calls[2][0]).toBe('value has to be a number, got object');
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,14 +9,70 @@ export interface CounterActions {
|
||||
reset: (value?: number) => void;
|
||||
}
|
||||
|
||||
const useCounter = (initialValue: number = 0): [number, CounterActions] => {
|
||||
const [get, set] = useGetSet<number>(initialValue);
|
||||
const inc = useCallback((delta: number = 1) => set(get() + delta), []);
|
||||
const dec = useCallback((delta: number = 1) => inc(-delta), []);
|
||||
const reset = useCallback((value: number = initialValue) => {
|
||||
initialValue = value;
|
||||
set(value);
|
||||
}, []);
|
||||
export default function useCounter(
|
||||
initialValue: number = 0,
|
||||
max: number | null = null,
|
||||
min: number | null = null
|
||||
): [number, CounterActions] {
|
||||
typeof initialValue !== 'number' && console.error('initialValue has to be a number, got ' + typeof initialValue);
|
||||
|
||||
if (typeof min === 'number') {
|
||||
initialValue = Math.max(initialValue, min);
|
||||
} else if (min !== null) {
|
||||
console.error('min has to be a number, got ' + typeof min);
|
||||
}
|
||||
|
||||
if (typeof max === 'number') {
|
||||
initialValue = Math.min(initialValue, max);
|
||||
} else if (max !== null) {
|
||||
console.error('max has to be a number, got ' + typeof max);
|
||||
}
|
||||
|
||||
const [get, setInternal] = useGetSet<number>(initialValue);
|
||||
|
||||
function set(value: number): void {
|
||||
const current = get();
|
||||
|
||||
if (current === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof min === 'number') {
|
||||
value = Math.max(value, min);
|
||||
}
|
||||
if (typeof max === 'number') {
|
||||
value = Math.min(value, max);
|
||||
}
|
||||
|
||||
current !== value && setInternal(value);
|
||||
}
|
||||
|
||||
const inc = useCallback(
|
||||
(delta: number = 1) => {
|
||||
typeof delta !== 'number' && console.error('delta has to be a number, got ' + typeof delta);
|
||||
|
||||
set(get() + delta);
|
||||
},
|
||||
[max, min]
|
||||
);
|
||||
const dec = useCallback(
|
||||
(delta: number = 1) => {
|
||||
typeof delta !== 'number' && console.error('delta has to be a number, got ' + typeof delta);
|
||||
|
||||
set(get() - delta);
|
||||
},
|
||||
[max, min]
|
||||
);
|
||||
const reset = useCallback(
|
||||
(value: number = initialValue) => {
|
||||
typeof value !== 'number' && console.error('value has to be a number, got ' + typeof value);
|
||||
|
||||
initialValue = value;
|
||||
set(value);
|
||||
},
|
||||
[max, min]
|
||||
);
|
||||
|
||||
const actions = {
|
||||
inc,
|
||||
dec,
|
||||
@ -26,6 +82,4 @@ const useCounter = (initialValue: number = 0): [number, CounterActions] => {
|
||||
};
|
||||
|
||||
return [get(), actions];
|
||||
};
|
||||
|
||||
export default useCounter;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user