react-patterns

安装量: 1.1K
排名: #1233

安装

npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill react-patterns
React Development Patterns
Overview
Expert guide for building modern React 19 applications with new concurrent features, Server Components, Actions, and advanced patterns. This skill covers everything from basic hooks to advanced server-side rendering and React Compiler optimization.
When to Use
Building React 19 components with TypeScript/JavaScript
Managing component state with useState and useReducer
Handling side effects with useEffect
Optimizing performance with useMemo and useCallback
Creating custom hooks for reusable logic
Implementing component composition patterns
Working with refs using useRef
Using React 19's new features (use(), useOptimistic, useFormStatus)
Implementing Server Components and Actions
Working with Suspense and concurrent rendering
Building forms with new form hooks
Instructions
Identify Component Type
Determine if Server Component or Client Component is needed
Start with Hooks
Use appropriate hooks for state management and side effects
Implement Component Logic
Build component with proper TypeScript typing
Add Event Handlers
Create stable references with useCallback where needed
Optimize Performance
Use useMemo for expensive computations
Handle Errors
Implement error boundaries for graceful error handling
Test Components
Write unit tests with React Testing Library
Examples
Server Component with Client Interaction
// Server Component (default)
async
function
ProductPage
(
{
id
}
:
{
id
:
string
}
)
{
const
product
=
await
db
.
product
.
findUnique
(
{
where
:
{
id
}
}
)
;
return
(
<
div
>
<
h1
>
{
product
.
name
}
</
h1
>
<
AddToCartButton
productId
=
{
product
.
id
}
/>
</
div
>
)
;
}
// Client Component
'use client'
;
function
AddToCartButton
(
{
productId
}
:
{
productId
:
string
}
)
{
const
[
isPending
,
startTransition
]
=
useTransition
(
)
;
const
handleAdd
=
(
)
=>
{
startTransition
(
async
(
)
=>
{
await
addToCart
(
productId
)
;
}
)
;
}
;
return
(
<
button
onClick
=
{
handleAdd
}
disabled
=
{
isPending
}
>
{
isPending
?
'Adding...'
:
'Add to Cart'
}
</
button
>
)
;
}
Constraints and Warnings
Server vs Client
Server Components cannot use hooks, event handlers, or browser APIs
use() Hook
Can only be called during render, not in callbacks or effects
Server Actions
Must include 'use server' directive at the top of the file
State Mutations
Never mutate state directly; always create new references
Effect Dependencies
Always include all dependencies in useEffect arrays
Key Stability
Use stable IDs for list keys, not array indices
Memory Leaks
Always clean up subscriptions and event listeners in useEffect
Core Hooks Patterns
useState - State Management
Basic state declaration and updates:
import
{
useState
}
from
'react'
;
function
Counter
(
)
{
const
[
count
,
setCount
]
=
useState
(
0
)
;
return
(
<
button onClick
=
{
(
)
=>
setCount
(
count
+
1
)
}
>
Count
:
{
count
}
<
/
button
>
)
;
}
State with initializer function (expensive computation):
const
[
state
,
setState
]
=
useState
(
(
)
=>
{
const
initialState
=
computeExpensiveValue
(
)
;
return
initialState
;
}
)
;
Multiple state variables:
function
UserProfile
(
)
{
const
[
name
,
setName
]
=
useState
(
''
)
;
const
[
age
,
setAge
]
=
useState
(
0
)
;
const
[
email
,
setEmail
]
=
useState
(
''
)
;
return
(
<
form
>
<
input value
=
{
name
}
onChange
=
{
e
=>
setName
(
e
.
target
.
value
)
}
/
>
<
input type
=
"number"
value
=
{
age
}
onChange
=
{
e
=>
setAge
(
Number
(
e
.
target
.
value
)
)
}
/
>
<
input type
=
"email"
value
=
{
email
}
onChange
=
{
e
=>
setEmail
(
e
.
target
.
value
)
}
/
>
<
/
form
>
)
;
}
useEffect - Side Effects
Basic effect with cleanup:
import
{
useEffect
}
from
'react'
;
function
ChatRoom
(
{
roomId
}
:
{
roomId
:
string
}
)
{
useEffect
(
(
)
=>
{
const
connection
=
createConnection
(
roomId
)
;
connection
.
connect
(
)
;
// Cleanup function
return
(
)
=>
{
connection
.
disconnect
(
)
;
}
;
}
,
[
roomId
]
)
;
// Dependency array
return
<
div
>
Connected to
{
roomId
}
<
/
div
>
;
}
Effect with multiple dependencies:
function
ChatRoom
(
{
roomId
,
serverUrl
}
:
{
roomId
:
string
;
serverUrl
:
string
}
)
{
useEffect
(
(
)
=>
{
const
connection
=
createConnection
(
serverUrl
,
roomId
)
;
connection
.
connect
(
)
;
return
(
)
=>
connection
.
disconnect
(
)
;
}
,
[
roomId
,
serverUrl
]
)
;
// Re-run when either changes
return
<
h1
>
Welcome to
{
roomId
}
<
/
h1
>
;
}
Effect for subscriptions:
function
StatusBar
(
)
{
const
[
isOnline
,
setIsOnline
]
=
useState
(
true
)
;
useEffect
(
(
)
=>
{
function
handleOnline
(
)
{
setIsOnline
(
true
)
;
}
function
handleOffline
(
)
{
setIsOnline
(
false
)
;
}
window
.
addEventListener
(
'online'
,
handleOnline
)
;
window
.
addEventListener
(
'offline'
,
handleOffline
)
;
return
(
)
=>
{
window
.
removeEventListener
(
'online'
,
handleOnline
)
;
window
.
removeEventListener
(
'offline'
,
handleOffline
)
;
}
;
}
,
[
]
)
;
// Empty array = run once on mount
return
<
h1
>
{
isOnline
?
'✅ Online'
:
'❌ Disconnected'
}
<
/
h1
>
;
}
useRef - Persistent References
Storing mutable values without re-renders:
import
{
useRef
}
from
'react'
;
function
Timer
(
)
{
const
intervalRef
=
useRef
<
NodeJS
.
Timeout
|
null
>
(
null
)
;
const
startTimer
=
(
)
=>
{
intervalRef
.
current
=
setInterval
(
(
)
=>
{
console
.
log
(
'Tick'
)
;
}
,
1000
)
;
}
;
const
stopTimer
=
(
)
=>
{
if
(
intervalRef
.
current
)
{
clearInterval
(
intervalRef
.
current
)
;
}
}
;
return
(
<
>
<
button onClick
=
{
startTimer
}
>
Start
<
/
button
>
<
button onClick
=
{
stopTimer
}
>
Stop
<
/
button
>
<
/
>
)
;
}
DOM element references:
function
TextInput
(
)
{
const
inputRef
=
useRef
<
HTMLInputElement
>
(
null
)
;
const
focusInput
=
(
)
=>
{
inputRef
.
current
?.
focus
(
)
;
}
;
return
(
<
>
<
input ref
=
{
inputRef
}
type
=
"text"
/
>
<
button onClick
=
{
focusInput
}
>
Focus Input
<
/
button
>
<
/
>
)
;
}
Custom Hooks Pattern
Extract reusable logic into custom hooks:
// useOnlineStatus.ts
import
{
useState
,
useEffect
}
from
'react'
;
export
function
useOnlineStatus
(
)
{
const
[
isOnline
,
setIsOnline
]
=
useState
(
true
)
;
useEffect
(
(
)
=>
{
function
handleOnline
(
)
{
setIsOnline
(
true
)
;
}
function
handleOffline
(
)
{
setIsOnline
(
false
)
;
}
window
.
addEventListener
(
'online'
,
handleOnline
)
;
window
.
addEventListener
(
'offline'
,
handleOffline
)
;
return
(
)
=>
{
window
.
removeEventListener
(
'online'
,
handleOnline
)
;
window
.
removeEventListener
(
'offline'
,
handleOffline
)
;
}
;
}
,
[
]
)
;
return
isOnline
;
}
// Usage in components
function
StatusBar
(
)
{
const
isOnline
=
useOnlineStatus
(
)
;
return
<
h1
>
{
isOnline
?
'✅ Online'
:
'❌ Disconnected'
}
<
/
h1
>
;
}
function
SaveButton
(
)
{
const
isOnline
=
useOnlineStatus
(
)
;
return
(
<
button disabled
=
{
!
isOnline
}
>
{
isOnline
?
'Save'
:
'Reconnecting...'
}
<
/
button
>
)
;
}
Custom hook with parameters:
// useChatRoom.ts
import
{
useEffect
}
from
'react'
;
interface
ChatOptions
{
serverUrl
:
string
;
roomId
:
string
;
}
export
function
useChatRoom
(
{
serverUrl
,
roomId
}
:
ChatOptions
)
{
useEffect
(
(
)
=>
{
const
connection
=
createConnection
(
serverUrl
,
roomId
)
;
connection
.
connect
(
)
;
return
(
)
=>
connection
.
disconnect
(
)
;
}
,
[
serverUrl
,
roomId
]
)
;
}
// Usage
function
ChatRoom
(
{
roomId
}
:
{
roomId
:
string
}
)
{
const
[
serverUrl
,
setServerUrl
]
=
useState
(
'https://localhost:1234'
)
;
useChatRoom
(
{
serverUrl
,
roomId
}
)
;
return
(
<
>
<
input value
=
{
serverUrl
}
onChange
=
{
e
=>
setServerUrl
(
e
.
target
.
value
)
}
/
>
<
h1
>
Welcome to
{
roomId
}
<
/
h1
>
<
/
>
)
;
}
Component Composition Patterns
Props and Children
Basic component with props:
interface
ButtonProps
{
variant
?
:
'primary'
|
'secondary'
;
size
?
:
'sm'
|
'md'
|
'lg'
;
onClick
?
:
(
)
=>
void
;
children
:
React
.
ReactNode
;
}
function
Button
(
{
variant
=
'primary'
,
size
=
'md'
,
onClick
,
children
}
:
ButtonProps
)
{
return
(
<
button
className
=
{
`
btn btn-
${
variant
}
btn-
${
size
}
`
}
onClick
=
{
onClick
}
>
{
children
}
<
/
button
>
)
;
}
Composition with children:
interface
CardProps
{
children
:
React
.
ReactNode
;
className
?
:
string
;
}
function
Card
(
{
children
,
className
=
''
}
:
CardProps
)
{
return
(
<
div className
=
{
`
card
${
className
}
`
}
>
{
children
}
<
/
div
>
)
;
}
// Usage
function
UserProfile
(
)
{
return
(
<
Card
>
<
h2
>
John Doe
<
/
h2
>
<
p
>
Software Engineer
<
/
p
>
<
/
Card
>
)
;
}
Lifting State Up
Shared state between siblings:
function
Parent
(
)
{
const
[
activeIndex
,
setActiveIndex
]
=
useState
(
0
)
;
return
(
<
>
<
Panel
isActive
=
{
activeIndex
===
0
}
onShow
=
{
(
)
=>
setActiveIndex
(
0
)
}
>
Panel
1
content
<
/
Panel
>
<
Panel
isActive
=
{
activeIndex
===
1
}
onShow
=
{
(
)
=>
setActiveIndex
(
1
)
}
>
Panel
2
content
<
/
Panel
>
<
/
>
)
;
}
interface
PanelProps
{
isActive
:
boolean
;
onShow
:
(
)
=>
void
;
children
:
React
.
ReactNode
;
}
function
Panel
(
{
isActive
,
onShow
,
children
}
:
PanelProps
)
{
return
(
<
div
>
<
button onClick
=
{
onShow
}
>
Show
<
/
button
>
{
isActive
&&
<
div
>
{
children
}
<
/
div
>
}
<
/
div
>
)
;
}
Performance Optimization
Avoid Unnecessary Effects
❌ Bad - Using effect for derived state:
function
TodoList
(
{
todos
}
:
{
todos
:
Todo
[
]
}
)
{
const
[
visibleTodos
,
setVisibleTodos
]
=
useState
<
Todo
[
]
>
(
[
]
)
;
useEffect
(
(
)
=>
{
setVisibleTodos
(
todos
.
filter
(
t
=>
!
t
.
completed
)
)
;
}
,
[
todos
]
)
;
// Unnecessary effect
return
<
ul
>
{
/ ... /
}
<
/
ul
>
;
}
✅ Good - Compute during render:
function
TodoList
(
{
todos
}
:
{
todos
:
Todo
[
]
}
)
{
const
visibleTodos
=
todos
.
filter
(
t
=>
!
t
.
completed
)
;
// Direct computation
return
<
ul
>
{
/ ... /
}
<
/
ul
>
;
}
useMemo for Expensive Computations
import
{
useMemo
}
from
'react'
;
function
DataTable
(
{
data
}
:
{
data
:
Item
[
]
}
)
{
const
sortedData
=
useMemo
(
(
)
=>
{
return
[
...
data
]
.
sort
(
(
a
,
b
)
=>
a
.
name
.
localeCompare
(
b
.
name
)
)
;
}
,
[
data
]
)
;
// Only recompute when data changes
return
<
table
>
{
/ render sortedData /
}
<
/
table
>
;
}
useCallback for Function Stability
import
{
useCallback
}
from
'react'
;
function
Parent
(
)
{
const
[
count
,
setCount
]
=
useState
(
0
)
;
const
handleClick
=
useCallback
(
(
)
=>
{
console
.
log
(
'Clicked'
,
count
)
;
}
,
[
count
]
)
;
// Recreate only when count changes
return
<
ExpensiveChild onClick
=
{
handleClick
}
/
>
;
}
TypeScript Best Practices
Type-Safe Props
interface
UserProps
{
id
:
string
;
name
:
string
;
email
:
string
;
age
?
:
number
;
// Optional
}
function
User
(
{
id
,
name
,
email
,
age
}
:
UserProps
)
{
return
(
<
div
>
<
h2
>
{
name
}
<
/
h2
>
<
p
>
{
email
}
<
/
p
>
{
age
&&
<
p
>
Age
:
{
age
}
<
/
p
>
}
<
/
div
>
)
;
}
Generic Components
interface
ListProps
<
T
>
{
items
:
T
[
]
;
renderItem
:
(
item
:
T
)
=>
React
.
ReactNode
;
}
function
List
<
T
>
(
{
items
,
renderItem
}
:
ListProps
<
T
>
)
{
return
(
<
ul
>
{
items
.
map
(
(
item
,
index
)
=>
(
<
li key
=
{
index
}
>
{
renderItem
(
item
)
}
<
/
li
>
)
)
}
<
/
ul
>
)
;
}
// Usage
<
List
items
=
{
users
}
renderItem
=
{
(
user
)
=>
<
span
>
{
user
.
name
}
<
/
span
>
}
/
>
Event Handlers
function
Form
(
)
{
const
handleSubmit
=
(
e
:
React
.
FormEvent
<
HTMLFormElement
>
)
=>
{
e
.
preventDefault
(
)
;
// Handle form submission
}
;
const
handleChange
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
console
.
log
(
e
.
target
.
value
)
;
}
;
return
(
<
form onSubmit
=
{
handleSubmit
}
>
<
input onChange
=
{
handleChange
}
/
>
<
/
form
>
)
;
}
Common Patterns
Controlled Components
function
ControlledInput
(
)
{
const
[
value
,
setValue
]
=
useState
(
''
)
;
return
(
<
input
value
=
{
value
}
onChange
=
{
e
=>
setValue
(
e
.
target
.
value
)
}
/
>
)
;
}
Conditional Rendering
function
Greeting
(
{
isLoggedIn
}
:
{
isLoggedIn
:
boolean
}
)
{
return
(
<
div
>
{
isLoggedIn
?
(
<
UserGreeting
/
>
)
:
(
<
GuestGreeting
/
>
)
}
<
/
div
>
)
;
}
Lists and Keys
function
UserList
(
{
users
}
:
{
users
:
User
[
]
}
)
{
return
(
<
ul
>
{
users
.
map
(
user
=>
(
<
li key
=
{
user
.
id
}
>
{
user
.
name
}
<
/
li
>
)
)
}
<
/
ul
>
)
;
}
Best Practices
General React Best Practices
Dependency Arrays
Always specify correct dependencies in useEffect
State Structure
Keep state minimal and avoid redundant state
Component Size
Keep components small and focused
Custom Hooks
Extract complex logic into reusable custom hooks
TypeScript
Use TypeScript for type safety
Keys
Use stable IDs as keys for list items, not array indices
Immutability
Never mutate state directly
Effects
Use effects only for synchronization with external systems
Performance
Profile before optimizing with useMemo/useCallback
React 19 Specific Best Practices
Server Components
Use Server Components for data fetching and static content
Client Components
Mark components as 'use client' only when necessary
Actions
Use Server Actions for mutations and form submissions
Optimistic Updates
Implement useOptimistic for better UX
use() Hook
Use for reading promises and context conditionally
Form State
Use useFormState and useFormStatus for complex forms
Concurrent Features
Leverage useTransition for non-urgent updates
Error Boundaries
Implement proper error handling with error boundaries Common Pitfalls General React Pitfalls ❌ Missing Dependencies : useEffect ( ( ) => { // Uses 'count' but doesn't include it in deps console . log ( count ) ; } , [ ] ) ; // Wrong! ❌ Mutating State : const [ items , setItems ] = useState ( [ ] ) ; items . push ( newItem ) ; // Wrong! Mutates state setItems ( items ) ; // Won't trigger re-render ✅ Correct Approach : setItems ( [ ... items , newItem ] ) ; // Create new array React 19 Specific Pitfalls ❌ Using use() outside of render : // Wrong! function handleClick ( ) { const data = use ( promise ) ; // Error: use() can only be called in render } ✅ Correct usage : function Component ( { promise } ) { const data = use ( promise ) ; // Correct: called during render return < div

{ data } </ div

; } ❌ Forgetting 'use server' directive : // Wrong - missing 'use server' export async function myAction ( ) { // This will run on the client! } ✅ Correct Server Action : 'use server' ; // Must be at the top export async function myAction ( ) { // Now runs on the server } ❌ Mixing Server and Client logic incorrectly : // Wrong - trying to use browser APIs in Server Component export default async function ServerComponent ( ) { const width = window . innerWidth ; // Error: window is not defined return < div

{ width } </ div

; } ✅ Correct separation : // Server Component for data export default async function ServerComponent ( ) { const data = await fetchData ( ) ; return < ClientComponent data = { data } /> ; } // Client Component for browser APIs 'use client' ; function ClientComponent ( { data } ) { const [ width , setWidth ] = useState ( window . innerWidth ) ; // Handle resize logic... return < div

