react-patterns
SKILL.md
React Patterns
Modern React patterns for production applications.
Hooks
Custom hooks — extract reusable logic
// useFetch hook
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
return () => controller.abort();
}, [url]);
return { data, error, loading };
}
useCallback/useMemo — prevent unnecessary re-renders
// Memoize expensive computation
const sorted = useMemo(() => items.sort((a, b) => a.name.localeCompare(b.name)), [items]);
// Stable function reference for child components
const handleClick = useCallback((id: string) => {
setSelected(id);
}, []);
Composition Over Inheritance
// Compound component pattern
function Select({ children, value, onChange }) {
return (
<SelectContext.Provider value={{ value, onChange }}>
<div role="listbox">{children}</div>
</SelectContext.Provider>
);
}
Select.Option = function Option({ value, children }) {
const ctx = useContext(SelectContext);
return (
<div role="option" onClick={() => ctx.onChange(value)}
aria-selected={ctx.value === value}>
{children}
</div>
);
};
// Usage
<Select value={selected} onChange={setSelected}>
<Select.Option value="a">Option A</Select.Option>
<Select.Option value="b">Option B</Select.Option>
</Select>
State Management
When to use what
- useState — local component state
- useReducer — complex state transitions in one component
- Context — shared state across a subtree (theme, auth, locale)
- Zustand/Jotai — global app state without boilerplate
- React Query/SWR — server state (API data with caching)
Avoid prop drilling with composition
// Instead of passing props through 5 levels, compose:
function Page() {
const user = useUser();
return <Layout header={<Header user={user} />} content={<Content />} />;
}
Performance
React.memo — skip re-renders for unchanged props
const ExpensiveList = React.memo(function ExpensiveList({ items }) {
return items.map(item => <Item key={item.id} {...item} />);
});
Virtualization for long lists
// Use @tanstack/react-virtual for lists >100 items
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef(null);
const virtualizer = useVirtualizer({ count: items.length, getScrollElement: () => parentRef.current, estimateSize: () => 50 });
// render only visible items
}
Code splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
);
}
Error Boundaries
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) { logError(error, info); }
render() {
if (this.state.hasError) return <ErrorFallback />;
return this.props.children;
}
}
Testing
# Component test with Testing Library
npx vitest run --reporter=verbose src/components/
# Key testing patterns:
# - Test behavior, not implementation
# - Use screen.getByRole() over getByTestId()
# - Prefer userEvent over fireEvent
# - Assert what the user sees, not internal state
Notes
- Don't optimize prematurely. Profile first with React DevTools Profiler.
- Server Components (Next.js App Router) don't need
useStateoruseEffect— they run on the server. - Keys should be stable IDs, never array indexes (unless the list never reorders).
- Avoid
useEffectfor derived state — compute it during render instead.
Weekly Installs
2
Repository
thinkfleetai/th…t-engineFirst Seen
14 days ago
Security Audits
Installed on
opencode2
gemini-cli2
claude-code2
github-copilot2
codex2
kimi-cli2