UNPKG

agent-rules-kit

Version:

Bootstrap of **Cursor** rules (`.mdc`) and mirror documentation (`.md`) for AI agent-guided projects.

373 lines (295 loc) 7.92 kB
--- description: Best practices for React applications globs: <root>/src/**/*.ts,<root>/src/**/*.tsx,<root>/src/**/*.js,<root>/src/**/*.jsx,<root>/components/**/*.ts,<root>/components/**/*.tsx,<root>/components/**/*.js,<root>/components/**/*.jsx alwaysApply: false --- # React Best Practices This guide outlines the recommended best practices for React development in {projectPath}. ## Component Structure ### Functional Components Use functional components with hooks as the default pattern: ```jsx // ✅ Preferred: Functional component with hooks const UserProfile = ({ userId }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetchUser(userId).then((data) => { setUser(data); setLoading(false); }); }, [userId]); if (loading) return <Spinner />; return <div>{user.name}</div>; }; ``` ### Component Organization Organize components by feature or page rather than by type: ``` src/ ├── features/ │ ├── auth/ │ │ ├── components/ │ │ ├── hooks/ │ │ ├── utils/ │ │ └── index.js │ ├── dashboard/ │ └── settings/ ├── common/ │ ├── components/ │ ├── hooks/ │ └── utils/ ``` ## State Management ### Local State Use local state for component-specific state: ```jsx // Simple counter with local state function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } ``` ### Global State For global state, choose the appropriate solution based on complexity: - **Context API**: For simpler applications or shared state within a specific feature - **Redux**: For complex applications with many state updates or when you need time-travel debugging - **Zustand/Jotai/Recoil**: For simpler API with less boilerplate than Redux ```jsx // Context API example const ThemeContext = createContext(); function ThemeProvider({ children }) { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); } // Hook to use the theme function useTheme() { return useContext(ThemeContext); } ``` ## Performance Optimization ### Memoization Use `React.memo`, `useMemo`, and `useCallback` for performance optimization, but only when necessary: ```jsx // Memoize expensive calculations const MemoizedComponent = React.memo(({ value }) => { // Only re-renders if value changes return <div>{value}</div>; }); function ParentComponent() { const [count, setCount] = useState(0); // Memoize callback to prevent unnecessary rerenders const handleClick = useCallback(() => { console.log('Clicked'); }, []); // Memoize expensive calculations const expensiveValue = useMemo(() => { return computeExpensiveValue(count); }, [count]); return ( <div> <button onClick={() => setCount(count + 1)}>Increment</button> <MemoizedComponent value={expensiveValue} onClick={handleClick} /> </div> ); } ``` ### Virtual List For long lists, use virtualization libraries like `react-window` or `react-virtualized`: ```jsx import { FixedSizeList } from 'react-window'; const Row = ({ index, style }) => <div style={style}>Row {index}</div>; const VirtualList = () => ( <FixedSizeList height={500} width='100%' itemCount={10000} itemSize={35}> {Row} </FixedSizeList> ); ``` ## Code Splitting Use dynamic imports and React.lazy for code splitting: ```jsx import React, { Suspense, lazy } from 'react'; // Lazy load the component const LazyComponent = lazy(() => import('./LazyComponent')); function App() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> </div> ); } ``` ## Custom Hooks Extract reusable logic into custom hooks: ```jsx // Custom hook for handling form state function useForm(initialValues) { const [values, setValues] = useState(initialValues); const handleChange = useCallback((e) => { const { name, value } = e.target; setValues((prev) => ({ ...prev, [name]: value })); }, []); const resetForm = useCallback(() => { setValues(initialValues); }, [initialValues]); return { values, handleChange, resetForm }; } // Usage function LoginForm() { const { values, handleChange, resetForm } = useForm({ email: '', password: '', }); const handleSubmit = (e) => { e.preventDefault(); // Process form submission resetForm(); }; return ( <form onSubmit={handleSubmit}> <input name='email' value={values.email} onChange={handleChange} /> <input type='password' name='password' value={values.password} onChange={handleChange} /> <button type='submit'>Login</button> </form> ); } ``` ## API Calls Use custom hooks for API calls: ```jsx function useApi(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let isMounted = true; setLoading(true); fetch(url) .then((response) => response.json()) .then((data) => { if (isMounted) { setData(data); setLoading(false); } }) .catch((error) => { if (isMounted) { setError(error); setLoading(false); } }); return () => { isMounted = false; }; }, [url]); return { data, loading, error }; } // Usage function UserList() { const { data, loading, error } = useApi('/api/users'); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <ul> {data.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); } ``` ## Type Safety Use TypeScript or PropTypes for type checking: ```tsx // TypeScript example interface UserProps { name: string; age: number; email?: string; // Optional } const User: React.FC<UserProps> = ({ name, age, email }) => { return ( <div> <h2>{name}</h2> <p>Age: {age}</p> {email && <p>Email: {email}</p>} </div> ); }; ``` ## Accessibility Follow accessibility best practices: - Use semantic HTML elements - Include proper labels for form elements - Ensure keyboard navigation works - Provide alternative text for images - Test with screen readers ```jsx // Accessible form element <div> <label htmlFor='email'>Email Address</label> <input id='email' type='email' aria-describedby='email-help' /> <p id='email-help'>We'll never share your email.</p> </div> ``` ## Error Boundaries Use error boundaries to catch JavaScript errors and display fallback UI: ```jsx class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { // Log the error to an error reporting service logErrorToService(error, errorInfo); } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } } // Usage <ErrorBoundary> <MyComponent /> </ErrorBoundary>; ``` ## Project Structure Follow a consistent project structure: ``` src/ ├── assets/ # Static files like images, fonts ├── components/ # Shared components │ ├── common/ # Very generic components used across features │ └── ui/ # UI components like buttons, inputs, etc. ├── features/ # Feature-based modules ├── hooks/ # Custom hooks ├── layouts/ # Layout components ├── pages/ # Page components ├── services/ # API services ├── store/ # State management ├── utils/ # Utility functions ├── App.jsx # Root component └── index.jsx # Entry point ```