Referensi lengkap React 19. Hooks, Server Components, Actions, Compiler, dan fitur terbaru. Perfect buat modern React development.
Panduan awal untuk memulai pengembangan aplikasi React dengan setup dan komponen dasar.
Cara menginstall React menggunakan berbagai tool modern seperti Vite atau Next.js.
# Create React App (gak direkomendasi lagi)
npx create-react-app my-app
# Vite (recommended)
npm create vite@latest my-app -- --template react
# Next.js (production-ready)
npx create-next-app@latest
# Remix
npx create-remix@latestCara membuat komponen React dasar dengan function component dan props.
// Function Component
function Welcome() {
return <h1>Hello, World!</h1>
}
// Dengan props
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>
}
// Export
export default WelcomeSintaks JSX yang memungkinkan menulis HTML dalam JavaScript untuk membuat UI React.
Dasar-dasar JSX termasuk expressions, attributes, children, dan conditional rendering.
// Expression dalam JSX
const name = 'Budi'
const element = <h1>Hello, {name}</h1>
// Attributes
const element = <img src={imageUrl} alt="Description" />
// Children
const element = (
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
)
// Fragment
return (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
)
// Conditional rendering
{isLoggedIn && <p>Welcome back!</p>}
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
// List rendering
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}Berbagai cara menambahkan styling pada komponen React dengan inline styles dan className.
// Inline styles
<div style={{ color: 'red', fontSize: '16px' }}>Text</div>
// className
<div className="container">Content</div>
// Conditional className
<div className={isActive ? 'active' : 'inactive'}>
// Multiple classes
<div className={`btn ${isActive ? 'active' : ''}`}>Cara membuat dan menggunakan komponen dengan props untuk data flow dalam React.
Komponen function modern dengan berbagai cara menangani props dan default values.
function Greeting({ name, age }) {
return (
<div>
<h1>Hello, {name}</h1>
<p>Age: {age}</p>
</div>
)
}
// Default props
function Greeting({ name = 'Guest', age = 0 }) {
return <h1>Hello, {name}</h1>
}
// Destructuring dengan rest
function Button({ children, ...props }) {
return <button {...props}>{children}</button>
}
// Usage
<Button className="primary" onClick={handleClick}>
Click me
</Button>Cara menggunakan children prop untuk membuat komponen yang fleksibel dengan konten dinamis.
function Card({ children }) {
return (
<div className="card">
{children}
</div>
)
}
// Usage
<Card>
<h2>Title</h2>
<p>Content</p>
</Card>Mengelola state lokal dalam komponen React menggunakan hook useState.
Cara dasar menggunakan useState untuk state sederhana.
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
)
}Mengelola beberapa state yang berbeda dalam satu komponen.
function Form() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [age, setAge] = useState(0)
return (
<form>
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</form>
)
}Cara mengelola state yang berupa object dengan spread operator.
function UserForm() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
})
function handleChange(e) {
setUser({
...user,
[e.target.name]: e.target.value
})
}
return (
<input
name="name"
value={user.name}
onChange={handleChange}
/>
)
}Teknik mengelola state array dengan operasi add, remove, dan update.
function TodoList() {
const [todos, setTodos] = useState([])
// Add
function addTodo(text) {
setTodos([...todos, { id: Date.now(), text }])
}
// Remove
function removeTodo(id) {
setTodos(todos.filter(todo => todo.id !== id))
}
// Update
function updateTodo(id, newText) {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
))
}
}Hook untuk menjalankan side effects dalam komponen function.
Cara dasar menggunakan useEffect untuk berbagai skenario.
import { useEffect } from 'react'
function Example() {
const [count, setCount] = useState(0)
// Run setelah every render
useEffect(() => {
document.title = `Count: ${count}`
})
// Run hanya sekali (on mount)
useEffect(() => {
console.log('Component mounted')
}, [])
// Run kalo dependencies berubah
useEffect(() => {
console.log('Count changed:', count)
}, [count])
}Cara menggunakan cleanup function untuk membersihkan effects.
useEffect(() => {
const timer = setInterval(() => {
console.log('Tick')
}, 1000)
// Cleanup function
return () => {
clearInterval(timer)
}
}, [])function UserProfile({ userId }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
let cancelled = false
async function fetchUser() {
setLoading(true)
try {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
if (!cancelled) {
setUser(data)
}
} catch (error) {
console.error(error)
} finally {
if (!cancelled) {
setLoading(false)
}
}
}
fetchUser()
return () => {
cancelled = true
}
}, [userId])
if (loading) return <div>Loading...</div>
return <div>{user.name}</div>
}import { useRef } from 'react'
function TextInput() {
const inputRef = useRef(null)
function handleClick() {
inputRef.current.focus()
}
return (
<>
<input ref={inputRef} />
<button onClick={handleClick}>Focus</button>
</>
)
}
// Store mutable value
function Timer() {
const intervalRef = useRef(null)
function start() {
intervalRef.current = setInterval(() => {
console.log('Tick')
}, 1000)
}
function stop() {
clearInterval(intervalRef.current)
}
}import { createContext, useContext, useState } from 'react'
// Create context
const ThemeContext = createContext()
// Provider
function App() {
const [theme, setTheme] = useState('light')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Page />
</ThemeContext.Provider>
)
}
// Consumer
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext)
return (
<button
style={{ background: theme === 'dark' ? '#333' : '#fff' }}
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
Toggle Theme
</button>
)
}import { useReducer } from 'react'
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
case 'reset':
return { count: 0 }
default:
throw new Error()
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 })
return (
<>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
)
}import { useMemo } from 'react'
function ExpensiveComponent({ items }) {
// Compute heavy calculation
const total = useMemo(() => {
console.log('Calculating...')
return items.reduce((sum, item) => sum + item.price, 0)
}, [items])
return <div>Total: {total}</div>
}import { useCallback } from 'react'
function Parent() {
const [count, setCount] = useState(0)
// Memoize function
const handleClick = useCallback(() => {
setCount(c => c + 1)
}, [])
return <Child onClick={handleClick} />
}
// Child gak re-render kalo handleClick gak berubah
const Child = React.memo(({ onClick }) => {
console.log('Child rendered')
return <button onClick={onClick}>Click</button>
})import { use } from 'react'
// Read promise
function UserProfile({ userPromise }) {
const user = use(userPromise)
return <div>{user.name}</div>
}
// Read context
function ThemedButton() {
const theme = use(ThemeContext)
return <button>{theme}</button>
}
// Conditional use (bisa di dalam if!)
function Component({ condition }) {
if (condition) {
const value = use(SomeContext)
return <div>{value}</div>
}
return null
}import { useActionState } from 'react'
function UpdateNameForm() {
async function updateName(prevState, formData) {
const name = formData.get('name')
try {
await saveToDatabase(name)
return { success: true, message: 'Saved!' }
} catch (error) {
return { success: false, message: error.message }
}
}
const [state, formAction, isPending] = useActionState(
updateName,
{ success: false, message: '' }
)
return (
<form action={formAction}>
<input name="name" disabled={isPending} />
<button disabled={isPending}>
{isPending ? 'Saving...' : 'Save'}
</button>
{state.message && <p>{state.message}</p>}
</form>
)
}import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
)
}
// Harus di dalam <form>
function MyForm() {
return (
<form action={handleSubmit}>
<input name="text" />
<SubmitButton />
</form>
)
}import { useOptimistic } from 'react'
function TodoList({ todos }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
)
async function handleSubmit(formData) {
const newTodo = { id: Date.now(), text: formData.get('text') }
// Update UI optimistically
addOptimisticTodo(newTodo)
// Send to server
await saveTodo(newTodo)
}
return (
<>
{optimisticTodos.map(todo => (
<li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</li>
))}
<form action={handleSubmit}>
<input name="text" />
<button>Add</button>
</form>
</>
)
}// Server Component (default di Next.js App Router)
async function UserList() {
// Fetch langsung di component
const users = await fetch('https://api.example.com/users')
.then(r => r.json())
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
// Client Component
'use client' // Directive
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}// Component bisa render metadata langsung
function BlogPost({ post }) {
return (
<>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
</>
)
}// useLocalStorage
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const item = localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
})
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value))
}, [key, value])
return [value, setValue]
}
// Usage
function App() {
const [name, setName] = useLocalStorage('name', 'Guest')
return (
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
)
}function useFetch(url) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let cancelled = false
async function fetchData() {
try {
const response = await fetch(url)
const json = await response.json()
if (!cancelled) {
setData(json)
setLoading(false)
}
} catch (e) {
if (!cancelled) {
setError(e)
setLoading(false)
}
}
}
fetchData()
return () => {
cancelled = true
}
}, [url])
return { data, loading, error }
}
// Usage
function UserProfile() {
const { data, loading, error } = useFetch('/api/user')
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return <div>{data.name}</div>
}import { memo } from 'react'
// Component cuma re-render kalo props berubah
const ExpensiveComponent = memo(function ExpensiveComponent({ value }) {
console.log('Rendering...')
return <div>{value}</div>
})
// Dengan custom comparison
const Component = memo(
function Component({ user }) {
return <div>{user.name}</div>
},
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id
}
)import { lazy, Suspense } from 'react'
// Code splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
)
}import { useState, useTransition } from 'react'
function SearchResults() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
function handleChange(e) {
setQuery(e.target.value)
// Mark expensive update as low priority
startTransition(() => {
setResults(filterResults(e.target.value))
})
}
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <div>Searching...</div>}
<ResultsList results={results} />
</>
)
}import { useDeferredValue } from 'react'
function SearchPage({ query }) {
const deferredQuery = useDeferredValue(query)
// Results akan update dengan delay
return <SearchResults query={deferredValue} />
}class ErrorBoundary extends React.Component {
constructor(props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error) {
return { hasError: true }
}
componentDidCatch(error, errorInfo) {
console.error('Error:', error, errorInfo)
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>
}
return this.props.children
}
}
// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>import { createPortal } from 'react-dom'
function Modal({ children }) {
return createPortal(
<div className="modal">
{children}
</div>,
document.body
)
}import { forwardRef } from 'react'
const Input = forwardRef((props, ref) => {
return <input ref={ref} {...props} />
})
// Usage
function Parent() {
const inputRef = useRef(null)
return <Input ref={inputRef} />
}// 1. Imports
import { useState, useEffect } from 'react'
// 2. Component
function MyComponent({ prop1, prop2 }) {
// 3. State
const [state, setState] = useState(0)
// 4. Effects
useEffect(() => {
// ...
}, [])
// 5. Event handlers
function handleClick() {
// ...
}
// 6. Render
return (
<div onClick={handleClick}>
{state}
</div>
)
}
// 7. Export
export default MyComponent// Don't mutate state directly
const [items, setItems] = useState([])
items.push(newItem) // Bad!
// Create new array
setItems([...items, newItem])
// Don't call hooks conditionally
if (condition) {
useState(0) // Bad!
}
// Always call at top level
const [state, setState] = useState(0)
// Don't forget dependencies
useEffect(() => {
doSomething(prop)
}, []) // Missing prop!
// Include all dependencies
useEffect(() => {
doSomething(prop)
}, [prop])// Index as key (avoid if possible)
{items.map((item, index) => (
<div key={index}>{item}</div>
))}
// Unique ID as key
{items.map(item => (
<div key={item.id}>{item.name}</div>
))}Login atau daftar akun gratis untuk membaca cheat sheet ini.