{ width } </ div

; } React 19 New Features use() Hook - Reading Resources The use() hook reads the value from a resource like a Promise or Context: import { use } from 'react' ; // Reading a Promise in a component function MessageComponent ( { messagePromise } ) { const message = use ( messagePromise ) ; return < p

{ message } < / p

; } // Reading Context conditionally function Button ( ) { if ( condition ) { const theme = use ( ThemeContext ) ; return < button className = { theme }

Click < / button

; } return < button

Click < / button

; } useOptimistic Hook - Optimistic UI Updates Manage optimistic UI updates for async operations: 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 ( < form action = { handleSubmit }

{ optimisticTodos . map ( todo => ( < div key = { todo . id }

{ todo . text } < / div

) ) } < input type = "text" name = "text" /

< button type = "submit"

Add Todo < / button

< / form

) ; } useFormStatus Hook - Form State Access form submission status from child components: import { useFormStatus } from 'react' ; function SubmitButton ( ) { const { pending , data } = useFormStatus ( ) ; return ( < button type = "submit" disabled = { pending }

{ pending ? 'Submitting...' : 'Submit' } < / button

) ; } function ContactForm ( ) { return ( < form action = { submitForm }

< input name = "email" type = "email" /

< SubmitButton /

< / form

) ; } useFormState Hook - Form State Management Manage form state with error handling: 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 ( < form action = { formAction }

< input name = "email" type = "email" /

< button type = "submit"

Subscribe < / button

{ state && < p className = "error"

{ state } < / p

} < / form

) ; } Server Actions Define server-side functions for form handling: // 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: // 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 ( < div

