Skip to content
Twilight React Playground
ثيم رائدaren

Hook result types

typeAdvanced

Shared shapes behind the action hooks: loading and error state, actions resolving to success, and the wishlist, like and coupon results.

import { AsyncState, AsyncStateWithValue, AsyncAction, AsyncActionWithParam, ToggleActions, ToggleActionsWithItem, ApplyActions, UseToggleWithItems, UseToggle, UseApply, FetchState, FetchStateWithRefresh, AsyncFnState } from '@salla.sa/twilight-theme-engine/hooks';

In plain words

Three engine hooks follow three patterns, and these types name them: a list you add items to and remove them from (the wishlist), one thing you switch on and off with a count (a blog like), and a value you apply or remove (a coupon). Each has loading, an error message, and actions that resolve true on success.

Use them to give your own hooks the same shape, so they feel familiar next to the engine's.

Signature

interface AsyncState { loading: boolean; error: string | null }
interface AsyncStateWithValue<T> extends AsyncState { value: T }

type AsyncAction = () => Promise<boolean>;
type AsyncActionWithParam<T> = (param: T) => Promise<boolean>;

interface ToggleActions { add: AsyncAction; remove: AsyncAction; toggle: AsyncAction }
interface ToggleActionsWithItem<T = number> {
  has: (item: T) => boolean;
  add: AsyncActionWithParam<T>; remove: AsyncActionWithParam<T>; toggle: AsyncActionWithParam<T>;
}
interface ApplyActions<T = string> { apply: AsyncActionWithParam<T>; remove: AsyncAction }

interface UseToggleWithItems<T = number> extends AsyncState, ToggleActionsWithItem<T> { ids: T[]; count: number }  // useWishlist
interface UseToggle extends AsyncState, ToggleActions { active: boolean; count: number }                        // useBlogLike
interface UseApply<T = string> extends AsyncState, ApplyActions<T> { value: T | null }                          // useCoupon

interface FetchState<T> { data: T; loading: boolean; error: Error | null }
interface FetchStateWithRefresh<T> extends FetchState<T> { refresh: () => Promise<void> }

// AsyncFnState<T>: useAsyncFn's generic AsyncState<T>, renamed in this barrel

Example

app/hooks/useSubmit.ts
import { useState } from 'react';
import type { AsyncActionWithParam, AsyncState } from '@salla.sa/twilight-theme-engine/hooks';

export interface UseSubmitResult extends AsyncState {
  submit: AsyncActionWithParam<string>;
}

/** The engine's action-hook shape around any async call of yours. */
export function useSubmit(send: (value: string) => Promise<void>): UseSubmitResult {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const submit = async (value: string) => {
    setLoading(true);
    setError(null);
    try {
      await send(value);
      return true;
    } catch (caught) {
      setError(caught instanceof Error ? caught.message : String(caught));
      return false;
    } finally {
      setLoading(false);
    }
  };

  return { loading, error, submit };
}

How it behaves

  • UseWishlistResult is UseToggleWithItems<number>, UseBlogLikeResult is UseToggle, and UseCouponResult is UseApply<string>.

  • All of them are reachable only through the @salla.sa/twilight-theme-engine/hooks barrel: hooks/types.ts has no subpath.

  • No engine hook returns AsyncStateWithValue, FetchState or FetchStateWithRefresh; engine data fetching uses TanStack Query instead.

Gotchas

  • Two different types share the name AsyncState. From @salla.sa/twilight-theme-engine/hooks it is this { loading; error: string | null }; from @salla.sa/twilight-theme-engine/hooks/useAsyncFn it is the generic AsyncState<T> whose error is an Error, which the barrel renames AsyncFnState. Check the import path when the types do not line up.

  • error is string | null in AsyncState but Error | null in FetchState.

Related

Source and docs