| name | react-frontend |
| description | React frontend standards covering hooks (useState, useEffect, useContext, custom hooks), state management (Context API, Redux, Zustand), performance optimization (memoization, lazy loading, code splitting), testing with React Testing Library, and accessibility (WCAG 2.1, ARIA) for modern SPAs |
React Frontend Development Standards
Quick Reference
Essential Hooks
const [count, setCount] = useState(0);
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
return () => {};
}, [dependencies]);
const theme = useContext(ThemeContext);
const inputRef = useRef<HTMLInputElement>(null);
const value = useMemo(() => expensiveCalc(a, b), [a, b]);
const callback = useCallback(() => doSomething(a), [a]);
State Management Decision Tree
Is state needed in >2 components?
NO -> Use local state (useState)
YES -> Is it global UI state (theme, auth)?
YES -> Use Context API
NO -> Is it server data (API responses)?
YES -> Use React Query/TanStack Query
NO -> Complex state with many actions?
YES -> Use Redux Toolkit or Zustand
NO -> Use Context API + useReducer
Component Checklist
Performance Checklist
Quick Start
npx create-react-app my-app --template typescript
npm install --save-dev @testing-library/react @testing-library/jest-dom
npm install @tanstack/react-query
npm install zustand
npm install @reduxjs/toolkit
Core Hooks Patterns
useState
const [data, setData] = useState(() => computeExpensiveValue());
setCount(prevCount => prevCount + 1);
useEffect
useEffect(() => {
console.log('Component mounted');
}, []);
useEffect(() => {
let isMounted = true;
async function fetchData() {
const result = await api.getData();
if (isMounted) setData(result);
}
fetchData();
return () => { isMounted = false; };
}, []);
useEffect(() => {
const subscription = api.subscribe(userId);
return () => subscription.unsubscribe();
}, [userId]);
useContext
interface ThemeContextType {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
};
useReducer
type Action = { type: 'increment' } | { type: 'decrement' } | { type: 'reset' };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
case 'reset': return { count: 0 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
Custom Hooks
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let isMounted = true;
fetch(url)
.then(res => res.json())
.then(data => { if (isMounted) { setData(data); setLoading(false); }})
.catch(err => { if (isMounted) { setError(err); setLoading(false); }});
return () => { isMounted = false; };
}, [url]);
return { data, loading, error };
}
State Management
Context API (Theme, Auth, Locale)
<AuthProvider>
<ThemeProvider>
<LocaleProvider>
<App />
</LocaleProvider>
</ThemeProvider>
</AuthProvider>
React Query (Server State)
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function Users() {
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: createUser,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
});
}
Zustand (Lightweight Global State)
import create from 'zustand';
const useBearStore = create<BearState>((set) => ({
bears: 0,
increase: () => set((state) => ({ bears: state.bears + 1 })),
}));
const bears = useBearStore((state) => state.bears);
Performance Optimization
React.memo
const ExpensiveComponent = React.memo(({ data }: Props) => {
return <div>{/* Expensive render */}</div>;
});
Code Splitting
const Home = lazy(() => import('./pages/Home'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</Suspense>
);
}
Virtual Scrolling
import { FixedSizeList } from 'react-window';
<FixedSizeList height={600} itemCount={10000} itemSize={35} width="100%">
{({ index, style }) => <div style={style}>Row {index}</div>}
</FixedSizeList>
Component Patterns
Props with Defaults
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
}
export function Button({ variant = 'primary', size = 'md', children }: ButtonProps) {
return <button className={`btn-${variant} btn-${size}`}>{children}</button>;
}
Compound Components
const TabsContext = createContext<TabsContextType | undefined>(undefined);
export function Tabs({ children }: { children: React.ReactNode }) {
const [activeTab, setActiveTab] = useState('tab1');
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
Tabs.Tab = function Tab({ id, children }: TabProps) {
const context = useContext(TabsContext);
return (
<button onClick={() => context?.setActiveTab(id)}>
{children}
</button>
);
};
Testing with React Testing Library
User-Centric Testing
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('user can submit form', async () => {
const user = userEvent.setup();
const onSubmit = jest.fn();
render(<ContactForm onSubmit={onSubmit} />);
const nameInput = screen.getByRole('textbox', { name: /name/i });
await user.type(nameInput, 'John Doe');
await user.click(screen.getByRole('button', { name: /submit/i }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({ name: 'John Doe' });
});
});
Testing Hooks
import { renderHook, act } from '@testing-library/react';
test('useCounter increments', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
act(() => { result.current.increment(); });
expect(result.current.count).toBe(1);
});
Coverage Targets
- Components: 80%+
- Hooks: 90%+
- Utils: 95%+
Accessibility (WCAG 2.1 AA)
Semantic HTML
<nav><ul><li><a href="/">Home</a></li></ul></nav>
<main><article><h1>Title</h1><section><h2>Section</h2></section></article></main>
<footer>© 2024 Company</footer>
ARIA and Keyboard Navigation
<button aria-label="Close dialog" onClick={onClose}>X</button>
<div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
<h2 id="dialog-title">Confirm Action</h2>
</div>
<div role="alert" aria-live="assertive">{error}</div>
Form Accessibility
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? 'email-error' : undefined}
/>
{errors.email && <span id="email-error" role="alert">{errors.email}</span>}
Security
XSS Prevention
<div>{userInput}</div>
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHTML);
Secure Token Storage
Summary
- Hooks mastery for state and side effects
- Appropriate state management (local -> Context -> external library)
- Performance optimization with memoization and code splitting
- User-centric testing with React Testing Library
- Accessibility (WCAG 2.1 AA) with semantic HTML and ARIA
- Security through XSS prevention and secure auth patterns
Resources
See REFERENCE.md for:
- Complete component templates
- Advanced hooks patterns (useReducer, usePrevious)
- Redux Toolkit implementation
- Compound component examples
- Comprehensive testing patterns
- Full accessibility checklists
- Production optimization examples