Skip to content

refactor(useBooleanState, useStorageState, useCounter): return the value with an actions object - #532

Merged
mnxmnz merged 3 commits into
v1-basefrom
refactor/state-hook-returns
Oct 8, 2026
Merged

mnxmnz merged 3 commits into
v1-basefrom
refactor/state-hook-returns

Conversation

@mnxmnz

@mnxmnz mnxmnz commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Overview

useBooleanState, useStorageState and useCounter now return [value, actions], the same shape useList, useMap and useSet already use. Closes #528.

This replaces the first draft, which returned a flat object. With a tuple, callers name the value per call site, and two useBooleanState calls in one component no longer collide on setTrue / toggle. Most libraries that ship a multi-action boolean hook use the same shape: ahooks useBoolean, mantine useDisclosure, chakra v2 useBoolean.

// Before
const [isOpen, setTrue, setFalse, toggle] = useBooleanState(false);
const [token, setToken, refreshToken] = useStorageState<string>('token');
const { count, increment, decrement, reset, setCount } = useCounter(0);

// After
const [isOpen, { setTrue, setFalse, toggle }] = useBooleanState(false);
const [token, { setValue, refresh }] = useStorageState<string>('token');
const [count, { increment, decrement, reset, setCount }] = useCounter(0);
  • The actions object is memoized, so it keeps the same reference across renders. A test covers this for each hook.
  • useToggle keeps [value, toggle] and now declares its return type, so React 18 consumers no longer see the React 19 ActionDispatch type. Deprecating it in favor of useBooleanState will follow once the deprecated-export tooling lands.
  • The return value convention in CLAUDE.md and AGENTS.md now reads: one action → [value, action], two or more → [value, { actions }].
  • Tests, docs in all five locales and the skill references are updated, with a major changeset.
  • yarn test, yarn test:coverage, yarn test:compiled, yarn test:docs and yarn test:skill pass.

Checklist

  • Did you write the test code?
  • Have you run yarn run fix to format and lint the code and docs?
  • Have you run yarn run test:coverage to make sure there is no uncovered line?
  • Did you write the JSDoc?

@mnxmnz mnxmnz self-assigned this Sep 28, 2026
@changeset-bot

changeset-bot Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a403c7e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
react-simplikit Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Size Change: +266 B (+0.23%)

Total Size: 115 kB

