diff --git a/packages/vkui/src/components/AppRoot/ScrollContext.tsx b/packages/vkui/src/components/AppRoot/ScrollContext.tsx index b7ca230642..48e9c10345 100644 --- a/packages/vkui/src/components/AppRoot/ScrollContext.tsx +++ b/packages/vkui/src/components/AppRoot/ScrollContext.tsx @@ -60,11 +60,8 @@ export const useScroll = (): ScrollContextInterface => React.useContext(ScrollCo * Если счетчик больше нуля, требуется заблокировать прокрутку */ function useScrollLockController(enableScrollLock: () => void, disableScrollLock: () => void) { - const { - count, - increment: incrementScrollLockCounter, - decrement: decrementScrollLockCounter, - } = useCounter(0); + const [count, { increment: incrementScrollLockCounter, decrement: decrementScrollLockCounter }] = + useCounter(0); const needLockScroll = count > 0; diff --git a/packages/vkui/src/hooks/useCounter.test.tsx b/packages/vkui/src/hooks/useCounter.test.tsx new file mode 100644 index 0000000000..9a2225005b --- /dev/null +++ b/packages/vkui/src/hooks/useCounter.test.tsx @@ -0,0 +1,77 @@ +import { act } from 'react'; +import { renderHook } from '@testing-library/react'; +import { useCounter } from './useCounter'; + +describe('useCounter', () => { + it('returns default value', () => { + const { result } = renderHook(useCounter); + expect(result.current[0]).toEqual(0); + }); + + it('returns passed value with initialProps number', () => { + const { result } = renderHook(useCounter, { + initialProps: 99, + }); + expect(result.current[0]).toEqual(99); + }); + it('returns passed value with initialProps function', () => { + const { result } = renderHook(useCounter, { + initialProps: () => 99, + }); + expect(result.current[0]).toEqual(99); + }); + + it('increment value', () => { + const { result } = renderHook(useCounter); + act(() => { + result.current[1].increment(); + result.current[1].increment(); + }); + expect(result.current[0]).toEqual(2); + }); + it('increment value with delta', () => { + const { result } = renderHook(useCounter); + act(() => { + result.current[1].increment(5); + result.current[1].increment(5); + }); + expect(result.current[0]).toEqual(10); + }); + + it('decrement value', () => { + const { result } = renderHook(useCounter); + act(() => { + result.current[1].decrement(); + result.current[1].decrement(); + }); + expect(result.current[0]).toEqual(-2); + }); + it('decrement value with delta', () => { + const { result } = renderHook(useCounter); + act(() => { + result.current[1].decrement(5); + result.current[1].decrement(5); + }); + expect(result.current[0]).toEqual(-10); + }); + + it('setCount value', () => { + const { result } = renderHook(useCounter); + act(() => { + result.current[1].setCount(5); + }); + expect(result.current[0]).toEqual(5); + }); + + it('actions not change after re-render', () => { + const { result } = renderHook(useCounter); + const firstRenderActions = result.current[1]; + + act(() => { + result.current[1].increment(); + }); + + expect(result.current[1]).toEqual(firstRenderActions); + expect(result.current[1].increment).toEqual(firstRenderActions.increment); + }); +}); diff --git a/packages/vkui/src/hooks/useCounter.ts b/packages/vkui/src/hooks/useCounter.ts index b5ed0bb72c..1e38750503 100644 --- a/packages/vkui/src/hooks/useCounter.ts +++ b/packages/vkui/src/hooks/useCounter.ts @@ -1,26 +1,61 @@ import * as React from 'react'; -type UseCounterReturn = { - count: number; - increment: () => void; - decrement: () => void; +type Actions = { + /** + * Увеличивает значение счетчика и вызывает ре-рендер + * + * @param delta Значение на которое требуется увеличить счетчик. По умолчанию `1` + */ + increment: (delta?: number) => void; + /** + * Уменьшает значение счетчика и вызывает ре-рендер + * + * @param delta Значение на которое требуется увеличить счетчик. По умолчанию `1` + */ + decrement: (delta?: number) => void; + + /** + * Функция, позволяющая обновить значение счетчика и вызвать ре-рендер + */ setCount: React.Dispatch>; }; /** - * Хук счетчика с возможностью задания начального значения и увеличения/уменьшения на 1. + * Хук счетчика с возможностью задания начального значения и увеличения/уменьшения значения. + * + * @param initialValue начальное значение счетчика. По умолчанию `0` + * + * ## Пример + * + * ```js + * const Component = () => { + * const [count, { increment, decrement, setCount }] = useCounter(0); + * + * return ( + *
+ *
count: {count}
+ * + * + * + * + * + * + *
+ * ); + * } + * ``` */ -export function useCounter(initialValue: number | (() => number) = 0): UseCounterReturn { +export function useCounter(initialValue: number | (() => number) = 0): [number, Actions] { const [count, setCount] = React.useState(initialValue); - const increment = React.useCallback(() => setCount((x) => x + 1), []); - - const decrement = React.useCallback(() => setCount((x) => x - 1), []); + const actions = React.useMemo( + () => ({ + increment: (delta = 1) => setCount((x) => x + delta), + decrement: (delta = 1) => setCount((x) => x - delta), + setCount, + }), + [], + ); - return { - count, - increment, - decrement, - setCount, - }; + return [count, actions]; }