feat(useCounter): reworked with use of new resolveHookState function plus improved memory usage;

feat(resolveHookState): improved types;
This commit is contained in:
xobotyi 2019-10-31 04:48:30 +03:00
parent 9b5d0f2ad1
commit befcf84c2c
3 changed files with 62 additions and 60 deletions

View File

@ -187,15 +187,15 @@ describe('should `console.error` on unexpected inputs', () => {
// @ts-ignore
act(() => inc(false));
expect(spy.mock.calls[0][0]).toBe('delta has to be a number, got boolean');
expect(spy.mock.calls[0][0]).toBe('delta has to be a number or function returning a number, got boolean');
// @ts-ignore
act(() => dec(false));
expect(spy.mock.calls[1][0]).toBe('delta has to be a number, got boolean');
expect(spy.mock.calls[1][0]).toBe('delta has to be a number or function returning a number, got boolean');
// @ts-ignore
act(() => reset({}));
expect(spy.mock.calls[2][0]).toBe('value has to be a number, got object');
expect(spy.mock.calls[2][0]).toBe('value has to be a number or function returning a number, got object');
spy.mockRestore();
});

View File

@ -1,85 +1,89 @@
import { useCallback } from 'react';
import { useMemo } from 'react';
import useGetSet from './useGetSet';
import { HookState, InitialHookState, resolveHookState } from './util/resolveHookState';
export interface CounterActions {
inc: (delta?: number) => void;
dec: (delta?: number) => void;
get: () => number;
set: (value: number) => void;
reset: (value?: number) => void;
set: (value: HookState<number>) => void;
reset: (value?: HookState<number>) => void;
}
export default function useCounter(
initialValue: number = 0,
initialValue: InitialHookState<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);
let init = resolveHookState(initialValue);
typeof init !== 'number' && console.error('initialValue has to be a number, got ' + typeof initialValue);
if (typeof min === 'number') {
initialValue = Math.max(initialValue, min);
init = Math.max(init, 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);
init = Math.min(init, max);
} else if (max !== null) {
console.error('max has to be a number, got ' + typeof max);
}
const [get, setInternal] = useGetSet<number>(initialValue);
const [get, setInternal] = useGetSet(init);
function set(value: number): void {
const current = get();
return [
get(),
useMemo(() => {
const set = (newState: HookState<number>) => {
const prevState = get();
let rState = resolveHookState(newState, prevState);
if (current === value) {
return;
}
if (prevState !== rState) {
if (typeof min === 'number') {
rState = Math.max(rState, min);
}
if (typeof max === 'number') {
rState = Math.min(rState, max);
}
if (typeof min === 'number') {
value = Math.max(value, min);
}
if (typeof max === 'number') {
value = Math.min(value, max);
}
prevState !== rState && setInternal(rState);
}
};
current !== value && setInternal(value);
}
return {
get,
set,
inc: (delta: HookState<number> = 1) => {
const rDelta = resolveHookState(delta, get());
const inc = useCallback(
(delta: number = 1) => {
typeof delta !== 'number' && console.error('delta has to be a number, got ' + typeof delta);
if (typeof rDelta !== 'number') {
console.error('delta has to be a number or function returning a number, got ' + typeof rDelta);
}
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((num: number) => num + rDelta);
},
dec: (delta: HookState<number> = 1) => {
const rDelta = resolveHookState(delta, get());
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);
if (typeof rDelta !== 'number') {
console.error('delta has to be a number or function returning a number, got ' + typeof rDelta);
}
initialValue = value;
set(value);
},
[max, min]
);
set((num: number) => num - rDelta);
},
reset: (value: HookState<number> = init) => {
const rValue = resolveHookState(value, get());
const actions = {
inc,
dec,
get,
set,
reset,
};
if (typeof rValue !== 'number') {
console.error('value has to be a number or function returning a number, got ' + typeof rValue);
}
return [get(), actions];
init = rValue;
set(rValue);
},
};
}, [min, max]),
];
}

View File

@ -5,14 +5,12 @@ export type InitialHookState<S> = S | InitialStateSetter<S>;
export type HookState<S> = S | StateSetter<S>;
export type ResolvableHookState<S> = S | StateSetter<S> | InitialStateSetter<S>;
export function resolveHookState<S>(newState: S | InitialStateSetter<S>): S;
export function resolveHookState<S>(newState: Exclude<HookState<any>, StateSetter<any>>, currentState: S): S;
// tslint:disable-next-line:unified-signatures
export function resolveHookState<S>(newState: StateSetter<S>, currentState: S): S;
export function resolveHookState<S>(newState: ResolvableHookState<S>, currentState?: S): S {
export function resolveHookState<S, C extends S>(newState: StateSetter<S>, currentState: C): S;
export function resolveHookState<S, C extends S>(newState: ResolvableHookState<S>, currentState?: C): S;
export function resolveHookState<S, C extends S>(newState: ResolvableHookState<S>, currentState?: C): S {
if (typeof newState === 'function') {
return (newState as Function)(currentState);
}
return newState as S;
return newState;
}