react
Core React patterns, hooks, state management, and best practices. Trigger: When working with React components, hooks, or state management.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
When to Use
- Creating or refactoring React components
- Implementing custom hooks
- Managing state (local, context, or global)
- Optimizing component performance
- Handling side effects
Critical Patterns
Functional Components (Always)
// Good: Functional component with hooks
export function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
if (!user) return <Loading />;
return <ProfileCard user={user} />;
}
// Bad: Class components (legacy)
class UserProfile extends React.Component { /* ... */ }
Component Organization
// Single component file structure
import { useState, useEffect, useCallback } from 'react';
// 1. Types/interfaces (if TypeScript)
// 2. Component
export function MyComponent({ prop1, prop2, onAction }) {
// 3. Hooks (state, refs, context, custom)
const [state, setState] = useState(null);
const ref = useRef(null);
// 4. Derived state / memos
const computed = useMemo(() => /* ... */, [dep]);
// 5. Callbacks
const handleClick = useCallback(() => {
onAction(state);
}, [state, onAction]);
// 6. Effects
useEffect(() => {
// setup
return () => { /* cleanup */ };
}, []);
// 7. Early returns (loading, error)
if (!state) return null;
// 8. Render
return <View />;
}
// 9. Styles (if colocated)
Hooks
useState
// Simple state
const [count, setCount] = useState(0);
// Object state (always spread)
const [form, setForm] = useState({ name: '', email: '' });
setForm(prev => ({ ...prev, name: 'John' }));
// Lazy initialization (expensive computation)
const [data, setData] = useState(() => computeExpensiveValue());
useEffect
// Mount only
useEffect(() => {
init();
}, []);
// With cleanup
useEffect(() => {
const subscription = subscribe();
return () => subscription.unsubscribe();
}, []);
// With dependencies
useEffect(() => {
fetchData(id);
}, [id]);
// NEVER: missing dependencies or lying about them
useEffect(() => {
doSomething(value); // ESLint will warn
}, []); // Bad: value is used but not listed
useCallback
// Memoize functions passed to children
const handlePress = useCallback(() => {
doSomething(id);
}, [id]);
// Without useCallback: new function every render = child re-renders
<ChildComponent onPress={() => doSomething(id)} /> // Bad
<ChildComponent onPress={handlePress} /> // Good
useMemo
// Expensive computations
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// Object references for dependencies
const config = useMemo(() => ({
threshold: 0.5,
maxItems: 10,
}), []);
useRef
// DOM/component references
const inputRef = useRef(null);
inputRef.current?.focus();
// Mutable values that don't trigger re-render
const renderCount = useRef(0);
renderCount.current += 1;
// Previous value pattern
const prevValue = useRef(value);
useEffect(() => {
prevValue.current = value;
}, [value]);
useContext
// Create context
const ThemeContext = createContext(null);
// Provider
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const value = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
// Custom hook (preferred over direct useContext)
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
Custom Hooks
Pattern
// hooks/useAsync.js
export function useAsync(asyncFn, deps = []) {
const [state, setState] = useState({
data: null,
loading: true,
error: null,
});
useEffect(() => {
setState(s => ({ ...s, loading: true }));
asyncFn()
.then(data => setState({ data, loading: false, error: null }))
.catch(error => setState({ data: null, loading: false, error }));
}, deps);
return state;
}
// Usage
const { data, loading, error } = useAsync(() => fetchUser(id), [id]);
Naming Convention
| Hook | Purpose |
|---|---|
useUser |
Fetch/manage user data |
useToggle |
Boolean toggle state |
useDebounce |
Debounced value |
useLocalStorage |
Persist to storage |
useVoiceCommands |
Domain-specific hook |
State Management Decision Tree
Is state used by one component only?
→ useState
Is state shared by parent-child?
→ Props + callbacks (lift state up)
Is state shared by siblings?
→ Lift to common parent, or Context
Is state global / deeply nested consumers?
→ Context + useReducer, or Zustand/Jotai
Is state server data?
→ React Query / TanStack Query
Performance
React.memo
// Wrap pure components that receive same props often
export const ExpensiveList = React.memo(function ExpensiveList({ items }) {
return items.map(item => <Item key={item.id} {...item} />);
});
// Custom comparison
export const Component = React.memo(MyComponent, (prevProps, nextProps) => {
return prevProps.id === nextProps.id;
});
Avoid Re-renders
// Bad: inline objects create new reference every render
<Component style={{ flex: 1 }} />
<Component config={{ theme: 'dark' }} />
// Good: stable references
const style = useMemo(() => ({ flex: 1 }), []);
const config = useMemo(() => ({ theme }), [theme]);
Code Splitting
// Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<Loading />}>
<HeavyChart />
</Suspense>
);
}
Anti-patterns
| Don't | Do | Why |
|---|---|---|
| Mutate state directly | Use setState with new object | React won't detect changes |
| useEffect for derived state | useMemo | Unnecessary render cycle |
| Fetch in useEffect without cleanup | Use abort controller or React Query | Race conditions |
| Index as key in dynamic lists | Unique stable ID | Causes bugs on reorder |
| Prop drilling 5+ levels | Context or composition | Hard to maintain |
| God components (500+ lines) | Split into smaller components | Unreadable |
File Naming
| Type | Convention | Example |
|---|---|---|
| Components | PascalCase | UserProfile.jsx |
| Hooks | camelCase with use prefix |
useAuth.js |
| Utils | camelCase | formatDate.js |
| Constants | UPPER_SNAKE or camelCase | API_URL or routes.js |
| Types | PascalCase | User.ts |
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-26
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!