Loading...
Loading...
Comprehensive React development guide covering component architecture, hooks, state management, TypeScript integration, useEffect patterns, and testing with Vitest. Use when creating React components, custom hooks, managing state, or any frontend React code. Essential for React 19+ development.
npx skill4agent add pedronauck/skills reactreferences/best-practices.mdreferences/useeffect-patterns.mdtanstacktanstackuse()useOptimistic()| Priority | Tool | Use Case |
|---|---|---|
| 1 | | Component-specific UI state |
| 2 | Zustand | Shared client state across components |
| 3 | TanStack Query | Server state and data synchronization |
| 4 | URL state | Shareable application state (TanStack Router) |
| Situation | DON'T | DO |
|---|---|---|
| Derived state from props/state | | Calculate during render |
| Expensive calculations | | |
| Reset state on prop change | | |
| User event responses | | Event handler directly |
| Notify parent of changes | | Call in event handler |
| Fetch data | | |
useSyncExternalStoreconst fullName = firstName + ' ' + lastName// CORRECT: Type props directly (never use React.FC)
interface ButtonProps {
variant: "primary" | "secondary";
children: React.ReactNode;
}
function Button({ variant, children }: ButtonProps) {
return <button className={variant}>{children}</button>;
}
// Extending HTML elements
interface InputProps extends React.ComponentProps<"input"> {
label: string;
}useXxx// State-like hook returns array
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle] as const;
}
// Complex hook returns object
function useUser(id: string) {
const query = useQuery({ queryKey: ["user", id], queryFn: () => fetchUser(id) });
return {
user: query.data,
isLoading: query.isLoading,
error: query.error,
refetch: query.refetch,
};
}// CORRECT: Hook handles all logic, component handles rendering
function useIssueSearch(projectId: string) {
const [query, setQuery] = useState("");
const [filters, setFilters] = useState<Filters>({});
const issues = useQuery({
queryKey: ["issues", projectId, query, filters],
queryFn: () => searchIssues(projectId, query, filters),
});
return {
query,
setQuery,
filters,
setFilters,
issues: issues.data ?? [],
isLoading: issues.isLoading,
};
}
function IssueList({ projectId }: { projectId: string }) {
const { query, setQuery, issues, isLoading } = useIssueSearch(projectId);
return (
<div>
<SearchInput value={query} onChange={setQuery} />
{isLoading ? <Loading /> : <IssueTable issues={issues} />}
</div>
);
}| Type | Pattern | Example |
|---|---|---|
| Components | kebab-case.tsx | |
| Hooks | use-kebab-case.ts | |
| Utilities | camelCase.ts | |
| Types | types.ts | |
| Tests | *.test.tsx | |
import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { renderHook, act } from "@testing-library/react";
describe("MyComponent", () => {
it("renders correctly", () => {
render(<MyComponent />);
expect(screen.getByText("Hello")).toBeInTheDocument();
});
});
describe("useMyHook", () => {
it("returns expected value", () => {
const { result } = renderHook(() => useMyHook());
expect(result.current.value).toBe(expected);
});
});React.FCpnpm run lintpnpm run typecheckpnpm run testreferences/best-practices.mdreferences/useeffect-patterns.md