📦 View Changed
Filename Size Change
packages/react-simplikit/dist/hooks/useBooleanState/useBooleanState.cjs 594 B +48 B (+8.79%) 🔍
packages/react-simplikit/dist/hooks/useBooleanState/useBooleanState.mjs 586 B +50 B (+9.33%) 🔍
packages/react-simplikit/dist/hooks/useCounter/useCounter.cjs 1.08 kB +51 B (+4.97%) 🔍
packages/react-simplikit/dist/hooks/useCounter/useCounter.mjs 1.06 kB +53 B (+5.26%) 🔍
packages/react-simplikit/dist/hooks/useStorageState/useStorageState.cjs 1 kB +31 B (+3.19%)
packages/react-simplikit/dist/hooks/useStorageState/useStorageState.mjs 997 B +33 B (+3.42%)
ℹ️ View Unchanged
Filename Size
packages/codemod/dist/cli.mjs 6.47 kB
packages/react-simplikit/dist/components/ImpressionArea/ImpressionArea.cjs 1.01 kB
packages/react-simplikit/dist/components/ImpressionArea/ImpressionArea.mjs 984 B
packages/react-simplikit/dist/components/Separated/Separated.cjs 686 B
packages/react-simplikit/dist/components/Separated/Separated.mjs 684 B
packages/react-simplikit/dist/components/SwitchCase/SwitchCase.cjs 723 B
packages/react-simplikit/dist/components/SwitchCase/SwitchCase.mjs 721 B
packages/react-simplikit/dist/hooks/useAsyncEffect/useAsyncEffect.cjs 625 B
packages/react-simplikit/dist/hooks/useAsyncEffect/useAsyncEffect.mjs 614 B
packages/react-simplikit/dist/hooks/useAvoidKeyboard/useAvoidKeyboard.cjs 985 B
packages/react-simplikit/dist/hooks/useAvoidKeyboard/useAvoidKeyboard.mjs 963 B
packages/react-simplikit/dist/hooks/useBodyScrollLock/useBodyScrollLock.cjs 887 B
packages/react-simplikit/dist/hooks/useBodyScrollLock/useBodyScrollLock.mjs 863 B
packages/react-simplikit/dist/hooks/useCallbackOncePerRender/useCallbackOncePerRender.cjs 815 B
packages/react-simplikit/dist/hooks/useCallbackOncePerRender/useCallbackOncePerRender.mjs 786 B
packages/react-simplikit/dist/hooks/useConditionalEffect/useConditionalEffect.cjs 980 B
packages/react-simplikit/dist/hooks/useConditionalEffect/useConditionalEffect.mjs 959 B
packages/react-simplikit/dist/hooks/useControlledState/useControlledState.cjs 1.04 kB
packages/react-simplikit/dist/hooks/useControlledState/useControlledState.mjs 1.03 kB
packages/react-simplikit/dist/hooks/useDebounce/debounce.cjs 463 B
packages/react-simplikit/dist/hooks/useDebounce/debounce.mjs 462 B
packages/react-simplikit/dist/hooks/useDebounce/useDebounce.cjs 1.28 kB
packages/react-simplikit/dist/hooks/useDebounce/useDebounce.mjs 1.26 kB
packages/react-simplikit/dist/hooks/useDebouncedCallback/useDebouncedCallback.cjs 1.15 kB
packages/react-simplikit/dist/hooks/useDebouncedCallback/useDebouncedCallback.mjs 1.12 kB
packages/react-simplikit/dist/hooks/useDebouncedValue/useDebouncedValue.cjs 1.21 kB
packages/react-simplikit/dist/hooks/useDebouncedValue/useDebouncedValue.mjs 1.18 kB
packages/react-simplikit/dist/hooks/useDoubleClick/useDoubleClick.cjs 970 B
packages/react-simplikit/dist/hooks/useDoubleClick/useDoubleClick.mjs 954 B
packages/react-simplikit/dist/hooks/useGeolocation/useGeolocation.cjs 2.02 kB
packages/react-simplikit/dist/hooks/useGeolocation/useGeolocation.mjs 2.02 kB
packages/react-simplikit/dist/hooks/useImpressionRef/useImpressionRef.cjs 1.25 kB
packages/react-simplikit/dist/hooks/useImpressionRef/useImpressionRef.mjs 1.22 kB
packages/react-simplikit/dist/hooks/useInputState/useInputState.cjs 672 B
packages/react-simplikit/dist/hooks/useInputState/useInputState.mjs 662 B
packages/react-simplikit/dist/hooks/useIntersectionObserver/useIntersectionObserver.cjs 1.1 kB
packages/react-simplikit/dist/hooks/useIntersectionObserver/useIntersectionObserver.mjs 1.07 kB
packages/react-simplikit/dist/hooks/useInterval/useInterval.cjs 837 B
packages/react-simplikit/dist/hooks/useInterval/useInterval.mjs 811 B
packages/react-simplikit/dist/hooks/useIsClient/useIsClient.cjs 585 B
packages/react-simplikit/dist/hooks/useIsClient/useIsClient.mjs 574 B
packages/react-simplikit/dist/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.cjs 577 B
packages/react-simplikit/dist/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.mjs 575 B
packages/react-simplikit/dist/hooks/useKeyboardHeight/useKeyboardHeight.cjs 736 B
packages/react-simplikit/dist/hooks/useKeyboardHeight/useKeyboardHeight.mjs 717 B
packages/react-simplikit/dist/hooks/useList/useList.cjs 955 B
packages/react-simplikit/dist/hooks/useList/useList.mjs 933 B
packages/react-simplikit/dist/hooks/useLoading/useLoading.cjs 919 B
packages/react-simplikit/dist/hooks/useLoading/useLoading.mjs 909 B
packages/react-simplikit/dist/hooks/useLongPress/useLongPress.cjs 1.71 kB
packages/react-simplikit/dist/hooks/useLongPress/useLongPress.mjs 1.68 kB
packages/react-simplikit/dist/hooks/useMap/useMap.cjs 762 B
packages/react-simplikit/dist/hooks/useMap/useMap.mjs 744 B
packages/react-simplikit/dist/hooks/useNetworkStatus/useNetworkStatus.cjs 1.61 kB
packages/react-simplikit/dist/hooks/useNetworkStatus/useNetworkStatus.mjs 1.59 kB
packages/react-simplikit/dist/hooks/useOutsideClickEffect/useOutsideClickEffect.cjs 785 B
packages/react-simplikit/dist/hooks/useOutsideClickEffect/useOutsideClickEffect.mjs 755 B
packages/react-simplikit/dist/hooks/usePageVisibility/usePageVisibility.cjs 1.28 kB
packages/react-simplikit/dist/hooks/usePageVisibility/usePageVisibility.mjs 1.25 kB
packages/react-simplikit/dist/hooks/usePreservedCallback/usePreservedCallback.cjs 751 B
packages/react-simplikit/dist/hooks/usePreservedCallback/usePreservedCallback.mjs 733 B
packages/react-simplikit/dist/hooks/usePreservedReference/usePreservedReference.cjs 805 B
packages/react-simplikit/dist/hooks/usePreservedReference/usePreservedReference.mjs 789 B
packages/react-simplikit/dist/hooks/usePrevious/usePrevious.cjs 640 B
packages/react-simplikit/dist/hooks/usePrevious/usePrevious.mjs 631 B
packages/react-simplikit/dist/hooks/useRefEffect/useRefEffect.cjs 836 B
packages/react-simplikit/dist/hooks/useRefEffect/useRefEffect.mjs 820 B
packages/react-simplikit/dist/hooks/useSafeAreaInset/useSafeAreaInset.cjs 942 B
packages/react-simplikit/dist/hooks/useSafeAreaInset/useSafeAreaInset.mjs 927 B
packages/react-simplikit/dist/hooks/useScrollDirection/useScrollDirection.cjs 1.34 kB
packages/react-simplikit/dist/hooks/useScrollDirection/useScrollDirection.mjs 1.33 kB
packages/react-simplikit/dist/hooks/useSet/useSet.cjs 1.03 kB
packages/react-simplikit/dist/hooks/useSet/useSet.mjs 1 kB
packages/react-simplikit/dist/hooks/useStorageState/storage.cjs 543 B
packages/react-simplikit/dist/hooks/useStorageState/storage.mjs 527 B
packages/react-simplikit/dist/hooks/useThrottle/throttle.cjs 431 B
packages/react-simplikit/dist/hooks/useThrottle/throttle.mjs 422 B
packages/react-simplikit/dist/hooks/useThrottle/useThrottle.cjs 894 B
packages/react-simplikit/dist/hooks/useThrottle/useThrottle.mjs 871 B
packages/react-simplikit/dist/hooks/useThrottledCallback/useThrottledCallback.cjs 1.03 kB
packages/react-simplikit/dist/hooks/useThrottledCallback/useThrottledCallback.mjs 1 kB
packages/react-simplikit/dist/hooks/useThrottledValue/useThrottledValue.cjs 1.1 kB
packages/react-simplikit/dist/hooks/useThrottledValue/useThrottledValue.mjs 1.07 kB
packages/react-simplikit/dist/hooks/useTimeout/useTimeout.cjs 627 B
packages/react-simplikit/dist/hooks/useTimeout/useTimeout.mjs 600 B
packages/react-simplikit/dist/hooks/useToggle/useToggle.cjs 524 B
packages/react-simplikit/dist/hooks/useToggle/useToggle.mjs 506 B
packages/react-simplikit/dist/hooks/useVisibilityEvent/useVisibilityEvent.cjs 692 B
packages/react-simplikit/dist/hooks/useVisibilityEvent/useVisibilityEvent.mjs 674 B
packages/react-simplikit/dist/hooks/useVisualViewport/useVisualViewport.cjs 1.54 kB
packages/react-simplikit/dist/hooks/useVisualViewport/useVisualViewport.mjs 1.53 kB
packages/react-simplikit/dist/index.cjs 1.39 kB
packages/react-simplikit/dist/index.mjs 1.01 kB
packages/react-simplikit/dist/utils/buildContext/buildContext.cjs 864 B
packages/react-simplikit/dist/utils/buildContext/buildContext.mjs 844 B
packages/react-simplikit/dist/utils/disableBodyScrollLock/disableBodyScrollLock.cjs 667 B
packages/react-simplikit/dist/utils/disableBodyScrollLock/disableBodyScrollLock.mjs 660 B
packages/react-simplikit/dist/utils/enableBodyScrollLock/enableBodyScrollLock.cjs 663 B
packages/react-simplikit/dist/utils/enableBodyScrollLock/enableBodyScrollLock.mjs 652 B
packages/react-simplikit/dist/utils/getKeyboardHeight/getKeyboardHeight.cjs 609 B
packages/react-simplikit/dist/utils/getKeyboardHeight/getKeyboardHeight.mjs 602 B
packages/react-simplikit/dist/utils/getSafeAreaInset/getSafeAreaInset.cjs 902 B
packages/react-simplikit/dist/utils/getSafeAreaInset/getSafeAreaInset.mjs 893 B
packages/react-simplikit/dist/utils/isAndroid/isAndroid.cjs 543 B
packages/react-simplikit/dist/utils/isAndroid/isAndroid.mjs 534 B
packages/react-simplikit/dist/utils/isIOS/isIOS.cjs 753 B
packages/react-simplikit/dist/utils/isIOS/isIOS.mjs 746 B
packages/react-simplikit/dist/utils/isKeyboardVisible/isKeyboardVisible.cjs 431 B
packages/react-simplikit/dist/utils/isKeyboardVisible/isKeyboardVisible.mjs 419 B
packages/react-simplikit/dist/utils/isServer/isServer.cjs 381 B
packages/react-simplikit/dist/utils/isServer/isServer.mjs 379 B
packages/react-simplikit/dist/utils/mergeProps/mergeProps.cjs 645 B
packages/react-simplikit/dist/utils/mergeProps/mergeProps.mjs 643 B
packages/react-simplikit/dist/utils/mergeRefs/mergeRefs.cjs 790 B
packages/react-simplikit/dist/utils/mergeRefs/mergeRefs.mjs 787 B
packages/react-simplikit/dist/utils/subscribeKeyboardHeight/subscribeKeyboardHeight.cjs 1.12 kB
packages/react-simplikit/dist/utils/subscribeKeyboardHeight/subscribeKeyboardHeight.mjs 1.1 kB

