30 āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđ React āļāļĩāđāļāļāļāđāļāļĒāļāļĩāđāļŠāļļāļ: āļāļđāđāļĄāļ·āļāđāļāļĢāļĩāļĒāļĄāļāļąāļ§āļāļāļąāļāļŠāļĄāļāļđāļĢāļāđ
āļĢāļ§āļĄ 30 āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđ React āļāļĩāđāļāļāļāđāļāļĒāļāļĩāđāļŠāļļāļāđāļāļāļĩ 2026 āļāļĢāđāļāļĄāļāļģāļāļāļāđāļāļīāļāļĨāļķāļāđāļĨāļ°āļāļąāļ§āļāļĒāđāļēāļāđāļāđāļāđāļāļ·āđāļāļāđāļ§āļĒāđāļŦāđāļāļđāđāļŠāļĄāļąāļāļĢāļāļ§āđāļēāļāļģāđāļŦāļāđāļāļāļąāļāļāļąāļāļāļē React

āļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđāđāļāļāļāļīāļ React āļāļ°āļ§āļąāļāļāļ§āļēāļĄāđāļāđāļēāđāļāđāļāđāļāļ§āļāļīāļāļāļ·āđāļāļāļēāļ āļĢāļđāļāđāļāļāļāļąāđāļāļŠāļđāļ āđāļĨāļ°āđāļāļ§āļāļāļīāļāļąāļāļīāļāļĩāđāļāļĩ āļāļđāđāļĄāļ·āļāļāļĩāđāļĢāļ§āļāļĢāļ§āļĄ 30 āļāļģāļāļēāļĄāļāļĩāđāļāļāļāđāļāļĒāļāļĩāđāļŠāļļāļ āļāļĢāđāļāļĄāļāļģāļāļāļāđāļāļīāļāļĨāļķāļāđāļĨāļ°āļāļąāļ§āļāļĒāđāļēāļāđāļāđāļāđāļāļ·āđāļāđāļŦāđāļāļēāļĢāđāļāļĢāļĩāļĒāļĄāļāļąāļ§āđāļāđāļāđāļāļāļĒāđāļēāļāļĄāļĩāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ
āļāļģāļāļēāļĄāđāļŦāļĨāđāļēāļāļĩāđāļāļđāļāļāļąāļāđāļĢāļĩāļĒāļāļāļēāļĄāļĢāļ°āļāļąāļāļāļ§āļēāļĄāļĒāļēāļ āļāļēāļĢāļāļķāļāļāļāļāļ·āđāļāļāļēāļāđāļŦāđāđāļĄāđāļāļĒāļģāļāđāļāļāđāļāđāļēāļŠāļđāđāđāļāļ§āļāļīāļāļāļąāđāļāļŠāļđāļāļāļ°āļāđāļ§āļĒāđāļŦāđāļāļēāļĢāđāļāļĢāļĩāļĒāļĄāļāļąāļ§āļĄāļĩāđāļāļĢāļāļŠāļĢāđāļēāļāļāļĩāđāļāļąāļāđāļāļāļĒāļīāđāļāļāļķāđāļ
āļāļ·āđāļāļāļēāļāļāļāļ React
1. Virtual DOM āļāļ·āļāļāļ°āđāļĢāđāļĨāļ°āļāļģāđāļĄ React āļāļķāļāđāļāđ
Virtual DOM āļāļ·āļāļāļēāļĢāđāļāļāļāđāļēāļāļāļ DOM āļāļĢāļīāļāđāļāļĢāļđāļāđāļāļ JavaScript āļāļĩāđāļĄāļĩāļāđāļģāļŦāļāļąāļāđāļāļē React āđāļāđāļŠāļīāđāļāļāļĩāđāđāļāļ·āđāļāļāļĢāļąāļāđāļāđāļāļāļēāļĢāļāļąāļāđāļāļāļāļīāļāđāļāļāļĢāđāđāļāļāđāļŦāđāļĄāļĩāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ
āļāļĢāļ°āļāļ§āļāļāļēāļĢāļāļĢāļ°āļāļāļāļāđāļ§āļĒāļŠāļēāļĄāļāļąāđāļāļāļāļ āđāļāđāđāļāđ React āļŠāļĢāđāļēāļāļŠāļģāđāļāļēāđāļŠāļĄāļ·āļāļāļāļāļ DOM āļāđāļāļ āļāļēāļāļāļąāđāļāđāļāļĢāļĩāļĒāļāđāļāļĩāļĒāļāļŠāļģāđāļāļēāļāļąāļāļāļĨāđāļēāļ§āļāļąāļāđāļ§āļāļĢāđāļāļąāļāļāđāļāļāļŦāļāđāļēāđāļĄāļ·āđāļāļĄāļĩāļāļēāļĢāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļ (āļāļąāļĨāļāļāļĢāļīāļāļķāļĄ diffing) āđāļĨāļ°āļŠāļļāļāļāđāļēāļĒāļāļģāđāļāļāļēāļ°āļāļēāļĢāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļāļāļĩāđāļāļģāđāļāđāļāđāļāđāļāđāļāļąāļ DOM āļāļĢāļīāļ (reconciliation)
// Simplified example of the concept
// When state changes, React doesn't recreate the entire DOM
function Counter() {
const [count, setCount] = useState(0)
// Only the span containing count will be updated in the real DOM
// The rest of the component is untouched
return (
<div>
<h1>Counter</h1>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}āļ§āļīāļāļĩāļāļēāļĢāļāļĩāđāļŦāļĨāļĩāļāđāļĨāļĩāđāļĒāļāļāļēāļĢāļāļģāđāļāļīāļāļāļēāļĢāļāļĩāđāļĄāļĩāļāđāļāļāļļāļāļŠāļđāļāļāļ DOM āđāļĨāļ°āļāļģāđāļŦāđāļŠāļēāļĄāļēāļĢāļāļāļąāļāđāļāļāļāļīāļāđāļāļāļĢāđāđāļāļāļāļĩāđāļāļąāļāļāđāļāļāđāļāđāļāļĒāđāļēāļāļĢāļ§āļāđāļĢāđāļ§
2. āļāļāļĄāđāļāđāļāļāļāđāđāļāļāļāļąāļāļāđāļāļąāļāļāļąāļāđāļāļāļāļĨāļēāļŠāļāđāļēāļāļāļąāļāļāļĒāđāļēāļāđāļĢ
āļāļāļĄāđāļāđāļāļāļāđāđāļāļāļāļąāļāļāđāļāļąāļāļāļ·āļāļāļąāļāļāđāļāļąāļ JavaScript āļāļĩāđāļĢāļąāļ props āđāļĨāļ°āļāļ·āļāļāđāļēāđāļāđāļ JSX āļāļąāļāļāļąāđāļāđāļāđ React 16.8 āđāļāđāļāļāđāļāļĄāļē āļŪāļļāļāļāļāļļāļāļēāļāđāļŦāđāđāļāđ state āđāļĨāļ° lifecycle āđāļāļāļāļĄāđāļāđāļāļāļāđāđāļāļāļāļąāļāļāđāļāļąāļāđāļāđ
// Functional component (recommended)
// More concise, easier to test, supports hooks
function Welcome({ name }) {
const [visits, setVisits] = useState(0)
useEffect(() => {
setVisits(v => v + 1)
}, [])
return <h1>Hello {name}, visit #{visits}</h1>
}
// Class component (legacy)
// More verbose, requires this binding
class WelcomeClass extends React.Component {
state = { visits: 0 }
componentDidMount() {
this.setState(prev => ({ visits: prev.visits + 1 }))
}
render() {
return <h1>Hello {this.props.name}, visit #{this.state.visits}</h1>
}
}āļāļąāļāļāļļāļāļąāļāļāļāļĄāđāļāđāļāļāļāđāđāļāļāļāļąāļāļāđāļāļąāļāļāļ·āļāđāļāđāļāļĄāļēāļāļĢāļāļēāļ āļāļāļĄāđāļāđāļāļāļāđāđāļāļāļāļĨāļēāļŠāļĒāļąāļāļāļāļĢāļāļāļĢāļąāļāļāļĒāļđāđāđāļāđāđāļĄāđāđāļāļ°āļāļģāļŠāļģāļŦāļĢāļąāļāđāļāļĢāđāļāļāļāđāđāļŦāļĄāđ
3. JSX āļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢ
JSX āļāļ·āļāļŠāđāļ§āļāļāļĒāļēāļĒāđāļ§āļĒāļēāļāļĢāļāđāļāļāļ JavaScript āļāļĩāđāļāļāļļāļāļēāļāđāļŦāđāđāļāļĩāļĒāļāļĄāļēāļĢāđāļāļāļąāļāļ āļēāļĒāđāļāđāļāđāļ JSX āđāļĄāđāđāļāđ HTML āđāļāđāđāļāđāļ JavaScript āđāļāļĢāļđāļāđāļāļāļāļĩāđāļāđāļēāļāļāđāļēāļĒāļāļ§āđāļē
// What we write (JSX)
const element = (
<div className="container">
<h1>Title</h1>
<p>Paragraph</p>
</div>
)
// What Babel compiles (pure JavaScript)
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Title'),
React.createElement('p', null, 'Paragraph')
)āļāļ§āļēāļĄāđāļāļāļāđāļēāļāļāļēāļ HTML āđāļāđāđāļāđ āļāļēāļĢāđāļāđ className āđāļāļ class, htmlFor āđāļāļ for, āļāļēāļĢāđāļāđ camelCase āļŠāļģāļŦāļĢāļąāļāđāļāļāļāļĢāļīāļāļīāļ§āļāđ (onClick, tabIndex) āđāļĨāļ°āļāđāļāļāļāļīāļāđāļāđāļāļāļĩāđāļāļīāļāđāļāļāļąāļ§āđāļāļāđāļŠāļĄāļ
4. state āļāļąāļ props āļāđāļēāļāļāļąāļāļāļĒāđāļēāļāđāļĢ
Props āļāļ·āļāļāđāļāļĄāļđāļĨāļāļĩāđāļŠāđāļāļāļēāļāļāļāļĄāđāļāđāļāļāļāđāđāļĄāđāđāļāļĒāļąāļāļĨāļđāļ āđāļāđāļāļāđāļāļĄāļđāļĨāļāļĩāđāļāđāļēāļāđāļāđāļāļĒāđāļēāļāđāļāļĩāļĒāļ§ āļŠāđāļ§āļ state āļāļ·āļāļŠāļāļēāļāļ°āļ āļēāļĒāđāļāļāļāļāļāļāļĄāđāļāđāļāļāļāđāļāļĩāđāļŠāļēāļĄāļēāļĢāļāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļāļāđāļēāļ setter āđāļāđ
// name and role are props (immutable)
function UserCard({ name, role }) {
// isExpanded is state (mutable)
const [isExpanded, setIsExpanded] = useState(false)
return (
<div className="card">
<h2>{name}</h2>
<p>{role}</p>
{/* Modifying state triggers a re-render */}
<button onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? 'Collapse' : 'Details'}
</button>
{isExpanded && <UserDetails name={name} />}
</div>
)
}
// Usage
<UserCard name="Alice" role="Developer" />āļŦāļĨāļąāļāļāļēāļĢāļāļ·āđāļāļāļēāļāļāļ·āļ props āđāļŦāļĨāļāļēāļāļāļāļĨāļāļĨāđāļēāļ (āļāđāļāđāļĄāđāđāļāļĒāļąāļāļĨāļđāļ) āļŠāđāļ§āļ state āđāļāđāļāļāļāļāđāļāđāļĨāļ°āļāļāļĄāđāļāđāļāļāļāđāđāļāļĒāđāļāļāļēāļ°
5. āļāļģāđāļĄ key āđāļāļĢāļēāļĒāļāļēāļĢāļāļķāļāļŠāļģāļāļąāļ
Key āļāđāļ§āļĒāđāļŦāđ React āļĢāļ°āļāļļāđāļāđāļ§āđāļēāļĄāļĩāļāļāļāđāļāļĢāļ°āļāļāļāđāļāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļ āđāļāļīāđāļĄ āļŦāļĢāļ·āļāļāļđāļāļĨāļāļāļāļāļāļēāļāļĢāļēāļĒāļāļēāļĢ āļŦāļēāļāđāļĄāđāļĄāļĩ key āļāļĩāđāđāļĄāđāļāđāļģāđāļĨāļ°āļāļāļāļĩāđ React āļāļēāļāđāļŠāļāļāļāļĪāļāļīāļāļĢāļĢāļĄāļāļĩāđāđāļĄāđāļāļēāļāļāļīāļ
// â Bad practice: index as key
// Problem: if order changes, React loses tracking
{items.map((item, index) => (
<ListItem key={index} item={item} />
))}
// â
Good practice: unique and stable identifier
{items.map(item => (
<ListItem key={item.id} item={item} />
))}
// Concrete example of the problem with indices
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React' },
{ id: 2, text: 'Create a project' }
])
// When deleting the first element with key={index}
// React will think element 0's content changed
// instead of understanding an element was removed
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
)
}React Hooks
6. āļāļāļīāļāļēāļĒ useState āđāļĨāļ°āļāđāļāļāļīāļāļāļĨāļēāļāļāļĩāđāļāļāļāđāļāļĒ
useState āđāļāđāļāļąāļāļāļēāļĢ state āļ āļēāļĒāđāļāļāļāļĄāđāļāđāļāļāļāđāđāļāļāļāļąāļāļāđāļāļąāļ setter āļĢāļąāļāđāļāđāļāļąāđāļāļāđāļēāđāļŦāļĄāđāļŦāļĢāļ·āļāļāļąāļāļāđāļāļąāļāļŠāļģāļŦāļĢāļąāļāļāļąāļāđāļāļ
// Declaration with initial value
const [count, setCount] = useState(0)
// â Pitfall: multiple updates in the same cycle
function increment() {
setCount(count + 1) // count = 0, sets 1
setCount(count + 1) // count = 0 still, sets 1
setCount(count + 1) // count = 0 still, sets 1
// Final result: 1 (not 3)
}
// â
Solution: use the update function
function incrementCorrect() {
setCount(prev => prev + 1) // 0 â 1
setCount(prev => prev + 1) // 1 â 2
setCount(prev => prev + 1) // 2 â 3
// Final result: 3
}
// â Pitfall: object mutation
const [user, setUser] = useState({ name: 'Alice', age: 25 })
user.age = 26 // Direct mutation, no re-render
// â
Solution: create a new object
setUser({ ...user, age: 26 })
// or
setUser(prev => ({ ...prev, age: 26 }))7. useEffect āļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢāļāļąāļāļāļēāļĢāđāđāļĢāļĒāđāļāļāļ dependency
useEffect āļāļģāļāļēāļ side effect āļŦāļĨāļąāļāļāļēāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđ āļāļēāļĢāđāđāļĢāļĒāđāļāļāļ dependency āļāļ§āļāļāļļāļĄāļ§āđāļēāđāļāļāđāļāļāļāđāļāļ°āļāļđāļāđāļĢāļĩāļĒāļāđāļĄāļ·āđāļāđāļ
// Executed on every render (rare, usually avoid)
useEffect(() => {
console.log('Render completed')
})
// Executed only on mount (equivalent to componentDidMount)
useEffect(() => {
console.log('Component mounted')
// Cleanup on unmount (equivalent to componentWillUnmount)
return () => {
console.log('Component unmounted')
}
}, [])
// Executed when userId changes
useEffect(() => {
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
setUser(data)
}
fetchUser()
}, [userId])
// â Missing dependency - subtle bug
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1) // count is "captured" at its initial value
}, 1000)
return () => clearInterval(timer)
}, []) // count is missing from dependencies
// â
Fix with update function
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1) // No need for count in deps
}, 1000)
return () => clearInterval(timer)
}, [])āļāļ§āļĢāđāļāļīāļāđāļāđāļāļēāļ eslint-plugin-react-hooks āđāļŠāļĄāļāđāļāļ·āđāļāļāļĢāļ§āļāļāļąāļ dependency āļāļĩāđāļāļēāļāļŦāļēāļĒāđāļ āļāļāļāļĩāđāļāđāļ§āļĒāļāđāļāļāļāļąāļāļāļąāđāļāļāļĩāđāļ§āļīāļāļīāļāļāļąāļĒāđāļāđāļĒāļēāļāļāļģāļāļ§āļāļĄāļēāļ
8. āđāļĄāļ·āđāļāđāļāļāļ§āļĢāđāļāđ useMemo āđāļĨāļ° useCallback
āļŪāļļāļāļāļąāđāļāļŠāļāļāļāļĩāđāđāļāđāļŠāļģāļŦāļĢāļąāļ memoization āđāļāļ·āđāļāļŦāļĨāļĩāļāđāļĨāļĩāđāļĒāļāļāļēāļĢāļāļģāļāļ§āļāļāđāļģāļŦāļĢāļ·āļāļāļēāļĢāļŠāļĢāđāļēāļāļāđāļēāđāļŦāļĄāđāđāļāļĒāđāļĄāđāļāļģāđāļāđāļ āļāļ§āļĢāļĢāļ°āļ§āļąāļāđāļĄāđāđāļāđāļĄāļēāļāđāļāļīāļāđāļ
// useMemo: memoizes a computed value
function ProductList({ products, filter }) {
// Recalculated only if products or filter change
const filteredProducts = useMemo(() => {
console.log('Filtering...')
return products.filter(p => p.category === filter)
}, [products, filter])
return <ul>{filteredProducts.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
// useCallback: memoizes a function
function ParentComponent() {
const [count, setCount] = useState(0)
// Without useCallback, handleClick is recreated on every render
// Causing unnecessary re-renders of ExpensiveChild
const handleClick = useCallback((id) => {
console.log('Clicked:', id)
}, []) // Empty deps = stable function
return (
<>
<span>{count}</span>
<button onClick={() => setCount(c => c + 1)}>+</button>
{/* React.memo on ExpensiveChild for this to be effective */}
<ExpensiveChild onClick={handleClick} />
</>
)
}
// â Over-optimization: not needed here
const SimpleComponent = () => {
// This calculation is trivial, useMemo adds overhead
const doubled = useMemo(() => 2 * 2, [])
return <span>{doubled}</span>
}āļāļ§āļĢāđāļāđāļŪāļļāļāđāļŦāļĨāđāļēāļāļĩāđāđāļāļāļēāļ°āđāļĄāļ·āđāļāļāļāļāļąāļāļŦāļēāļāđāļēāļāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļāļāļĩāđāļāļąāļāđāļāļ āļŦāļĢāļ·āļāđāļĄāļ·āđāļāļāđāļāļāļāļēāļĢāļĢāļąāļāļĐāļē reference āļāļĩāđāļŠāđāļāđāļŦāđāļāļąāļāļāļāļĄāđāļāđāļāļāļāđāļāļĩāđāļāļđāļ memoize āđāļ§āđāđāļŦāđāļāļāļāļĩāđ
9. useRef āļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢāđāļĨāļ°āđāļāđāđāļāļāļĢāļāļĩāđāļāļāđāļēāļ
useRef āļŠāļĢāđāļēāļ reference āļāļĩāđāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļāđāļāđāđāļĨāļ°āļāļāļāļĒāļđāđāļĢāļ°āļŦāļ§āđāļēāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđ āđāļāļĒāđāļĄāđāļāļģāđāļŦāđāđāļāļīāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāđāļŦāļĄāđāđāļĄāļ·āđāļāļĄāļĩāļāļēāļĢāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļāļāđāļē
// Case 1: Access a DOM element
function TextInput() {
const inputRef = useRef(null)
const focusInput = () => {
inputRef.current.focus()
}
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus</button>
</>
)
}
// Case 2: Store a mutable value without re-render
function Timer() {
const [seconds, setSeconds] = useState(0)
const intervalRef = useRef(null)
const start = () => {
// Store the interval ID to be able to stop it
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1)
}, 1000)
}
const stop = () => {
clearInterval(intervalRef.current)
}
return (
<div>
<span>{seconds}s</span>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
)
}
// Case 3: Keep the previous value
function usePrevious(value) {
const ref = useRef()
useEffect(() => {
ref.current = value
}, [value])
return ref.current
}10. āļāļāļīāļāļēāļĒ useContext āđāļĨāļ°āđāļĄāļ·āđāļāđāļāļāļ§āļĢāđāļāđ
useContext āļāđāļ§āļĒāđāļŦāđāđāļāđāļēāļāļķāļ context āļāļāļ React āđāļāļĒāđāļĄāđāļāđāļāļāļŠāđāļ props āļāļāļāļāđāļāļāļąāļāļŦāļĨāļēāļĒāļāļąāđāļ āđāļŦāļĄāļēāļ°āļŠāļģāļŦāļĢāļąāļāļāđāļāļĄāļđāļĨāđāļāļ global āđāļāđāļ āļāļĩāļĄāļŦāļĢāļ·āļāļāļđāđāđāļāđāļāļĩāđāļĨāđāļāļāļāļīāļāļāļĒāļđāđ
const ThemeContext = createContext({
theme: 'light',
toggleTheme: () => {}
})
// 2. Create the provider
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light')
const toggleTheme = useCallback(() => {
setTheme(prev => prev === 'light' ? 'dark' : 'light')
}, [])
// Memoize the value to avoid unnecessary re-renders
const value = useMemo(() => ({ theme, toggleTheme }), [theme, toggleTheme])
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
)
}
// 3. Use the context
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext)
return (
<button
onClick={toggleTheme}
className={theme === 'dark' ? 'bg-gray-800 text-white' : 'bg-white text-gray-800'}
>
{theme === 'dark' ? 'Light' : 'Dark'} mode
</button>
)
}
// 4. Wrap the application
function App() {
return (
<ThemeProvider>
<Header />
<Main />
<Footer />
</ThemeProvider>
)
}āļāļĢāđāļāļĄāļāļĩāđāļāļ°āļāļīāļāļīāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ React / Next.js āđāļĨāđāļ§āļŦāļĢāļ·āļāļĒāļąāļāļāļĢāļąāļ?
āļāļķāļāļāļāļāđāļ§āļĒāļāļąāļ§āļāļģāļĨāļāļāđāļāļāđāļāđāļāļāļ, flashcards āđāļĨāļ°āđāļāļāļāļāļŠāļāļāđāļāļāļāļīāļāļāļĢāļąāļ
āļĢāļđāļāđāļāļāļāļąāđāļāļŠāļđāļ
11. Higher-Order Component (HOC) āļāļ·āļāļāļ°āđāļĢ
HOC āļāļ·āļāļāļąāļāļāđāļāļąāļāļāļĩāđāļĢāļąāļāļāļāļĄāđāļāđāļāļāļāđāđāļāđāļēāļĄāļēāđāļĨāļ°āļāļ·āļāļāđāļēāđāļāđāļāļāļāļĄāđāļāđāļāļāļāđāđāļŦāļĄāđāļāļĩāđāļāļđāļāđāļŠāļĢāļīāļĄāļāļ§āļēāļĄāļŠāļēāļĄāļēāļĢāļ āđāļāđāļāđāļāļĒāļĨāļāļāļąāđāļāđāļāđāļĄāļĩāļŪāļļāļ āđāļāđāļĒāļąāļāļāļāļāļāđāļāđāđāļāđāļĨāļāļĢāļēāļĢāļĩāļāļēāļāļāļąāļ§
// HOC that adds logging
function withLogging(WrappedComponent) {
return function WithLogging(props) {
useEffect(() => {
console.log(`${WrappedComponent.name} mounted with props:`, props)
return () => {
console.log(`${WrappedComponent.name} unmounted`)
}
}, [])
return <WrappedComponent {...props} />
}
}
// HOC that handles authentication
function withAuth(WrappedComponent) {
return function WithAuth(props) {
const { user, isLoading } = useAuth()
if (isLoading) return <LoadingSpinner />
if (!user) return <Navigate to="/login" />
return <WrappedComponent {...props} user={user} />
}
}
// Usage
const ProtectedDashboard = withAuth(Dashboard)
const LoggedButton = withLogging(Button)12. āļāļāļīāļāļēāļĒāļĢāļđāļāđāļāļ Render Props
āļĢāļđāļāđāļāļ Render Props āļāđāļ§āļĒāđāļāđāļāļāļąāļāļĨāļāļāļīāļāļĢāļ°āļŦāļ§āđāļēāļāļāļāļĄāđāļāđāļāļāļāđāđāļāļĒāļŠāđāļāļāđāļēāļ prop āļāļĩāđāđāļāđāļāļāļąāļāļāđāļāļąāļ
// Component with render prop
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => {
const handleMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY })
}
window.addEventListener('mousemove', handleMove)
return () => window.removeEventListener('mousemove', handleMove)
}, [])
// Call the render function with data
return render(position)
}
// Usage
function App() {
return (
<MouseTracker
render={({ x, y }) => (
<div>
Position: {x}, {y}
</div>
)}
/>
)
}
// Modern version with custom hook (preferred)
function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => {
const handleMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY })
}
window.addEventListener('mousemove', handleMove)
return () => window.removeEventListener('mousemove', handleMove)
}, [])
return position
}
function App() {
const { x, y } = useMousePosition()
return <div>Position: {x}, {y}</div>
}13. āļ§āļīāļāļĩāļŠāļĢāđāļēāļ custom hook
Custom hook āđāļāđāļŠāļģāļŦāļĢāļąāļāđāļĒāļāđāļĨāļ°āļāļģāļĨāļāļāļīāļāļāļĩāđāļĄāļĩ state āđāļāđāļāđāļĢāđāļ§āļĄāļāļąāļāļĢāļ°āļŦāļ§āđāļēāļāļāļāļĄāđāļāđāļāļāļāđāļāđāļēāļāđ
function useLocalStorage(key, initialValue) {
// Initialize with localStorage value or default
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
console.error(error)
return initialValue
}
})
// Setter wrapper that syncs with localStorage
const setValue = useCallback((value) => {
try {
// Support update functions
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
console.error(error)
}
}, [key, storedValue])
return [storedValue, setValue]
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light')
const [fontSize, setFontSize] = useLocalStorage('fontSize', 16)
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<input
type="range"
min="12"
max="24"
value={fontSize}
onChange={e => setFontSize(Number(e.target.value))}
/>
</div>
)
}14. āļĢāļđāļāđāļāļ Compound Components āļāļ·āļāļāļ°āđāļĢ
āļĢāļđāļāđāļāļāļāļĩāđāļāđāļ§āļĒāļŠāļĢāđāļēāļāļāļāļĄāđāļāđāļāļāļāđāļāļĩāđāļāļģāļāļēāļāļĢāđāļ§āļĄāļāļąāļāđāļāļĒāļāļĢāļīāļĒāļēāļĒ āļāļĨāđāļēāļĒāļāļąāļāļāļēāļĢāđāļāđāđāļāđāļ <select> āđāļĨāļ° <option>
// Shared context between components
const TabsContext = createContext()
function Tabs({ children, defaultValue }) {
const [activeTab, setActiveTab] = useState(defaultValue)
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
)
}
function TabList({ children }) {
return <div className="tab-list" role="tablist">{children}</div>
}
function Tab({ value, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext)
const isActive = activeTab === value
return (
<button
role="tab"
aria-selected={isActive}
className={`tab ${isActive ? 'active' : ''}`}
onClick={() => setActiveTab(value)}
>
{children}
</button>
)
}
function TabPanel({ value, children }) {
const { activeTab } = useContext(TabsContext)
if (activeTab !== value) return null
return (
<div role="tabpanel" className="tab-panel">
{children}
</div>
)
}
// Attach sub-components
Tabs.List = TabList
Tabs.Tab = Tab
Tabs.Panel = TabPanel
// Intuitive usage
function App() {
return (
<Tabs defaultValue="profile">
<Tabs.List>
<Tabs.Tab value="profile">Profile</Tabs.Tab>
<Tabs.Tab value="settings">Settings</Tabs.Tab>
<Tabs.Tab value="billing">Billing</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="profile">Profile content...</Tabs.Panel>
<Tabs.Panel value="settings">Settings content...</Tabs.Panel>
<Tabs.Panel value="billing">Billing content...</Tabs.Panel>
</Tabs>
)
}15. āļ§āļīāļāļĩāļāļģāļĢāļđāļāđāļāļ Controlled vs Uncontrolled āđāļāđāļāđ
āļāļāļĄāđāļāđāļāļāļāđāđāļāļ controlled āļĄāļĩ state āļāļĩāđāļāļđāļāļāļąāļāļāļēāļĢāđāļāļĒ React āļŠāđāļ§āļāđāļāļ uncontrolled āđāļāđ DOM āđāļāļĒāļāļĢāļ
// CONTROLLED component
// State is in React, component reflects that state
function ControlledInput() {
const [value, setValue] = useState('')
const handleSubmit = (e) => {
e.preventDefault()
console.log('Submitted value:', value)
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
)
}
// UNCONTROLLED component
// State is in the DOM, we read it when needed
function UncontrolledInput() {
const inputRef = useRef(null)
const handleSubmit = (e) => {
e.preventDefault()
console.log('Submitted value:', inputRef.current.value)
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
ref={inputRef}
defaultValue=""
/>
<button type="submit">Submit</button>
</form>
)
}
// Component that supports BOTH modes
function FlexibleInput({ value, defaultValue, onChange }) {
const isControlled = value !== undefined
const [internalValue, setInternalValue] = useState(defaultValue || '')
const currentValue = isControlled ? value : internalValue
const handleChange = (e) => {
if (!isControlled) {
setInternalValue(e.target.value)
}
onChange?.(e.target.value)
}
return (
<input
type="text"
value={currentValue}
onChange={handleChange}
/>
)
}āļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļāđāļĨāļ°āļāļēāļĢāđāļāļīāđāļĄāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ
16. React.memo āļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢ
React.memo āļāļ·āļ HOC āļāļĩāđāļāļģāļāļēāļĢ memoize āļāļāļĄāđāļāđāļāļāļāđāđāļāļ·āđāļāļŦāļĨāļĩāļāđāļĨāļĩāđāļĒāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāđāļŦāļĄāđāļŦāļēāļ props āđāļĄāđāđāļāļĨāļĩāđāļĒāļāđāļāļĨāļ
// Memoized component
const ExpensiveList = React.memo(function ExpensiveList({ items, onItemClick }) {
console.log('ExpensiveList rendered')
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
)
})
// Parent using the memoized component
function Parent() {
const [count, setCount] = useState(0)
const [items] = useState([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
])
// â This function is recreated on every render
// So ExpensiveList re-renders despite memo
const handleClick = (id) => console.log(id)
// â
Stable function with useCallback
const handleClickStable = useCallback((id) => {
console.log(id)
}, [])
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveList items={items} onItemClick={handleClickStable} />
</div>
)
}
// Custom comparison
const MemoWithCustomCompare = React.memo(
function Component({ user, onClick }) {
return <div onClick={onClick}>{user.name}</div>
},
(prevProps, nextProps) => {
// Return true if props are equal (skip re-render)
return prevProps.user.id === nextProps.user.id
}
)17. Code splitting āđāļĨāļ° React.lazy āļāļ·āļāļāļ°āđāļĢ
Code splitting āđāļāđāļāļāļąāļāđāļāļīāļĨāļāļāļāđāļāđāļ chunk āļāļĩāđāđāļŦāļĨāļāļāļēāļĄāļāđāļāļāļāļēāļĢ āļāđāļ§āļĒāļĨāļāđāļ§āļĨāļēāđāļāļāļēāļĢāđāļŦāļĨāļāļāļĢāļąāđāļāđāļĢāļ
// Lazy loading components
const Dashboard = lazy(() => import('./Dashboard'))
const Settings = lazy(() => import('./Settings'))
const Profile = lazy(() => import('./Profile'))
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</Suspense>
)
}
// Conditional lazy loading
function FeaturePanel({ showAdvanced }) {
// Component is only loaded if needed
const AdvancedOptions = lazy(() => import('./AdvancedOptions'))
return (
<div>
<BasicOptions />
{showAdvanced && (
<Suspense fallback={<Skeleton />}>
<AdvancedOptions />
</Suspense>
)}
</div>
)
}
// Named exports with lazy
const { Chart } = lazy(() =>
import('./Charts').then(module => ({ default: module.Chart }))
)18. āļ§āļīāļāļĩāļāļĢāļąāļāđāļāđāļāļĢāļēāļĒāļāļēāļĢāļāļĩāđāļĄāļĩāļāļ§āļēāļĄāļĒāļēāļ§āļĄāļēāļ
āļĢāļēāļĒāļāļēāļĢāļāļĩāđāļĒāļēāļ§āļĄāļēāļāļāļēāļāļāļģāđāļŦāđāđāļāļīāļāļāļąāļāļŦāļēāļāđāļēāļāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ āļāļēāļĢāļāļģ virtualization āļāļ°āđāļĢāļāđāļāļāļĢāđāđāļāļāļēāļ°āļāļāļāđāļāļĢāļ°āļāļāļāļāļĩāđāļĄāļāļāđāļŦāđāļāđāļāđ
// With react-window (virtualization library)
import { FixedSizeList } from 'react-window'
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style} className="list-item">
{items[index].name}
</div>
)
return (
<FixedSizeList
height={400}
width="100%"
itemCount={items.length}
itemSize={50}
>
{Row}
</FixedSizeList>
)
}
// Optimization without library: pagination
function PaginatedList({ items, pageSize = 20 }) {
const [page, setPage] = useState(0)
const paginatedItems = useMemo(() => {
const start = page * pageSize
return items.slice(start, start + pageSize)
}, [items, page, pageSize])
const totalPages = Math.ceil(items.length / pageSize)
return (
<div>
<ul>
{paginatedItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<div className="pagination">
<button
onClick={() => setPage(p => p - 1)}
disabled={page === 0}
>
Previous
</button>
<span>{page + 1} / {totalPages}</span>
<button
onClick={() => setPage(p => p + 1)}
disabled={page >= totalPages - 1}
>
Next
</button>
</div>
</div>
)
}Virtualization āļāļ°āđāļĢāļīāđāļĄāļĄāļĩāļāļĢāļ°āđāļĒāļāļāđāđāļĄāļ·āđāļāļĢāļēāļĒāļāļēāļĢāļĄāļĩāļāļāļāđāļāļĢāļ°āļāļāļāđāļāļīāļāđāļĄāđāļāļĩāđāļĢāđāļāļĒāļĢāļēāļĒāļāļēāļĢ āļŠāļģāļŦāļĢāļąāļāļĢāļēāļĒāļāļēāļĢāļāļĩāđāđāļĨāđāļāļāļ§āđāļēāļāļąāđāļ āļāļēāļĢāđāļāđ pagination āļŦāļĢāļ·āļāļāļēāļĢāđāļŦāļĨāļāđāļāļāļāļĒāļāļĒāļĄāļąāļāđāļāļĩāļĒāļāļāļāđāļĨāđāļ§
19. āļ§āļīāļāļĩāļŦāļĨāļĩāļāđāļĨāļĩāđāļĒāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāļāđāļģāļāļĩāđāđāļĄāđāļāļģāđāļāđāļ
āļāļēāļĢāļĢāļ°āļāļļāđāļĨāļ°āļāļģāļāļąāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāļāđāļģāļāļĩāđāđāļĄāđāļāļģāđāļāđāļāđāļāđāļāļŠāļīāđāļāļŠāļģāļāļąāļāļāđāļāļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ āļĄāļĩāđāļāļāļāļīāļāļŦāļĨāļēāļĒāļāļĒāđāļēāļāļāļĩāđāļāđāļ§āļĒāļāļĢāļąāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāđāļāđ
// Technique 1: Separate components
// â Everything re-renders when count changes
function BadExample() {
const [count, setCount] = useState(0)
return (
<div>
<button onClick={() => setCount(c => c + 1)}>{count}</button>
<ExpensiveComponent /> {/* Unnecessary re-render */}
</div>
)
}
// â
ExpensiveComponent no longer re-renders
function GoodExample() {
return (
<div>
<Counter />
<ExpensiveComponent />
</div>
)
}
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// Technique 2: Pass children instead of rendering directly
// â
children is stable, no re-render of children
function ContextProvider({ children }) {
const [value, setValue] = useState(0)
return (
<Context.Provider value={value}>
{children} {/* children doesn't re-render when value changes */}
</Context.Provider>
)
}
// Technique 3: Use DevTools to identify re-renders
// React DevTools > Profiler > "Highlight updates when components render"20. Automatic batching āđāļ React 18+ āļāļ·āļāļāļ°āđāļĢ
React 18 āļāļ°āļĢāļ§āļĄāļāļēāļĢāļāļąāļāđāļāļ state āđāļāļĒāļāļąāļāđāļāļĄāļąāļāļīāđāļāļ·āđāļāļĨāļāļāļģāļāļ§āļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāļāđāļģ
// Before React 18: two re-renders
function handleClick() {
setCount(c => c + 1) // Re-render
setFlag(f => !f) // Re-render
}
// React 18+: single re-render (automatic batching)
function handleClick() {
setCount(c => c + 1) // Batched
setFlag(f => !f) // Batched
// Single re-render at the end
}
// Even in async callbacks (React 18 novelty)
async function handleSubmit() {
const response = await fetch('/api/submit')
// These two updates are batched
setData(response.data)
setLoading(false)
// Single re-render
}
// To force immediate re-render (rare)
import { flushSync } from 'react-dom'
function handleClick() {
flushSync(() => {
setCount(c => c + 1)
})
// DOM updated here
flushSync(() => {
setFlag(f => !f)
})
// DOM updated here
}āļāļĢāđāļāļĄāļāļĩāđāļāļ°āļāļīāļāļīāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ React / Next.js āđāļĨāđāļ§āļŦāļĢāļ·āļāļĒāļąāļāļāļĢāļąāļ?
āļāļķāļāļāļāļāđāļ§āļĒāļāļąāļ§āļāļģāļĨāļāļāđāļāļāđāļāđāļāļāļ, flashcards āđāļĨāļ°āđāļāļāļāļāļŠāļāļāđāļāļāļāļīāļāļāļĢāļąāļ
React āļŠāļĄāļąāļĒāđāļŦāļĄāđ (18+)
21. āļāļāļīāļāļēāļĒ useTransition āđāļĨāļ° useDeferredValue
āļŪāļļāļāļāļąāđāļāļŠāļāļāļāļĩāđāđāļāđāļŠāļģāļŦāļĢāļąāļāļāļģāđāļāļĢāļ·āđāļāļāļŦāļĄāļēāļĒāļ§āđāļēāļāļąāļāđāļāļāđāļāđāļāđāļāđāļāļāđāļĄāđāđāļĢāđāļāļāđāļ§āļ āđāļāļ·āđāļāļĢāļąāļāļĐāļēāđāļŦāđāļāļīāļāđāļāļāļĢāđāđāļāļāļāļāļāļŠāļāļāļāđāļāđāļĢāļ§āļāđāļĢāđāļ§
// useTransition: mark an update as non-urgent
function SearchResults() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
const handleChange = (e) => {
// Urgent update: input stays responsive
setQuery(e.target.value)
// Non-urgent update: can be interrupted
startTransition(() => {
const filtered = filterLargeDataset(e.target.value)
setResults(filtered)
})
}
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <span>Searching...</span>}
<ul>
{results.map(r => <li key={r.id}>{r.name}</li>)}
</ul>
</div>
)
}
// useDeferredValue: defer a value
function DeferredSearch() {
const [query, setQuery] = useState('')
// deferredQuery is "behind" during rapid updates
const deferredQuery = useDeferredValue(query)
// Show indicator during the "lag"
const isStale = query !== deferredQuery
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
<div style={{ opacity: isStale ? 0.5 : 1 }}>
{/* Uses deferred value, fewer calculations */}
<ExpensiveList filter={deferredQuery} />
</div>
</div>
)
}22. Suspense āļŠāļģāļŦāļĢāļąāļāļāļēāļĢāļāļķāļāļāđāļāļĄāļđāļĨāļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢ
Suspense āļāļąāļāļāļēāļĢāļŠāļāļēāļāļ°āļāļēāļĢāđāļŦāļĨāļāđāļāļĢāļđāļāđāļāļāđāļāļīāļāļāļĢāļ°āļāļēāļĻ āđāļĨāļ°āļāļąāđāļāđāļāđ React 18+ āļŠāļēāļĄāļēāļĢāļāđāļāđāļāļąāļāļāļēāļĢāļāļķāļāļāđāļāļĄāļđāļĨāđāļāđ
function UserProfile({ userId }) {
return (
<Suspense fallback={<ProfileSkeleton />}>
<ProfileContent userId={userId} />
</Suspense>
)
}
// The component "suspends" during loading
function ProfileContent({ userId }) {
// This function suspends if data isn't ready
const user = useUserData(userId)
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
)
}
// Nested Suspense boundaries
function Dashboard() {
return (
<Suspense fallback={<DashboardSkeleton />}>
<Header />
<div className="grid">
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
<Suspense fallback={<ChartSkeleton />}>
<Chart />
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentActivity />
</Suspense>
</div>
</Suspense>
)
}23. useActionState (React 19) āļāļ·āļāļāļ°āđāļĢ
useActionState (āļāļ·āđāļāđāļāļīāļĄ useFormState) āļāđāļ§āļĒāđāļŦāđāļāļēāļĢāļāļąāļāļāļēāļĢāļāļāļĢāđāļĄāļĢāđāļ§āļĄāļāļąāļ Server Action āļāđāļēāļĒāļāļķāđāļ
'use server'
async function submitForm(prevState, formData) {
const email = formData.get('email')
const password = formData.get('password')
// Validation
if (!email || !password) {
return { error: 'All fields are required' }
}
try {
await createUser({ email, password })
return { success: true, message: 'Account created!' }
} catch (e) {
return { error: e.message }
}
}
// Component
function SignupForm() {
const [state, formAction, isPending] = useActionState(submitForm, {})
return (
<form action={formAction}>
{state.error && (
<div className="error">{state.error}</div>
)}
{state.success && (
<div className="success">{state.message}</div>
)}
<input name="email" type="email" placeholder="Email" />
<input name="password" type="password" placeholder="Password" />
<button type="submit" disabled={isPending}>
{isPending ? 'Creating...' : 'Create account'}
</button>
</form>
)
}24. Server Component āļāļģāļāļēāļāļāļĒāđāļēāļāđāļĢ
Server Component āļāļģāļāļēāļāļāļāđāļāļīāļĢāđāļāđāļ§āļāļĢāđāđāļāđāļēāļāļąāđāļ āļāļģāđāļŦāđāļĨāļāļāļĢāļīāļĄāļēāļ JavaScript āļāļĩāđāļŠāđāļāđāļāļĒāļąāļāđāļāļĨāđāļāļāļāđāđāļāđ
// No "use client" = Server Component by default
import { prisma } from '@/lib/prisma'
// async/await directly in the component
export default async function ProductList() {
// Direct database call (no API)
const products = await prisma.product.findMany({
orderBy: { createdAt: 'desc' },
take: 10
})
return (
<div>
<h2>Recent Products</h2>
<ul>
{products.map(product => (
<li key={product.id}>
{product.name} - ${product.price}
{/* Can include Client Components */}
<AddToCartButton productId={product.id} />
</li>
))}
</ul>
</div>
)
}
// AddToCartButton.jsx
'use client'
// This component needs interactivity
export function AddToCartButton({ productId }) {
const [isPending, startTransition] = useTransition()
const handleClick = () => {
startTransition(async () => {
await addToCart(productId)
})
}
return (
<button onClick={handleClick} disabled={isPending}>
{isPending ? '...' : 'Add'}
</button>
)
}25. āļāļāļīāļāļēāļĒ SSR streaming āđāļ React 18
SSR streaming āļāļ°āļŠāđāļ HTML āđāļāļĒāļąāļāđāļāļĢāļēāļ§āđāđāļāļāļĢāđāļāļĒāđāļēāļāļāđāļāļĒāđāļāđāļāļāđāļāļĒāđāļ āđāļāļāļāļĩāđāļāļ°āļĢāļāđāļŦāđāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāđāļŠāļĢāđāļāļŠāļĄāļāļđāļĢāļāđāļāđāļāļ
import { Suspense } from 'react'
export default function Page() {
return (
<div>
{/* Immediate render */}
<Header />
{/* Streamed when ready */}
<Suspense fallback={<MainContentSkeleton />}>
<MainContent />
</Suspense>
{/* Streamed independently */}
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
{/* Immediate render */}
<Footer />
</div>
)
}
// The browser receives:
// 1. HTML shell with Header, skeletons, Footer
// 2. MainContent when its request completes
// 3. Sidebar when its request completes
// loading.jsx for route-level streaming
export default function Loading() {
return <PageSkeleton />
}āļāļēāļĢāļāļąāļāļāļēāļĢ state
26. āđāļĄāļ·āđāļāđāļāļāļ§āļĢāđāļāđ Redux, Context API āļŦāļĢāļ·āļāļāļēāļāđāļĨāļ·āļāļāļāļ·āđāļ
āļāļēāļĢāđāļĨāļ·āļāļāļāļķāđāļāļāļĒāļđāđāļāļąāļāļāļ§āļēāļĄāļāļąāļāļāđāļāļāļāļāļ state āđāļĨāļ°āļāļ§āļēāļĄāļāđāļāļāļāļēāļĢāļāļāļāđāļāļĢāđāļāļāļāđ
// Context API: simple state, few updates
// â
Good for: theme, user, preferences
const ThemeContext = createContext()
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
)
}
// Redux / Zustand: complex state, frequent updates
// â
Good for: cart, filters, complex business data
import { create } from 'zustand'
const useCartStore = create((set, get) => ({
items: [],
total: 0,
addItem: (product) => set((state) => {
const existing = state.items.find(i => i.id === product.id)
if (existing) {
return {
items: state.items.map(i =>
i.id === product.id ? { ...i, quantity: i.quantity + 1 } : i
)
}
}
return { items: [...state.items, { ...product, quantity: 1 }] }
}),
removeItem: (id) => set((state) => ({
items: state.items.filter(i => i.id !== id)
})),
getTotal: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0)
}))
// React Query / SWR: server state (cache, refetch)
// â
Good for: API data, server synchronization
import { useQuery, useMutation } from '@tanstack/react-query'
function Products() {
const { data, isLoading } = useQuery({
queryKey: ['products'],
queryFn: () => fetch('/api/products').then(r => r.json())
})
const mutation = useMutation({
mutationFn: (newProduct) => fetch('/api/products', {
method: 'POST',
body: JSON.stringify(newProduct)
})
})
}27. āļ§āļīāļāļĩāļāļģāļĢāļđāļāđāļāļ Reducer āđāļāđāļāđ
āļĢāļđāļāđāļāļ Reducer āđāļāđāļĢāļ§āļĄāļĻāļđāļāļĒāđāļĨāļāļāļīāļāļāļēāļĢāļāļąāļāđāļāļ state āļāļĩāđāļāļąāļāļāđāļāļāđāļ§āđāđāļāļāļĩāđāđāļāļĩāļĒāļ§
// Define actions
const ACTIONS = {
ADD_TODO: 'ADD_TODO',
TOGGLE_TODO: 'TOGGLE_TODO',
DELETE_TODO: 'DELETE_TODO',
SET_FILTER: 'SET_FILTER'
}
// Pure reducer (no side effects)
function todoReducer(state, action) {
switch (action.type) {
case ACTIONS.ADD_TODO:
return {
...state,
todos: [
...state.todos,
{ id: Date.now(), text: action.payload, completed: false }
]
}
case ACTIONS.TOGGLE_TODO:
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
}
case ACTIONS.DELETE_TODO:
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
}
case ACTIONS.SET_FILTER:
return { ...state, filter: action.payload }
default:
return state
}
}
// Usage with useReducer
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, {
todos: [],
filter: 'all'
})
const addTodo = (text) => {
dispatch({ type: ACTIONS.ADD_TODO, payload: text })
}
const toggleTodo = (id) => {
dispatch({ type: ACTIONS.TOGGLE_TODO, payload: id })
}
const filteredTodos = useMemo(() => {
switch (state.filter) {
case 'completed':
return state.todos.filter(t => t.completed)
case 'active':
return state.todos.filter(t => !t.completed)
default:
return state.todos
}
}, [state.todos, state.filter])
return (
<div>
<TodoForm onAdd={addTodo} />
<TodoList todos={filteredTodos} onToggle={toggleTodo} />
<FilterButtons
current={state.filter}
onChange={(f) => dispatch({ type: ACTIONS.SET_FILTER, payload: f })}
/>
</div>
)
}āļāļēāļĢāļāļāļŠāļāļ
28. āļ§āļīāļāļĩāļāļāļŠāļāļāļāļāļĄāđāļāđāļāļāļāđ React
āļāļēāļĢāļāļāļŠāļāļāļāļāļĄāđāļāđāļāļāļāđāđāļāđāļŠāļģāļŦāļĢāļąāļāļāļĢāļ§āļāļŠāļāļāļāļēāļĢāđāļĢāļāđāļāļāļĢāđāđāļĨāļ°āļāļĪāļāļīāļāļĢāļĢāļĄāļāļāļāļāļīāļāđāļāļāļĢāđāđāļāļ
import { render, screen, fireEvent } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Button from './Button'
describe('Button', () => {
// Basic rendering test
it('renders with correct text', () => {
render(<Button>Click me</Button>)
expect(screen.getByRole('button')).toHaveTextContent('Click me')
})
// Interaction test
it('calls onClick when clicked', async () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click</Button>)
await userEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
// Disabled state test
it('does not call onClick when disabled', async () => {
const handleClick = jest.fn()
render(<Button disabled onClick={handleClick}>Click</Button>)
await userEvent.click(screen.getByRole('button'))
expect(handleClick).not.toHaveBeenCalled()
})
// Async state test
it('shows loading state', async () => {
render(<Button isLoading>Submit</Button>)
expect(screen.getByRole('button')).toBeDisabled()
expect(screen.getByTestId('spinner')).toBeInTheDocument()
})
})
// Testing a custom hook
import { renderHook, act } from '@testing-library/react'
import useCounter from './useCounter'
describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter(0))
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
})29. āļ§āļīāļāļĩ mock āļāļēāļĢāđāļĢāļĩāļĒāļ API āđāļāļāļēāļĢāļāļāļŠāļāļ
Mock āļāđāļ§āļĒāđāļĒāļāļāļēāļĢāļāļāļŠāļāļāļāļāļāļāļēāļāļāļēāļĢāļāļķāđāļāļāļēāļ āļēāļĒāļāļāļ
// With MSW (Mock Service Worker) - recommended
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'
const server = setupServer(
http.get('/api/users', () => {
return HttpResponse.json([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
])
}),
http.post('/api/users', async ({ request }) => {
const body = await request.json()
return HttpResponse.json({ id: 3, ...body }, { status: 201 })
})
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
test('loads and displays users', async () => {
render(<UserList />)
expect(screen.getByText('Loading...')).toBeInTheDocument()
await waitFor(() => {
expect(screen.getByText('Alice')).toBeInTheDocument()
expect(screen.getByText('Bob')).toBeInTheDocument()
})
})
// Override for a specific test
test('handles error state', async () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.json({ error: 'Server error' }, { status: 500 })
})
)
render(<UserList />)
await waitFor(() => {
expect(screen.getByText('Loading error')).toBeInTheDocument()
})
})30. āļ§āļīāļāļĩāļāļąāļāđāļāļĢāļāļŠāļĢāđāļēāļāļāļēāļĢāļāļāļŠāļāļāđāļāļ·āđāļāļāļ§āļēāļĄāļāļĢāļāļāļāļĨāļļāļĄāļāļĩāđāđāļŦāļĄāļēāļ°āļŠāļĄ
āļāļĨāļĒāļļāļāļāđāļāļēāļĢāļāļāļŠāļāļāļāļĩāđāļŠāļĄāļāļļāļĨāļāļ§āļĢāļĢāļ§āļĄāļāļēāļĢāļāļāļŠāļāļāđāļāļĢāļ°āļāļąāļāļāđāļēāļāđ āđāļāđāļēāļāđāļ§āļĒāļāļąāļ
// Recommended testing pyramid:
// - Many unit tests (fast, isolated)
// - Some integration tests (components + hooks)
// - Few E2E tests (critical scenarios)
// Unit tests: pure logic
// utils/formatPrice.test.js
describe('formatPrice', () => {
it('formats with 2 decimals', () => {
expect(formatPrice(10)).toBe('$10.00')
})
it('handles zero', () => {
expect(formatPrice(0)).toBe('$0.00')
})
})
// Integration tests: complete component
// features/Checkout/Checkout.test.jsx
describe('Checkout', () => {
it('completes purchase flow', async () => {
render(
<CartProvider>
<Checkout />
</CartProvider>
)
// Fill the form
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com')
await userEvent.type(screen.getByLabelText('Card'), '4242424242424242')
// Submit
await userEvent.click(screen.getByRole('button', { name: 'Pay' }))
// Verify result
await waitFor(() => {
expect(screen.getByText('Order confirmed')).toBeInTheDocument()
})
})
})
// E2E tests with Playwright
// e2e/checkout.spec.ts
test('user can complete checkout', async ({ page }) => {
await page.goto('/products')
await page.click('[data-testid="add-to-cart-1"]')
await page.click('[data-testid="checkout-button"]')
await page.fill('[name="email"]', 'test@example.com')
await page.fill('[name="card"]', '4242424242424242')
await page.click('button[type="submit"]')
await expect(page.locator('text=Order confirmed')).toBeVisible()
})āļāļāļŠāļĢāļļāļ
āļāļģāļāļēāļĄāļāļąāđāļ 30 āļāđāļāļāļĩāđāļāļĢāļāļāļāļĨāļļāļĄāļāļ§āļēāļĄāļĢāļđāđ React āļāļĩāđāļāļģāđāļāđāļāļŠāļģāļŦāļĢāļąāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ āļāļāļāđāļāļāļŦāļĨāļąāļāļāļĩāđāļāļ§āļĢāđāļāļĩāđāļĒāļ§āļāļēāļ āđāļāđāđāļāđ
- â āļāļ·āđāļāļāļēāļ: Virtual DOM, JSX, props āļāļąāļ state, āļāļāļĄāđāļāđāļāļāļāđ
- â Hooks: useState, useEffect, useMemo, useCallback, useRef, useContext
- â āļĢāļđāļāđāļāļ: HOC, Render Props, Compound Components, custom hooks
- â āļāļĢāļ°āļŠāļīāļāļāļīāļ āļēāļ: React.memo, lazy loading, virtualization
- â React āļŠāļĄāļąāļĒāđāļŦāļĄāđ: Suspense, Transitions, Server Components
- â āļāļēāļĢāļāļąāļāļāļēāļĢ state: Context, Redux/Zustand, React Query
- â āļāļēāļĢāļāļāļŠāļāļ: React Testing Library, mocks, āļāļĨāļĒāļļāļāļāđāļāļēāļĢāļāļāļŠāļāļ
āļāļēāļĢāđāļāļĢāļĩāļĒāļĄāļāļąāļ§āļŠāļąāļĄāļ āļēāļĐāļāđ React āđāļĄāđāđāļāđāđāļāđāļāđāļāļĩāļĒāļāļāļēāļĢāļāđāļāļāļāļģ āļāļēāļĢāļāļķāļāļāļāļāļēāļāđāļāļĢāđāļāļāļāđāļāļĢāļīāļāļāđāļ§āļĒāđāļŦāđāđāļāļ§āļāļīāļāđāļŦāļĨāđāļēāļāļĩāđāļāļąāļāđāļāđāļ āđāļĨāļ°āļāļāļīāļāļēāļĒāđāļāđāļāļĒāđāļēāļāđāļāđāļāļāļĢāļĢāļĄāļāļēāļāļīāđāļāļĢāļ°āļŦāļ§āđāļēāļāļāļēāļĢāļŠāļąāļĄāļ āļēāļĐāļāđ
āđāļĢāļīāđāļĄāļāļķāļāļāđāļāļĄāđāļĨāļĒ!
āļāļāļŠāļāļāļāļ§āļēāļĄāļĢāļđāđāļāļāļāļāļļāļāļāđāļ§āļĒāļāļąāļ§āļāļģāļĨāļāļāļŠāļąāļĄāļ āļēāļĐāļāđāđāļĨāļ°āđāļāļāļāļāļŠāļāļāđāļāļāļāļīāļāļāļĢāļąāļ
āđāļāđāļ
āđāļāļĢāđ
āļāļāļāļ§āļēāļĄāļāļĩāđāđāļāļĩāđāļĒāļ§āļāđāļāļ

