diff --git a/src/mantine-hooks/src/use-toggle/use-toggle.test.ts b/src/mantine-hooks/src/use-toggle/use-toggle.test.ts index 869073240f7..425f94910b6 100644 --- a/src/mantine-hooks/src/use-toggle/use-toggle.test.ts +++ b/src/mantine-hooks/src/use-toggle/use-toggle.test.ts @@ -17,6 +17,25 @@ describe('@mantine/hooks/use-toggle', () => { expect(hook.result.current[0]).toBe('dark'); }); + it('correctly toggles more than two values', () => { + const hook = renderHook(() => useToggle(['dark', 'light', 'normal'] as const)); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('light'); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('normal'); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('dark'); + + act(() => hook.result.current[1]('normal')); + expect(hook.result.current[0]).toBe('normal'); + + act(() => hook.result.current[1]()); + expect(hook.result.current[0]).toBe('dark'); + }); + it('allows to set value', () => { const hook = renderHook(() => useToggle(['dark', 'light'] as const)); diff --git a/src/mantine-hooks/src/use-toggle/use-toggle.ts b/src/mantine-hooks/src/use-toggle/use-toggle.ts index 1cb2c79a76e..3cfdf79c9d3 100644 --- a/src/mantine-hooks/src/use-toggle/use-toggle.ts +++ b/src/mantine-hooks/src/use-toggle/use-toggle.ts @@ -1,21 +1,12 @@ -import { useState } from 'react'; +import { useReducer } from 'react'; -export function useToggle(options: readonly [T, T] = [false, true] as any) { - const [state, setState] = useState(options[0]); +export function useToggle(options: readonly T[] = [false, true] as any) { + const [[option], toggle] = useReducer((state: T[], action: React.SetStateAction) => { + const value = action instanceof Function ? action(state[0]) : action; + const index = Math.abs(state.indexOf(value)); - const toggle = (value?: React.SetStateAction) => { - if (typeof value !== 'undefined') { - setState(value); - } else { - setState((current) => { - if (current === options[0]) { - return options[1]; - } + return state.slice(index).concat(state.slice(0, index)); + }, options as T[]); - return options[0]; - }); - } - }; - - return [state, toggle] as const; + return [option, toggle as (value?: React.SetStateAction) => void] as const; }