compressed-size-action

@codecov-commenter

codecov-commenter commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (15aea8a) to head (a403c7e).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           v1-base      #532   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           69        69           
  Lines         2280      2281    +1     
  Branches       735       738    +3     
=========================================
+ Hits          2280      2281    +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hyesungoh

Copy link
Copy Markdown
Member
// before
const [value, setTrue, setFalse, toggle] = useBooleanState(false);

const [value, setValue, refresh] = useStorageState<string>('token');

const [list, { push, insertAt, updateAt, removeAt, setAll, reset }] = useList<string>(['a', 'b']);

const [map, { set, setAll, remove, reset }] = useMap<string, number>([['a', 1]]);

const [set, { add, remove, toggle, setAll, reset }] = useSet<string>(['a']);

// after (this pr now)
const { value, setTrue, setFalse, toggle } = useBooleanState(false);

const { value, setValue, refresh } = useStorageState<string>('token');

const { list, push, insertAt, updateAt, removeAt, setAll, reset } = useList<string>(['a', 'b']);

const { map, set, setAll, remove, reset } = useMap<string, number>([['a', 1]]);

const { set, add, remove, toggle, setAll, reset } = useSet<string>(['a']);

// or this ??? 

const [isOpen, { setTrue, setFalse, toggle }] = useBooleanState(false);
const [token, { setValue, refresh }] = useStorageState<string>('token');

