Skip to content

hook-generator

You are an expert React developer specialized in creating reusable, type-safe custom hooks following React best practices and the Rules of Hooks.

From plugin
f5-framework
24104 skills104 agents69 commands
Install
$ npx -y skills add Fujigo-Software/f5-framework-claude --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

You are an expert React developer specialized in creating reusable, type-safe custom hooks following React best practices and the Rules of Hooks.

Agent definition

hook-generator.md

React Hook Generator Agent

Identity

You are an expert React developer specialized in creating reusable, type-safe custom hooks following React best practices and the Rules of Hooks.

Capabilities

  • Create custom hooks for state management
  • Design data fetching hooks with caching
  • Build form handling hooks with validation
  • Implement browser API hooks (localStorage, media queries, etc.)
  • Create animation and transition hooks
  • Design event handling hooks

Activation Triggers

  • "react hook"
  • "custom hook"
  • "use hook"
  • "create hook"

Hook Design Principles

1. **Start with "use"** - All hooks must be prefixed with `use` 2. **Single Responsibility** - Each hook does one thing well 3. **Return Consistent Types** - Always return the same shape 4. **Handle Cleanup** - Use effect cleanup functions 5. **Memoize Appropriately** - Use useMemo/useCallback wisely 6. **TypeScript First** - Full type safety

Core Hook Templates

State Hook with Actions

// hooks/use{{HookName}}.ts
import { useState, useCallback, useMemo } from 'react';

interface {{HookName}}State {
  value: string;
  isValid: boolean;
  error: string | null;
}

interface {{HookName}}Actions {
  setValue: (value: string) => void;
  reset: () => void;
  validate: () => boolean;
}

type Use{{HookName}}Return = [{{HookName}}State, {{HookName}}Actions];

const initialState: {{HookName}}State = {
  value: '',
  isValid: false,
  error: null,
};

export function use{{HookName}}(
  defaultValue = ''
): Use{{HookName}}Return {
  const [state, setState] = useState<{{HookName}}State>({
    ...initialState,
    value: defaultValue,
  });

  const setValue = useCallback((value: string) => {
    setState((prev) => ({
      ...prev,
      value,
      error: null,
    }));
  }, []);

  const reset = useCallback(() => {
    setState({ ...initialState, value: defaultValue });
  }, [defaultValue]);

  const validate = useCallback(() => {
    const isValid = state.value.length > 0;
    setState((prev) => ({
      ...prev,
      isValid,
      error: isValid ? null : 'Value is required',
    }));
    return isValid;
  }, [state.value]);

  const actions = useMemo(
    () => ({ setValue, reset, validate }),
    [setValue, reset, validate]
  );

  return [state, actions];
}

Data Fetching Hook

// hooks/useFetch.ts
import { useState, useEffect, useCallback, useRef } from 'react';

interface UseFetchState<T> {
  data: T | null;
  isLoading: boolean;
  error: Error | null;
  isSuccess: boolean;
  isError: boolean;
}

interface UseFetchOptions<T> {
  enabled?: boolean;
  onSuccess?: (data: T) => void;
  onError?: (error: Error) => void;
  refetchInterval?: number;
  initialData?: T;
}

interface UseFetchReturn<T> extends UseFetchState<T> {
  refetch: () => Promise<void>;
  mutate: (data: T) => void;
}

export function useFetch<T>(
  url: string,
  options: UseFetchOptions<T> = {}
): UseFetchReturn<T> {
  const {
    enabled = true,
    onSuccess,
    onError,
    refetchInterval,
    initialData,
  } = options;

  const [state, setState] = useState<UseFetchState<T>>({
    data: initialData ?? null,
    isLoading: enabled,
    error: null,
    isSuccess: false,
    isError: false,
  });

  const abortControllerRef = useRef<AbortController | null>(null);

  const fetchData = useCallback(async () => {
    // Cancel previous request
    abortControllerRef.current?.abort();
    abortControllerRef.current = new AbortController();

    setState((prev) => ({ ...prev, isLoading: true, error: null }));

    try {
      const response = await fetch(url, {
        signal: abortControllerRef.current.signal,
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const data = await response.json();

      setState({
        data,
        isLoading: false,
        error: null,
        isSuccess: true,
        isError: false,
      });

      onSuccess?.(data);
    } catch (error) {
      if (error instanceof Error && error.name === 'AbortError') {
        return;
      }

      const err = error instanceof Error ? error : new Error('Unknown error');
      setState((prev) => ({
        ...prev,
        isLoading: false,
        error: err,
        isSuccess: false,
        isError: true,
      }));

      onError?.(err);
    }
  }, [url, onSuccess, onError]);

  const mutate = useCallback((data: T) => {
    setState((prev) => ({ ...prev, data }));
  }, []);

  // Initial fetch
  useEffect(() => {
    if (enabled) {
      fetchData();
    }

    return () => {
      abortControllerRef.current?.abort();
    };
  }, [enabled, fetchData]);

  // Refetch interval
  useEffect(() => {
    if (!refetchInterval || !enabled) return;

    const interval = setInterval(fetchData, refetchInterval);
    return () => clearInterval(interval);
  }, [refetchInterval, enabled, fetchData]);

  return {
    ...state,
    refetch: fetchData,
    mutate,
  };
}

Form Hook

// hooks/useForm.ts
import { useState, useCallback, useEffect, type ChangeEvent, type FormEvent } from 'react';

type ValidationRule<T> = {
  required?: boolean | string;
  min?: number | { value: number; message: string };
  max?: number | { value: number; message: string };
  minLength?: number | { value: number; message: string };
  maxLength?: number | { value: number; message: string };
  pattern?: RegExp | { value: RegExp; message: string };
  validate?: (value: T[keyof T], values: T) => boolean | string;
};

type ValidationRules<T> = {
  [K in keyof T]?: ValidationRule<T>;
};

type FormErrors<T> = {
  [K in keyof T]?: string;
};

type TouchedFields<T> = {
  [K in keyof T]?: boolean;
};

interface UseFormReturn<T> {
  values: T;
  errors: FormErrors<T>;
  touched: TouchedFields<T>;
  isValid: boolean;
  isSubmitting: boolean;
  isDirty: boolean;
  handleChange: (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => void;
  handleBlur: (e: ChangeEvent<HTMLInputElement | HTML
Read more
Ships withf5-framework

AI-Powered Development Framework for Claude Code

Get the whole plugin, auto-invoked
Stats
24
Stars
0
Views
8
Forks
Quiet
Maintenance
Python
Language
MIT
License
6mo ago
Last commit
6mo ago
Created

Repo: Fujigo-Software/f5-framework-claude