< h1

Latest Posts < / h1

< PostsList posts = { posts } /

< / div

) ; } // Client Component for interactivity 'use client' ; function PostsList ( { posts } : { posts : Post [ ] } ) { const [ selectedId , setSelectedId ] = useState < number | null

( null ) ; return ( < ul

{ posts . map ( post => ( < li key = { post . id } onClick = { ( ) => setSelectedId ( post . id ) } className = { selectedId === post . id ? 'selected' : '' }

{ post . title } < / li

) ) } < / ul

) ; } React Compiler Automatic Optimization React Compiler automatically optimizes your components: // Before React Compiler - manual memoization needed const ExpensiveComponent = memo ( function ExpensiveComponent ( { data , onUpdate } ) { const processedData = useMemo ( ( ) => { return data . map ( item => ( { ... item , computed : expensiveCalculation ( item ) } ) ) ; } , [ data ] ) ; const handleClick = useCallback ( ( id ) => { onUpdate ( id ) ; } , [ onUpdate ] ) ; return ( < div

{ processedData . map ( item => ( < Item key = { item . id } item = { item } onClick = { handleClick } /

) ) } < / div

) ; } ) ; // After React Compiler - no manual optimization needed function ExpensiveComponent ( { data , onUpdate } ) { const processedData = data . map ( item => ( { ... item , computed : expensiveCalculation ( item ) } ) ) ; const handleClick = ( id ) => { onUpdate ( id ) ; } ; return ( < div