const [list, { push, insertAt, updateAt, removeAt, setAll, reset }] = useList<string>(['a', 'b']);   // same as before
const [map, { set, setAll, remove, reset }] = useMap<string, number>([['a', 1]]);                  // same as before
const [set, { add, remove, toggle, setAll, reset }] = useSet<string>(['a']);                       // same as before

@mnxmnz
mnxmnz force-pushed the refactor/state-hook-returns branch from 17f6d47 to 1425afb Compare October 6, 2026 14:40
@mnxmnz mnxmnz changed the title refactor: standardize state hook return values refactor(useBooleanState, useStorageState, useCounter): return the value with an actions object Oct 6, 2026
@mnxmnz

mnxmnz commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

@hyesungoh Went with [value, actions]! 🙌 Each call site can name its own value, and useList / useMap / useSet stay as they are!

const [isOpen, { setTrue, setFalse, toggle }] = useBooleanState(false);
const [token, { setValue, refresh }] = useStorageState<string>('token');
const [count, { increment, decrement, reset, setCount }] = useCounter(0);

useCounter now follows the same shape too useToggle is unchanged for now, but since it's just const [on, { toggle }] = useBooleanState(), how about deprecating it once your deprecated-export tooling lands? 🤔

@mnxmnz
mnxmnz marked this pull request as ready for review October 6, 2026 15:02
@mnxmnz
mnxmnz requested a review from hyesungoh as a code owner October 6, 2026 15:02

@hyesungoh hyesungoh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCounter looks good to me! 👍

Not sure about deprecating useToggle though 🤔 Let's leave it as is for now and talk about it later.

@mnxmnz
mnxmnz merged commit 78788e2 into v1-base Oct 8, 2026
19 checks passed
@mnxmnz
mnxmnz deleted the refactor/state-hook-returns branch October 8, 2026 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Group actions in state hook return values

3 participants