react-component
Personal backup of Claude Code skills and plugins
5 stars0 forksUpdated Jan 26, 2026
npx skills add https://github.com/vapvarun/claude-backup --skill react-componentSKILL.md
React Component Development
Modern React patterns with TypeScript, hooks, and best practices.
Component Structure
Basic Component Template
import { useState, useCallback, memo } from 'react';
import type { FC, ReactNode } from 'react';
import styles from './Button.module.css';
interface ButtonProps {
/** Button content */
children: ReactNode;
/** Button variant style */
variant?: 'primary' | 'secondary' | 'danger';
/** Size of the button */
size?: 'sm' | 'md' | 'lg';
/** Whether the button is disabled */
disabled?: boolean;
/** Loading state */
loading?: boolean;
/** Click handler */
onClick?: () => void;
/** Additional CSS classes */
className?: string;
}
export const Button: FC<ButtonProps> = memo(({
children,
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
onClick,
className,
}) => {
const handleClick = useCallback(() => {
if (!disabled && !loading && onClick) {
onClick();
}
}, [disabled, loading, onClick]);
return (
<button
type="button"
className={`${styles.button} ${styles[variant]} ${styles[size]} ${className ?? ''}`}
disabled={disabled || loading}
onClick={handleClick}
aria-busy={loading}
>
{loading ? <Spinner size="sm" /> : children}
</button>
);
});
Button.displayName = 'Button';
Hooks Best Practices
useState
// BAD: Multiple related states
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
// GOOD: Group related state
interface FormData {
firstName: string;
lastName: string;
email: string;
}
const [formData, setFormData] = useState<FormData>({
firstName: '',
lastName: '',
email: '',
});
// Update single field
const updateField = (field: keyof FormData, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
useEffect
// BAD: Missing cleanup
useEffect(() => {
const subscription = api.subscribe(handler);
// Memory leak - no cleanup!
}, []);
// GOOD: Proper cleanup
useEffect(() => {
const subscription = api.subscribe(handler);
return () => subscription.unsubscribe();
}, [handler]);
// BAD: Stale closure
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // count is stale!
}, 1000);
return () => clearInterval(interval);
}, []); // Missing count dependency
// GOOD: Functional update
useEffect(() => {
const interval = setInterval(() => {
setCount(prev => prev + 1); // Always uses latest
}, 1000);
return () => clearInterval(interval);
}, []);
useCallback & useMemo
// useCallback - Memoize functions
const handleSubmit = useCallback(async (data: FormData) => {
await api.submit(data);
onSuccess();
}, [onSuccess]);
// useMemo - Memoize expensive computations
const sortedItems = useMemo(() => {
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// useMemo - Memoize objects/arrays passed as props
const config = useMemo(() => ({
theme: 'dark',
locale: 'en',
}), []); // Stable reference
// DON'T over-memoize simple values
// BAD
const doubled = useMemo(() => count * 2, [count]);
// GOOD - Simple math doesn't need memoization
const doubled = count * 2;
useRef
// DOM references
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
inputRef.current?.focus();
};
// Mutable values that don't trigger re-renders
const renderCount = useRef(0);
renderCount.current += 1;
// Previous value pattern
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
Custom Hooks
Data Fetching Hook
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = useCallback(async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(url);
if (!response.ok) throw new Error('Fetch failed');
const json = await response.json();
setData(json);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setLoading(false);
}
}, [url]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
}
Form Hook
interface UseFormOptions<T> {
initialValues: T;
validate?: (values: T) => Partial<Record<keyof T, string>>;
onSubmit: (values: T) => void | Promise<void>;
}
function useForm<T extends Record<string, any>>({
initialValues,
validate,
onSubmit,
}: UseFormOptions<T>) {
const [values
...
Repository
vapvarun/claude-backupParent repository
Repository Stats
Stars5
Forks0