* chore: bump version to v0.8.0 * chore: update version to 0.7.1 instead of 0.8.0 * docs: expand comparison table with Oh My OpenCode and additional features * backup: development context before reorganization * refactor(context): reorganize development folder - Remove 7 duplicate files (ui/design files exist in ui/web/) - Remove 2 redundant READMEs (use navigation.md instead) - Move react-patterns.md to frontend/react/ - Create frontend/react/navigation.md - Update 4 navigation files (development, ui, frontend, frontend/react) - Update developer profile (4 path changes: ui-styling-standards, design-systems, design-assets, animation-patterns) - Update 3 core context files (visual-development.md, design-iteration.md, file-locations.md) Result: 29 context files (down from 36), no duplicates, clean structure Refs: REORGANIZATION_PLAN.md, REORGANIZATION_SUMMARY.md * docs(context): add frontend delegation guide - Create when-to-delegate.md with clear decision criteria - Decision matrix for when to delegate vs. handle directly - Common patterns and examples - Red flags and green flags - Frontend-specialist capabilities reference - Update frontend and development navigation files Helps orchestrators decide when to delegate UI work to frontend-specialist subagent. * feat: enhance developer and advanced profiles with UI, development, and system builder context - Developer profile: Add UI/UX patterns, development principles, design systems, React patterns, animation patterns, styling standards, and design assets - Full profile: Add same UI/development context as developer profile for consistency - Advanced profile: Add comprehensive system builder context including templates, guides, context system standards, and repository management context - Update profile descriptions to reflect new capabilities - All context files verified to exist and are properly referenced * fix: remove duplicate context entries with incorrect paths - Remove duplicate design-assets, ui-styling-standards, clean-code, animation-patterns, design-systems, react-patterns, and api-design entries that pointed to wrong paths - Remove non-existent development-readme and mastra-ai-readme entries - Keep only correct entries with proper file paths - All registry paths now validated and correct --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
7.5 KiB
7.5 KiB
React Patterns & Best Practices
Category: development
Purpose: Modern React patterns, hooks usage, and component design principles
Used by: frontend-specialist
Overview
This guide covers modern React patterns using functional components, hooks, and best practices for building scalable React applications.
Component Patterns
1. Functional Components with Hooks
Always use functional components:
// Good
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
return <div>{user?.name}</div>;
}
2. Custom Hooks for Reusable Logic
Extract common logic into custom hooks:
// Custom hook
function useUser(userId) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetchUser(userId)
.then(setUser)
.catch(setError)
.finally(() => setLoading(false));
}, [userId]);
return { user, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { user, loading, error } = useUser(userId);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <div>{user.name}</div>;
}
3. Composition Over Props Drilling
Use composition to avoid prop drilling:
// Bad - Props drilling
function App() {
const [theme, setTheme] = useState('light');
return <Layout theme={theme} setTheme={setTheme} />;
}
// Good - Composition with Context
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Layout />
</ThemeContext.Provider>
);
}
function Layout() {
const { theme } = useContext(ThemeContext);
return <div className={theme}>...</div>;
}
4. Compound Components
For complex, related components:
function Tabs({ children }) {
const [activeTab, setActiveTab] = useState(0);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
);
}
Tabs.List = function TabsList({ children }) {
return <div className="tabs-list">{children}</div>;
};
Tabs.Tab = function Tab({ index, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
<button
className={activeTab === index ? 'active' : ''}
onClick={() => setActiveTab(index)}
>
{children}
</button>
);
};
Tabs.Panel = function TabPanel({ index, children }) {
const { activeTab } = useContext(TabsContext);
return activeTab === index ? <div>{children}</div> : null;
};
// Usage
<Tabs>
<Tabs.List>
<Tabs.Tab index={0}>Tab 1</Tabs.Tab>
<Tabs.Tab index={1}>Tab 2</Tabs.Tab>
</Tabs.List>
<Tabs.Panel index={0}>Content 1</Tabs.Panel>
<Tabs.Panel index={1}>Content 2</Tabs.Panel>
</Tabs>
Hooks Best Practices
1. useEffect Dependencies
Always specify dependencies correctly:
// Bad - Missing dependencies
useEffect(() => {
fetchData(userId);
}, []);
// Good - Correct dependencies
useEffect(() => {
fetchData(userId);
}, [userId]);
// Good - Stable function reference
const fetchData = useCallback((id) => {
api.getUser(id).then(setUser);
}, []);
useEffect(() => {
fetchData(userId);
}, [userId, fetchData]);
2. useMemo for Expensive Calculations
Memoize expensive computations:
function DataTable({ data, filters }) {
const filteredData = useMemo(() => {
return data.filter(item =>
filters.every(filter => filter(item))
);
}, [data, filters]);
return <Table data={filteredData} />;
}
3. useCallback for Stable References
Prevent unnecessary re-renders:
function Parent() {
const [count, setCount] = useState(0);
// Bad - New function on every render
const handleClick = () => setCount(c => c + 1);
// Good - Stable function reference
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <Child onClick={handleClick} />;
}
const Child = memo(function Child({ onClick }) {
return <button onClick={onClick}>Click</button>;
});
State Management Patterns
1. Local State First
Start with local state, lift when needed:
// Local state
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// Lifted state when shared
function App() {
const [count, setCount] = useState(0);
return (
<>
<Counter count={count} setCount={setCount} />
<Display count={count} />
</>
);
}
2. useReducer for Complex State
Use reducer for related state updates:
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'decrement':
return { ...state, count: state.count - state.step };
case 'setStep':
return { ...state, step: action.payload };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}
Performance Optimization
1. Code Splitting
Lazy load routes and heavy components:
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
2. Virtualization for Long Lists
Use virtualization for large datasets:
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>{items[index].name}</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
Best Practices
- Keep components small and focused - Single responsibility principle
- Use TypeScript - Type safety prevents bugs and improves DX
- Colocate related code - Keep components, styles, and tests together
- Use meaningful prop names - Clear, descriptive names improve readability
- Avoid inline functions in JSX - Extract to named functions or useCallback
- Use fragments - Avoid unnecessary wrapper divs
- Handle loading and error states - Always show feedback to users
- Test components - Use React Testing Library for user-centric tests
Anti-Patterns
- ❌ Prop drilling - Use context or composition instead
- ❌ Massive components - Break down into smaller, focused components
- ❌ Mutating state directly - Always use setState or dispatch
- ❌ Using index as key - Use stable, unique identifiers
- ❌ Unnecessary useEffect - Derive state when possible
- ❌ Ignoring ESLint warnings - React hooks rules prevent bugs
- ❌ Not memorizing context values - Causes unnecessary re-renders
References
- React Documentation (react.dev)
- React Patterns by Kent C. Dodds
- Epic React by Kent C. Dodds