React
The React skill provides expertise in React component patterns, hooks, context, and modern React best practices.
When Activated
Section titled “When Activated”- Building React components
- Using React hooks
- Component state management
- Working with
.jsxor.tsxfiles containing React
Core Patterns
Section titled “Core Patterns”Functional Components
Section titled “Functional Components”interface UserCardProps { user: User; onSelect?: (user: User) => void;}
export function UserCard({ user, onSelect }: UserCardProps) { return ( <div className="card" onClick={() => onSelect?.(user)}> <h3>{user.name}</h3> <p>{user.email}</p> </div> );}// useStateconst [count, setCount] = useState(0);
// useEffectuseEffect(() => { const subscription = subscribe(); return () => subscription.unsubscribe();}, [dependency]);
// useMemoconst expensive = useMemo(() => compute(data), [data]);
// useCallbackconst handleClick = useCallback(() => { doSomething(id);}, [id]);Custom Hooks
Section titled “Custom Hooks”function useUser(id: string) { const [user, setUser] = useState<User | null>(null); const [loading, setLoading] = useState(true);
useEffect(() => { setLoading(true); fetchUser(id) .then(setUser) .finally(() => setLoading(false)); }, [id]);
return { user, loading };}
// Usagefunction UserProfile({ userId }: { userId: string }) { const { user, loading } = useUser(userId);
if (loading) return <div>Loading...</div>; if (!user) return <div>User not found</div>;
return <div>{user.name}</div>;}Context Pattern
Section titled “Context Pattern”const UserContext = createContext<User | null>(null);
export function UserProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState<User | null>(null);
return ( <UserContext.Provider value={user}> {children} </UserContext.Provider> );}
export function useUser() { const context = useContext(UserContext); if (!context) throw new Error('useUser must be within UserProvider'); return context;}Best Practices
Section titled “Best Practices”- Keep components small and focused
- Use TypeScript for props
- Memoize expensive computations
- Clean up effects properly
- Lift state up when needed
Common Pitfalls
Section titled “Common Pitfalls”Missing Dependencies in Hooks
Section titled “Missing Dependencies in Hooks”// ❌ BAD: Missing dependencyuseEffect(() => { fetchData(userId);}, []); // userId not in dependencies
// ✅ GOOD: All dependencies includeduseEffect(() => { fetchData(userId);}, [userId]);State Updates on Unmounted Components
Section titled “State Updates on Unmounted Components”// ❌ BAD: No cleanupuseEffect(() => { fetchUser(id).then(setUser);}, [id]);
// ✅ GOOD: Cleanup functionuseEffect(() => { let cancelled = false;
fetchUser(id).then(user => { if (!cancelled) setUser(user); });
return () => { cancelled = true; };}, [id]);Prop Drilling
Section titled “Prop Drilling”// ❌ BAD: Passing props through many levels<App> <Layout user={user}> <Header user={user}> <UserMenu user={user} /> </Header> </Layout></App>
// ✅ GOOD: Use context<UserProvider value={user}> <App> <Layout> <Header> <UserMenu /> {/* Gets user from context */} </Header> </Layout> </App></UserProvider>Not Memoizing Callbacks
Section titled “Not Memoizing Callbacks”// ❌ BAD: New function every render<Child onUpdate={(data) => handleUpdate(id, data)} />
// ✅ GOOD: Memoized callbackconst handleUpdate = useCallback((data) => { updateData(id, data);}, [id]);
<Child onUpdate={handleUpdate} />Component Patterns
Section titled “Component Patterns”Compound Components
Section titled “Compound Components”function Tabs({ children }: { children: ReactNode }) { const [activeTab, setActiveTab] = useState(0);
return ( <TabsContext.Provider value={{ activeTab, setActiveTab }}> {children} </TabsContext.Provider> );}
Tabs.List = function TabsList({ children }: { children: ReactNode }) { return <div className="tabs-list">{children}</div>;};
Tabs.Tab = function Tab({ index, children }: { index: number; children: ReactNode }) { const { activeTab, setActiveTab } = useTabsContext(); return ( <button className={activeTab === index ? 'active' : ''} onClick={() => setActiveTab(index)} > {children} </button> );};
// Usage<Tabs> <Tabs.List> <Tabs.Tab index={0}>Tab 1</Tabs.Tab> <Tabs.Tab index={1}>Tab 2</Tabs.Tab> </Tabs.List></Tabs>Render Props
Section titled “Render Props”interface DataLoaderProps<T> { url: string; children: (data: T | null, loading: boolean) => ReactNode;}
function DataLoader<T>({ url, children }: DataLoaderProps<T>) { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState(true);
useEffect(() => { fetch(url) .then(res => res.json()) .then(setData) .finally(() => setLoading(false)); }, [url]);
return <>{children(data, loading)}</>;}
// Usage<DataLoader<User> url="/api/user"> {(user, loading) => ( loading ? <Spinner /> : <UserProfile user={user} /> )}</DataLoader>Integration with Frameworks
Section titled “Integration with Frameworks”With Next.js
Section titled “With Next.js”See Next.js skill for server components and App Router patterns.
With Testing
Section titled “With Testing”import { render, screen } from '@testing-library/react';import userEvent from '@testing-library/user-event';
it('should increment count on click', async () => { render(<Counter />);
const button = screen.getByRole('button', { name: /increment/i }); await userEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();});Related Skills
Section titled “Related Skills”- TypeScript - Type safety
- Next.js - Full-stack React
- Tailwind CSS - Styling
- vitest - Testing