diff --git a/docs/useStateList.md b/docs/useStateList.md index 21cf8f8d1b..c57af89280 100644 --- a/docs/useStateList.md +++ b/docs/useStateList.md @@ -1,7 +1,7 @@ # `useStateList` Provides handles for circular iteration over states list. -Supports forward and backward iterations and arbitrary position set. +Supports forward and backward iterations and arbitrary position set , and now supports initializing with default index. ## Usage @@ -10,9 +10,10 @@ import { useStateList } from 'react-use'; import { useRef } from 'react'; const stateSet = ['first', 'second', 'third', 'fourth', 'fifth']; +const defaultCurrentIndex = 2 ; // Start at 'third' const Demo = () => { - const { state, prev, next, setStateAt, setState, currentIndex, isFirst, isLast } = useStateList(stateSet); + const { state, prev, next, setStateAt, setState, currentIndex, isFirst, isLast } = useStateList(stateSet , defaultCurrentIndex); const indexInput = useRef(null); const stateInput = useRef(null); @@ -38,11 +39,18 @@ const Demo = () => { ## Reference ```ts -const { state, currentIndex, prev, next, setStateAt, setState, isFirst, isLast } = useStateList(stateSet: T[] = []); +const { state, currentIndex, prev, next, setStateAt, setState, isFirst, isLast } = useStateList(stateSet: T[] = [] , defaultCurrentIndex?: number); ``` +### Parameters + +- **`stateSet`**_`: T[]`_ — List of possible states. +- **`defaultCurrentIndex`**_`: number`_ (optional) — The index to start from. Defaults to `0`. If out of bounds, it will be clamped to the valid range. + If `stateSet` changed, became shorter than before and `currentIndex` left in shrunk gap - the last element of list will be taken as current. +### Returns + - **`state`**_`: T`_ — current state value; - **`currentIndex`**_`: number`_ — current state index; - **`prev()`**_`: void`_ — switches state to the previous one. If first element selected it will switch to the last one; diff --git a/src/useStateList.ts b/src/useStateList.ts index 748e317c8a..825f6e976b 100644 --- a/src/useStateList.ts +++ b/src/useStateList.ts @@ -14,12 +14,21 @@ export interface UseStateListReturn { isLast: boolean; } -export default function useStateList(stateSet: T[] = []): UseStateListReturn { +// Add defaultCurrentIndex parameter +export default function useStateList( + stateSet: T[] = [], + defaultCurrentIndex: number = 0 +): UseStateListReturn { const isMounted = useMountedState(); const update = useUpdate(); - const index = useRef(0); + // Initialize index with defaultCurrentIndex, clamp to valid range + const initialIndex = + stateSet.length === 0 + ? 0 + : Math.max(0, Math.min(defaultCurrentIndex, stateSet.length - 1)); + const index = useRef(initialIndex); - // If new state list is shorter that before - switch to the last element + // If new state list is shorter than before - switch to the last element useUpdateEffect(() => { if (stateSet.length <= index.current) { index.current = stateSet.length - 1; @@ -32,18 +41,9 @@ export default function useStateList(stateSet: T[] = []): UseStateListReturn< next: () => actions.setStateAt(index.current + 1), prev: () => actions.setStateAt(index.current - 1), setStateAt: (newIndex: number) => { - // do nothing on unmounted component if (!isMounted()) return; - - // do nothing on empty states list if (!stateSet.length) return; - - // in case new index is equal current - do nothing if (newIndex === index.current) return; - - // it gives the ability to travel through the left and right borders. - // 4ex: if list contains 5 elements, attempt to set index 9 will bring use to 5th element - // in case of negative index it will start counting from the right, so -17 will bring us to 4th element index.current = newIndex >= 0 ? newIndex % stateSet.length @@ -51,15 +51,13 @@ export default function useStateList(stateSet: T[] = []): UseStateListReturn< update(); }, setState: (state: T) => { - // do nothing on unmounted component if (!isMounted()) return; - const newIndex = stateSet.length ? stateSet.indexOf(state) : -1; - if (newIndex === -1) { - throw new Error(`State '${state}' is not a valid state (does not exist in state list)`); + throw new Error( + `State '${state}' is not a valid state (does not exist in state list)` + ); } - index.current = newIndex; update(); }, @@ -74,4 +72,4 @@ export default function useStateList(stateSet: T[] = []): UseStateListReturn< isLast: index.current === stateSet.length - 1, ...actions, }; -} +} \ No newline at end of file diff --git a/tests/useStateList.test.ts b/tests/useStateList.test.ts index 21e5641036..b6fa2f20a7 100644 --- a/tests/useStateList.test.ts +++ b/tests/useStateList.test.ts @@ -6,9 +6,19 @@ describe('useStateList', () => { expect(useStateList).toBeDefined(); }); - function getHook(list: any[] = ['a', 'b', 'c']) { - return renderHook(({ states }) => useStateList(states), { initialProps: { states: list } }); - } + function getHook(list: any[] = ['a', 'b', 'c'], defaultCurrentIndex?: number) { + return renderHook( + ({ states, index }) => + typeof index === 'number' + ? useStateList(states, index) + : useStateList(states), + { + initialProps: defaultCurrentIndex !== undefined + ? { states: list, index: defaultCurrentIndex } + : { states: list } + } + ); +} it('should return an object containing `state`, `next` and `prev`', () => { const res = getHook().result.current; @@ -40,6 +50,12 @@ describe('useStateList', () => { expect(hook.result.current.isLast).toBe(true); }); + it('should initialize with the provided default index', () => { + const hook = getHook(['a', 'b', 'c'], 1); + expect(hook.result.current.state).toBe('b'); + expect(hook.result.current.currentIndex).toBe(1); + }); + describe('setState()', () => { it('should set state value if it exists in states list', () => { const hook = getHook();