{ processedData . map ( item => ( < Item key = { item . id } item = { item } onClick = { handleClick } /

) ) } < / div

) ; } Installation and Setup

Install React Compiler

npm install -D babel-plugin-react-compiler@latest

Install ESLint plugin for validation

npm
install
-D
eslint-plugin-react-hooks@latest
// babel.config.js
module
.
exports
=
{
plugins
:
[
'babel-plugin-react-compiler'
,
// Must run first!
// ... other plugins
]
,
}
;
// vite.config.js for Vite users
import
{
defineConfig
}
from
'vite'
;
import
react
from
'@vitejs/plugin-react'
;
export
default
defineConfig
(
{
plugins
:
[
react
(
{
babel
:
{
plugins
:
[
'babel-plugin-react-compiler'
]
,
}
,
}
)
,
]
,
}
)
;
Compiler Configuration
// babel.config.js with compiler options
module
.
exports
=
{
plugins
:
[
[
'babel-plugin-react-compiler'
,
{
// Enable compilation for specific files
target
:
'18'
,
// or '19'
// Debug mode for development
debug
:
process
.
env
.
NODE_ENV
===
'development'
}
]
,
]
,
}
;
// Incremental adoption with overrides
module
.
exports
=
{
plugins
:
[
]
,
overrides
:
[
{
test
:
'./src/components/*/.{js,jsx,ts,tsx}'
,
plugins
:
[
'babel-plugin-react-compiler'
]
}
]
}
;
Advanced Server Components Patterns
Mixed Server/Client Architecture
// Server Component for data fetching
async
function
ProductPage
(
{
id
}
:
{
id
:
string
}
)
{
const
product
=
await
fetchProduct
(
id
)
;
const
related
=
await
fetchRelatedProducts
(
id
)
;
return
(
<
div
>
<
ProductDetails product
=
{
product
}
/
>
<
ProductGallery images
=
{
product
.
images
}
/
>
<
RelatedProducts products
=
{
related
}
/
>
<
/
div
>
)
;
}
// Client Component for interactivity
'use client'
;
function
ProductDetails
(
{
product
}
:
{
product
:
Product
}
)
{
const
[
quantity
,
setQuantity
]
=
useState
(
1
)
;
const
[
isAdded
,
setIsAdded
]
=
useState
(
false
)
;
return
(
<
div
>
<
h1
>
{
product
.
name
}
<
/
h1
>
<
p
>
{
product
.
description
}
<
/
p
>
<
p
>
$
{
product
.
price
}
<
/
p
>
<
QuantitySelector
value
=
{
quantity
}
onChange
=
{
setQuantity
}
/
>
<
AddToCartButton
productId
=
{
product
.
id
}
quantity
=
{
quantity
}
onAdded
=
{
(
)
=>
setIsAdded
(
true
)
}
/
>
{
isAdded
&&
<
p
>
Added to cart
!
<
/
p
>
}
<
/
div
>
)
;
}
Server Actions with Validation
'use server'
;
import
{
z
}
from
'zod'
;
const
checkoutSchema
=
z
.
object
(
{
items
:
z
.
array
(
z
.
object
(
{
productId
:
z
.
string
(
)
,
quantity
:
z
.
number
(
)
.
min
(
1
)
}
)
)
,
shippingAddress
:
z
.
object
(
{
street
:
z
.
string
(
)
.
min
(
1
)
,
city
:
z
.
string
(
)
.
min
(
1
)
,
zipCode
:
z
.
string
(
)
.
regex
(
/
^
\d
{5}
$
/
)
}
)
,
paymentMethod
:
z
.
enum
(
[
'credit'
,
'paypal'
,
'apple'
]
)
}
)
;
export
async
function
processCheckout
(
prevState
:
any
,
formData
:
FormData
)
{
// Extract and validate data
const
rawData
=
{
items
:
JSON
.
parse
(
formData
.
get
(
'items'
)
as
string
)
,
shippingAddress
:
{
street
:
formData
.
get
(
'street'
)
,
city
:
formData
.
get
(
'city'
)
,
zipCode
:
formData
.
get
(
'zipCode'
)
}
,
paymentMethod
:
formData
.
get
(
'paymentMethod'
)
}
;
const
result
=
checkoutSchema
.
safeParse
(
rawData
)
;
if
(
!
result
.
success
)
{
return
{
error
:
'Validation failed'
,
fieldErrors
:
result
.
error
.
flatten
(
)
.
fieldErrors
}
;
}
try
{
// Process payment
const
order
=
await
createOrder
(
result
.
data
)
;
// Update inventory
await
updateInventory
(
result
.
data
.
items
)
;
// Send confirmation
await
sendConfirmationEmail
(
order
)
;
// Revalidate cache
revalidatePath
(
'/orders'
)
;
return
{
success
:
true
,
orderId
:
order
.
id
}
;
}
catch
(
error
)
{
return
{
error
:
'Payment failed'
}
;
}
}
Concurrent Features
useTransition for Non-Urgent Updates
import
{
useTransition
,
useState
}
from
'react'
;
function
SearchableList
(
{
items
}
:
{
items
:
Item
[
]
}
)
{
const
[
query
,
setQuery
]
=
useState
(
''
)
;
const
[
isPending
,
startTransition
]
=
useTransition
(
)
;
const
[
filteredItems
,
setFilteredItems
]
=
useState
(
items
)
;
const
handleChange
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
// Update input immediately
setQuery
(
e
.
target
.
value
)
;
// Transition the filter operation
startTransition
(
(
)
=>
{
setFilteredItems
(
items
.
filter
(
item
=>
item
.
name
.
toLowerCase
(
)
.
includes
(
e
.
target
.
value
.
toLowerCase
(
)
)
)
)
;
}
)
;
}
;
return
(
<
div
>
<
input
type
=
"text"
value
=
{
query
}
onChange
=
{
handleChange
}
placeholder
=
"Search items..."
/
>
{
isPending
&&
<
div className
=
"loading"
>
Filtering
...
<
/
div
>
}
<
ul
>
{
filteredItems
.
map
(
item
=>
(
<
li key
=
{
item
.
id
}
>
{
item
.
name
}
<
/
li
>
)
)
}
<
/
ul
>
<
/
div
>
)
;
}
useDeferredValue for Expensive UI
import
{
useDeferredValue
,
useMemo
}
from
'react'
;
function
DataGrid
(
{
data
}
:
{
data
:
DataRow
[
]
}
)
{
const
[
searchTerm
,
setSearchTerm
]
=
useState
(
''
)
;
const
deferredSearchTerm
=
useDeferredValue
(
searchTerm
)
;
const
filteredData
=
useMemo
(
(
)
=>
{
return
data
.
filter
(
row
=>
Object
.
values
(
row
)
.
some
(
value
=>
String
(
value
)
.
toLowerCase
(
)
.
includes
(
deferredSearchTerm
.
toLowerCase
(
)
)
)
)
;
}
,
[
data
,
deferredSearchTerm
]
)
;
return
(
<
div
>
<
input
value
=
{
searchTerm
}
onChange
=
{
e
=>
setSearchTerm
(
e
.
target
.
value
)
}
placeholder
=
"Search..."
className
=
{
searchTerm
!==
deferredSearchTerm
?
'stale'
:
''
}
/
>
<
DataGridRows
data
=
{
filteredData
}
isStale
=
{
searchTerm
!==
deferredSearchTerm
}
/
>
<
/
div
>
)
;
}
Testing React 19 Features
Testing Server Actions
import
{
render
,
screen
,
fireEvent
}
from
'@testing-library/react'
;
import
{
jest
}
from
'@jest/globals'
;
import
ContactForm
from
'./ContactForm'
;
// Mock server action
const
mockSubmitForm
=
jest
.
fn
(
)
;
describe
(
'ContactForm'
,
(
)
=>
{
it
(
'submits form with server action'
,
async
(
)
=>
{
render
(
<
ContactForm
/
>
)
;
fireEvent
.
change
(
screen
.
getByLabelText
(
'Email'
)
,
{
target
:
{
value
:
'test@example.com'
}
}
)
;
fireEvent
.
click
(
screen
.
getByText
(
'Submit'
)
)
;
expect
(
mockSubmitForm
)
.
toHaveBeenCalledWith
(
expect
.
any
(
FormData
)
)
;
}
)
;
it
(
'shows loading state during submission'
,
async
(
)
=>
{
mockSubmitForm
.
mockImplementation
(
(
)
=>
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
100
)
)
)
;
render
(
<
ContactForm
/
>
)
;
fireEvent
.
click
(
screen
.
getByText
(
'Submit'
)
)
;
expect
(
screen
.
getByText
(
'Submitting...'
)
)
.
toBeInTheDocument
(
)
;
await
waitFor
(
(
)
=>
{
expect
(
screen
.
getByText
(
'Submit'
)
)
.
toBeInTheDocument
(
)
;
}
)
;
}
)
;
}
)
;
Testing Optimistic Updates
import
{
render
,
screen
,
fireEvent
,
waitFor
}
from
'@testing-library/react'
;
import
{
jest
}
from
'@jest/globals'
;
import
TodoList
from
'./TodoList'
;
describe
(
'useOptimistic'
,
(
)
=>
{
it
(
'shows optimistic update immediately'
,
async
(
)
=>
{
const
mockAddTodo
=
jest
.
fn
(
(
)
=>
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
100
)
)
)
;
render
(
<
TodoList
todos
=
{
[
]
}
addTodo
=
{
mockAddTodo
}
/
>
)
;
fireEvent
.
change
(
screen
.
getByPlaceholderText
(
'Add a todo'
)
,
{
target
:
{
value
:
'New todo'
}
}
)
;
fireEvent
.
click
(
screen
.
getByText
(
'Add'
)
)
;
// Optimistic update appears immediately
expect
(
screen
.
getByText
(
'New todo'
)
)
.
toBeInTheDocument
(
)
;
// Wait for actual submission
await
waitFor
(
(
)
=>
{
expect
(
mockAddTodo
)
.
toHaveBeenCalledWith
(
{
id
:
expect
.
any
(
Number
)
,
text
:
'New todo'
}
)
;
}
)
;
}
)
;
}
)
;
Performance Best Practices
React Compiler Guidelines
Write Standard React Code
The compiler works best with idiomatic React patterns
Avoid Manual Memoization
Let the compiler handle useMemo, useCallback, and memo
Keep Components Pure
Avoid side effects in render
Use Stable References
Pass stable objects as props
// Good: Clean, idiomatic React
function
ProductCard
(
{
product
,
onAddToCart
}
)
{
const
[
quantity
,
setQuantity
]
=
useState
(
1
)
;
const
handleAdd
=
(
)
=>
{
onAddToCart
(
product
.
id
,
quantity
)
;
}
;
return
(
<
div
>
<
h3
>
{
product
.
name
}
<
/
h3
>
<
p
>
$
{
product
.
price
}
<
/
p
>
<
input
type
=
"number"
value
=
{
quantity
}
onChange
=
{
e
=>
setQuantity
(
Number
(
e
.
target
.
value
)
)
}
min
=
"1"
/
>
<
button onClick
=
{
handleAdd
}
>
Add to Cart
<
/
button
>
<
/
div
>
)
;
}
// Avoid: Manual optimization
function
ProductCard
(
{
product
,
onAddToCart
}
)
{
const
[
quantity
,
setQuantity
]
=
useState
(
1
)
;
const
handleAdd
=
useCallback
(
(
)
=>
{
onAddToCart
(
product
.
id
,
quantity
)
;
}
,
[
product
.
id
,
quantity
,
onAddToCart
]
)
;
return
(
<
div
>
<
h3
>
{
product
.
name
}
<
/
h3
>
<
p
>
$
{
product
.
price
}
<
/
p
>
<
QuantityInput
value
=
{
quantity
}
onChange
=
{
setQuantity
}
/
>
<
button onClick
=
{
handleAdd
}
>
Add to Cart
<
/
button
>
<
/
div
>
)
;
}
Server Components Best Practices
Keep Server Components Server-Only
No event handlers, hooks, or browser APIs
Minimize Client Components
Only use 'use client' when necessary
Pass Data as Props
Serialize data when passing from Server to Client
Use Server Actions for Mutations
Keep data operations on the server // Good: Server Component for static content async function ProductPage ( { id } : { id : string } ) { const product = await fetchProduct ( id ) ; return ( < article

< header

< h1

{ product . name } < / h1

< p

{ product . description } < / p

< / header

< img src = { product . imageUrl } alt = { product . name } width = { 600 } height = { 400 } /

< PriceDisplay price = { product . price } /

< AddToCartForm productId = { product . id } /

< / article

) ; } // 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 ( < form action = { handleSubmit }

< button type = "submit" disabled = { isAdding }

{ isAdding ? 'Adding...' : 'Add to Cart' } < / button

< / form

) ; } Migration Guide From React 18 to 19 Update Dependencies : npm install react@19 react-dom@19 Adopt Server Components : Identify data-fetching components Remove client-side code from Server Components Add 'use client' directive where needed Replace Manual Optimistic Updates : // 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 ) ; } ; } 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/

返回排行榜