);
}
```
### Lists and Keys
```typescript
function UserList({ users }: { users: User[] }) {
return (
{users.map(user => (
{user.name}
))}
);
}
```
## Best Practices
### General React Best Practices
1. **Dependency Arrays**: Always specify correct dependencies in useEffect
2. **State Structure**: Keep state minimal and avoid redundant state
3. **Component Size**: Keep components small and focused
4. **Custom Hooks**: Extract complex logic into reusable custom hooks
5. **TypeScript**: Use TypeScript for type safety
6. **Keys**: Use stable IDs as keys for list items, not array indices
7. **Immutability**: Never mutate state directly
8. **Effects**: Use effects only for synchronization with external systems
9. **Performance**: Profile before optimizing with useMemo/useCallback
### React 19 Specific Best Practices
1. **Server Components**: Use Server Components for data fetching and static content
2. **Client Components**: Mark components as 'use client' only when necessary
3. **Actions**: Use Server Actions for mutations and form submissions
4. **Optimistic Updates**: Implement useOptimistic for better UX
5. **use() Hook**: Use for reading promises and context conditionally
6. **Form State**: Use useFormState and useFormStatus for complex forms
7. **Concurrent Features**: Leverage useTransition for non-urgent updates
8. **Error Boundaries**: Implement proper error handling with error boundaries
## Common Pitfalls
### General React Pitfalls
❌ **Missing Dependencies**:
```typescript
useEffect(() => {
// Uses 'count' but doesn't include it in deps
console.log(count);
}, []); // Wrong!
```
❌ **Mutating State**:
```typescript
const [items, setItems] = useState([]);
items.push(newItem); // Wrong! Mutates state
setItems(items); // Won't trigger re-render
```
✅ **Correct Approach**:
```typescript
setItems([...items, newItem]); // Create new array
```
### React 19 Specific Pitfalls
❌ **Using use() outside of render**:
```typescript
// Wrong!
function handleClick() {
const data = use(promise); // Error: use() can only be called in render
}
```
✅ **Correct usage**:
```tsx
function Component({ promise }) {
const data = use(promise); // Correct: called during render
return
{data}
;
}
```
❌ **Forgetting 'use server' directive**:
```typescript
// Wrong - missing 'use server'
export async function myAction() {
// This will run on the client!
}
```
✅ **Correct Server Action**:
```typescript
'use server'; // Must be at the top
export async function myAction() {
// Now runs on the server
}
```
❌ **Mixing Server and Client logic incorrectly**:
```tsx
// Wrong - trying to use browser APIs in Server Component
export default async function ServerComponent() {
const width = window.innerWidth; // Error: window is not defined
return
{width}
;
}
```
✅ **Correct separation**:
```tsx
// Server Component for data
export default async function ServerComponent() {
const data = await fetchData();
return ;
}
// Client Component for browser APIs
'use client';
function ClientComponent({ data }) {
const [width, setWidth] = useState(window.innerWidth);
// Handle resize logic...
return
{width}
;
}
```
## React 19 New Features
### use() Hook - Reading Resources
The `use()` hook reads the value from a resource like a Promise or Context:
```typescript
import { use } from 'react';
// Reading a Promise in a component
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
return
{message}
;
}
// Reading Context conditionally
function Button() {
if (condition) {
const theme = use(ThemeContext);
return ;
}
return ;
}
```
### useOptimistic Hook - Optimistic UI Updates
Manage optimistic UI updates for async operations:
```typescript
import { useOptimistic } from 'react';
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, newTodo]
);
const handleSubmit = async (formData) => {
const newTodo = { id: Date.now(), text: formData.get('text') };
// Optimistically add to UI
addOptimisticTodo(newTodo);
// Actually add to backend
await addTodo(newTodo);
};
return (
);
}
```
### useFormStatus Hook - Form State
Access form submission status from child components:
```typescript
import { useFormStatus } from 'react';
function SubmitButton() {
const { pending, data } = useFormStatus();
return (
);
}
function ContactForm() {
return (
);
}
```
### useFormState Hook - Form State Management
Manage form state with error handling:
```typescript
import { useFormState } from 'react';
async function submitAction(prevState: string | null, formData: FormData) {
const email = formData.get('email') as string;
if (!email.includes('@')) {
return 'Invalid email address';
}
await submitToDatabase(email);
return null;
}
function EmailForm() {
const [state, formAction] = useFormState(submitAction, null);
return (
);
}
```
### Server Actions
Define server-side functions for form handling:
```typescript
// app/actions.ts
'use server';
import { redirect } from 'next/navigation';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
// Validate input
if (!title || !content) {
return { error: 'Title and content are required' };
}
// Save to database
const post = await db.post.create({
data: { title, content }
});
// Update cache and redirect
revalidatePath('/posts');
redirect(`/posts/${post.id}`);
}
```
### Server Components
Components that run exclusively on the server:
```typescript
// app/posts/page.tsx - Server Component
async function PostsPage() {
// Server-side data fetching
const posts = await db.post.findMany({
orderBy: { createdAt: 'desc' },
take: 10
});
return (
);
}
```
### Server Components Best Practices
1. **Keep Server Components Server-Only**: No event handlers, hooks, or browser APIs
2. **Minimize Client Components**: Only use 'use client' when necessary
3. **Pass Data as Props**: Serialize data when passing from Server to Client
4. **Use Server Actions for Mutations**: Keep data operations on the server
```typescript
// Good: Server Component for static content
async function ProductPage({ id }: { id: string }) {
const product = await fetchProduct(id);
return (
{product.name}
{product.description}
);
}
// Client Component only for interactivity
'use client';
function AddToCartForm({ productId }: { productId: string }) {
const [isAdding, setIsAdding] = useState(false);
async function handleSubmit() {
setIsAdding(true);
await addToCart(productId);
setIsAdding(false);
}
return (
);
}
```
## Migration Guide
### From React 18 to 19
1. **Update Dependencies**:
```bash
npm install react@19 react-dom@19
```
2. **Adopt Server Components**:
- Identify data-fetching components
- Remove client-side code from Server Components
- Add 'use client' directive where needed
3. **Replace Manual Optimistic Updates**:
```typescript
// Before
function TodoList({ todos, addTodo }) {
const [optimisticTodos, setOptimisticTodos] = useState(todos);
const handleAdd = async (text) => {
const newTodo = { id: Date.now(), text };
setOptimisticTodos([...optimisticTodos, newTodo]);
await addTodo(newTodo);
};
}
// After
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, newTodo]
);
const handleAdd = async (formData) => {
const newTodo = { id: Date.now(), text: formData.get('text') };
addOptimisticTodo(newTodo);
await addTodo(newTodo);
};
}
```
4. **Enable React Compiler**:
- Install babel-plugin-react-compiler
- Remove manual memoization
- Let the compiler optimize automatically
## References
- React 19 Official Docs: https://react.dev/blog/2024/04/25/react-19
- React Server Components: https://react.dev/reference/rsc/server-components
- React Compiler: https://react.dev/learn/react-compiler
- TypeScript with React: https://react-typescript-cheatsheet.netlify.app/