React 19 useEffectEvent āđāļĨāļ° Activity: API āđāļŦāļĄāđāļāļĢāđāļāļĄāļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāļāļēāļ 2026
āđāļāļēāļ°āļĨāļķāļ useEffectEvent āđāļĨāļ° Activity component āđāļ React 19.2 āđāļāđāļāļąāļāļŦāļē stale closure, pre-rendering āđāļāļ·āđāļāļāļŦāļĨāļąāļ āļāļĢāđāļāļĄāļāļąāļ§āļāļĒāđāļēāļāđāļāđāļāđāļĨāļ°āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđ

Cache Components āđāļ Next.js 16 āļāļĩ 2026: āļāļđāđāļĄāļ·āļāļāļĢāļāļāđāļ§āļ use cache, PPR āđāļĨāļ°āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāļāļēāļ
āļāļđāđāļĄāļ·āļāđāļāļīāļāļĨāļķāļāđāļāļĩāđāļĒāļ§āļāļąāļ Cache Components āđāļ Next.js 16: directive use cache āļāļąāđāļāļŠāļēāļĄāļĢāļ°āļāļąāļ, Partial Pre-Rendering, cacheLife profiles, cacheTag, āļāļ§āļēāļĄāļāļĨāļāļāļ āļąāļĒāļāļāļ cache āđāļĨāļ°āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāļāļēāļāļŠāļģāļŦāļĢāļąāļ developer āđāļāļāļĩ 2026

React Compiler āđāļāļāļĩ 2026: Automatic Memoization āđāļĨāļ°āļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāļāļēāļ
āđāļĢāļĩāļĒāļāļĢāļđāđ React Compiler āļāļĩāđāļāļģ memoization āļāļąāļāđāļāļĄāļąāļāļī āļāļĢāđāļāļĄāļāļģāļāļēāļĄāļŠāļąāļĄāļ āļēāļĐāļāđāļāļēāļāļĒāļāļāļāļīāļĒāļĄāļŠāļģāļŦāļĢāļąāļāļāļąāļāļāļąāļāļāļē React āđāļāļāļĩ 2026