arayaroma/ether/react-custom-hooks

Trust tier 3
  • community
  • ext:[{"type":"hook","description":"Pre/post lifecycle hooks"}]
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when extracting reusable stateful logic in React — toggle state, async data fetching with refetch, debounced values. Use before writing ad-hoc useState/useEffect combos that will likely be needed in more than one component.

Install

npx vskill@latest install arayaroma/ether/react-custom-hooks

Semi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard

SKILL.md

React custom hooks patterns

Toggle state

tsx
export function useToggle(initialValue = false): [boolean, () => void] {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle];
}

Async data fetching with a stable refetch

Trap: if refetch closes over fetcher/options directly, a caller passing an inline arrow function makes refetch a new reference every render, which re-triggers any useEffect([..., refetch]) — an infinite fetch loop. Fix: stash the latest fetcher/options in refs, keep refetch itself dependency-free.

tsx
export function useQuery<T>(key: string, fetcher: () => Promise<T>, options?: { onSuccess?: (d: T) => void; onError?: (e: Error) => void; enabled?: boolean }) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [loading, setLoading] = useState(false);
const fetcherRef = useRef(fetcher);
const optionsRef = useRef(options);
useEffect(() => {
fetcherRef.current = fetcher;
optionsRef.current = options;
});
const refetch = useCallback(async () => {
setLoading(true);
setError(null);
try {
const result = await fetcherRef.current();
setData(result);
optionsRef.current?.onSuccess?.(result);
} catch (err) {
const e = err as Error;
setError(e);
optionsRef.current?.onError?.(e);
} finally {
setLoading(false);
}
}, []);
const enabled = options?.enabled !== false;
useEffect(() => {
if (enabled) refetch();
}, [key, enabled, refetch]);
return { data, error, loading, refetch };
}

For real apps prefer SWR or React Query over hand-rolling this — write this pattern only when those libraries aren't available.

Debounce

tsx
export function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debounced;
}

Checklist before extracting a hook

  • Does the logic show up in 2+ components already, or is it clearly about to? If it's truly

one-off, leave it inline.

  • Any callback the hook returns and that callers put in a dependency array (refetch, toggle)

must be reference-stable (useCallback with an empty or minimal dep array) — otherwise callers get infinite-effect loops for free.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Extension points

  • hookPre/post lifecycle hooks

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/arayaroma/ether/react-custom-hooks/badge)](https://verified-skill.com/skills/arayaroma/ether/react-custom-hooks)

Something wrong with this skill or its page? Tell us.