Skip to main content

react-workflow

React framework workflow guidelines. Activate when working with React components (.jsx, .tsx), React hooks (useState, useEffect), or React-specific patterns.

معلومات المصدر

المستودع
ilude/claude-code-config
آخر نشاط في المصدر
٢٩ ديسمبر ٢٠٢٥ في ١٥:٢٨
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٨
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
react-workflow
description
React framework workflow guidelines. Activate when working with React components (.jsx, .tsx), React hooks (useState, useEffect), or React-specific patterns.
location
user
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. # React Workflow ## Tool Grid | Task | Tool | Command | |------|------|---------| | Lint | Biome | `biome check .` | | Format | Biome | `biome format --write .` | | Test | Vitest + Testing Library | `vitest` | | E2E | Playwright | `playwright test` | --- ## Component Architecture ### Functional Components Only Components MUST be functional components. Class components MUST NOT be used. ```tsx // CORRECT function UserProfile({ user }: UserProfileProps) { return <div>{user.name}</div>; } // ALSO CORRECT const UserProfile = ({ user }: UserProfileProps) => { return <div>{user.name}</div>; }; // INCORRECT - MUST NOT use class components class UserProfile extends Component { ... } ``` ### Component Naming - Component files MUST use PascalCase: `UserProfile.tsx` - Components MUST be named exports for better tree-shaking - One component per file SHOULD be the default; co-located helpers are OPTIONAL ### Props Interface Props MUST be typed with explicit interfaces: ```tsx interface ButtonProps { variant: 'primary' | 'secondary'; disabled?: boolean; children: React.ReactNode; onClick?: () => void; } function Button({ variant, disabled = false, children, onClick }: ButtonProps) { return ( <button className={variant} disabled={disabled} onClick={onClick}> {children} </button> ); } ``` --- ## React 19 Features ### useActionState Server actions SHOULD use `useActionState` for form handling: ```tsx import { useActionState } from 'react'; function LoginForm() { const [state, formAction, isPending] = useActionState(loginAction, null); return ( <form action={formAction}> <input name="email" type="email" required /> <button type="submit" disabled={isPending}> {isPending ? 'Logging in...' : 'Log in'} </button> {state?.error && <p role="alert">{state.error}</p>} </form> ); } ``` ### useFormStatus Form submission state SHOULD use `useFormStatus` in child components: ```tsx import { useFormStatus } from 'react-dom'; function SubmitButton() { const { pending } = useFormStatus(); return ( <button type="submit" disabled={pending}> {pending ? 'Submitting...' : 'Submit'} </button> ); } ``` ### useOptimistic Optimistic updates SHOULD use `useOptimistic`: ```tsx import { useOptimistic } from 'react'; function TodoList({ todos, addTodo }) { const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo) => [...state, { ...newTodo, pending: true }] ); async function handleAdd(formData: FormData) { const newTodo = { id: crypto.randomUUID(), text: formData.get('text') }; addOptimisticTodo(newTodo); await addTodo(newTodo); } return ( <form action={handleAdd}> {optimisticTodos.map(todo => ( <li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}> {todo.text} </li> ))} </form> ); } ``` ### use() Hook The `use()` hook MAY be used to read promises and context: ```tsx import { use, Suspense } from 'react'; function UserData({ userPromise }) { const user = use(userPromise); return <div>{user.name}</div>; } // Usage with Suspense <Suspense fallback={<Skeleton />}> <UserData userPromise={fetchUser(id)} /> </Suspense> ``` --- ## Server Components (RSC) ### Default to Server Components Components SHOULD be Server Components by default. Client Components MUST use the `'use client'` directive. ```tsx // Server Component (default) - no directive needed async function UserList() { const users = await db.users.findMany(); return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>; } // Client Component - requires directive 'use client'; import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(c => c + 1)}>{count}</button>; } ``` ### RSC Patterns - Data fetching SHOULD happen in Server Components - Interactive elements MUST be Client Components - Server Components MUST NOT use hooks or browser APIs - Client Components SHOULD be pushed to the leaves of the component tree --- ## React Compiler ### Automatic Memoization React Compiler handles memoization automatically. Manual memoization SHOULD NOT be used unless profiling shows a specific need: ```tsx // RECOMMENDED - let React Compiler optimize function ExpensiveList({ items, filter }) { const filtered = items.filter(filter); return <ul>{filtered.map(item => <li key={item.id}>{item.name}</li>)}</ul>; } // AVOID - unnecessary manual memoization function ExpensiveList({ items, filter }) { const filtered = useMemo(() => items.filter(filter), [items, filter]); return <ul>{filtered.map(item => <li key={item.id}>{item.name}</li>)}</ul>; } ``` ### Compiler Requirements - Components MUST follow the Rules of React (pure rendering, immutable props) - Side effects MUST be in useEffect or event handlers - State updates MUST be immutable --- ## Testing ### Testing Library Patterns Tests MUST use accessible queries. Priority order: 1. `getByRole` (RECOMMENDED) 2. `getByLabelText` (for form fields) 3. `getByPlaceholderText` (when no label) 4. `getByText` (for non-interactive elements) 5. `getByTestId` (last resort) ```tsx import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; test('submits form with user data', async () => { const user = userEvent.setup(); const onSubmit = vi.fn(); render(<LoginForm onSubmit={onSubmit} />); // CORRECT - uses accessible queries await user.type(screen.getByRole('textbox', { name: /email/i }), 'test@example.com'); await user.type(screen.getByLabelText(/password/i), 'secret123'); await user.click(screen.getByRole('button', { name: /log in/i })); expect(onSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'secret123' }); }); ``` ### User Event User interactions MUST use `@testing-library/user-event` over `fireEvent`: ```tsx // CORRECT const user = userEvent.setup(); await user.click(button); await user.type(input, 'text'); // AVOID fireEvent.click(button); fireEvent.change(input, { target: { value: 'text' } }); ``` ### Async Queries Async operations MUST use `findBy*` or `waitFor`: ```tsx // CORRECT const successMessage = await screen.findByRole('alert'); expect(successMessage).toHaveTextContent('Saved!'); // CORRECT await waitFor(() => { expect(screen.getByRole('list')).toHaveTextContent('Item 1'); }); ``` --- ## Accessibility ### ARIA Requirements Interactive elements MUST have accessible names: ```tsx // CORRECT <button aria-label="Close dialog"> <CloseIcon /> </button> // CORRECT <button> <CloseIcon aria-hidden="true" /> <span className="sr-only">Close dialog</span> </button> // INCORRECT - no accessible name <button> <CloseIcon /> </button> ``` ### Keyboard Navigation All interactive elements MUST be keyboard accessible: ```tsx function Menu({ items }) { const [activeIndex, setActiveIndex] = useState(0); const handleKeyDown = (e: React.KeyboardEvent) => { switch (e.key) { case 'ArrowDown': e.preventDefault(); setActiveIndex(i => Math.min(i + 1, items.length - 1)); break; case 'ArrowUp': e.preventDefault(); setActiveIndex(i => Math.max(i - 1, 0)); break; case 'Enter': case ' ': e.preventDefault(); items[activeIndex].onSelect(); break; } }; return ( <ul role="menu" onKeyDown={handleKeyDown}> {items.map((item, i) => ( <li key={item.id} role="menuitem" tabIndex={i === activeIndex ? 0 : -1} aria-selected={i === activeIndex} > {item.label} </li> ))} </ul> ); } ``` ### Focus Management Focus MUST be managed appropriately for modals, dialogs, and dynamic content: ```tsx function Dialog({ isOpen, onClose, children }) { const dialogRef = useRef<HTMLDivElement>(null); useEffect(() => { if (isOpen) { dialogRef.current?.focus(); } }, [isOpen]); if (!isOpen) return null; return ( <div ref={dialogRef} role="dialog" aria-modal="true" tabIndex={-1} onKeyDown={e => e.key === 'Escape' && onClose()} > {children} </div> ); } ``` --- ## State Management ### State Hierarchy State SHOULD follow this hierarchy (simplest to most complex): 1. **Local state** (useState) - component-specific state 2. **Lifted state** - shared between siblings via parent 3. **Context** - deeply nested or cross-cutting state 4. **External store** (Zustand, Jotai) - complex global state ### Local State First Simple state MUST use `useState`: ```tsx function Toggle() { const [isOn, setIsOn] = useState(false); return <button onClick={() => setIsOn(!isOn)}>{isOn ? 'On' : 'Off'}</button>; } ``` ### Context Usage Context SHOULD be used for theme, auth, or other cross-cutting concerns: ```tsx const ThemeContext = createContext<Theme | null>(null); function useTheme() { const theme = useContext(ThemeContext); if (!theme) throw new Error('useTheme must be used within ThemeProvider'); return theme; } function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, setTheme] = useState<Theme>('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> {children} </ThemeContext.Provider> ); } ``` --- ## Custom Hooks ### Extraction Pattern Reusable logic SHOULD be extracted into custom hooks: ```tsx function useLocalStorage<T>(key: string, initialValue: T) { const [value, setValue] = useState<T>(() => { const stored = localStorage.getItem(key);
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub