FullStackOpen Part 1 C Component State, Event Handlers
FullStackOpen Part 1 C Component State, Event Handlers
md 2025-08-08
🧩 If the section is deep, technical, or essential for backend/frontend architecture: Use the Full Detailed
Format with the following goals: ✅ Core Objectives Clearly explain the concepts using beginner-friendly but
technically sound language Demonstrate how each idea applies to real-world MERN projects (e.g., REST APIs,
component state, Express middleware) Include code snippets, project structures, or diagrams where helpful
Summarize all necessary key takeaways — cover everything essential to fully understand the content Identify
and explain core concepts to retain for long-term understanding Highlight common pitfalls,
misunderstandings, or mistakes beginners might face Describe how this section fits into the broader MERN
learning path (e.g., frontend foundations, backend APIs, database logic)
🧩 Bonus Learning Tools (as appropriate): Interview-style Q&A Conceptual (e.g., “What does useEffect do?”)
Technical (e.g., “Fix this Express route” or “Write a Mongoose schema”)
🧠 Mini Quiz or Checkpoints Create a mini quiz or checkpoint with enough questions to ensure full
understanding of the section. For each question, provide the correct answer immediately after to help
reinforce learning and allow self-assessment. Use a mix of conceptual, technical, and applied questions where
appropriate.
⚡ If the section is introductory or covers a small utility or isolated topic: Use the Lightweight Format with the
following goals: ✅ What to Do Provide a clear and concise explanation Share relevant examples or code
snippets List important concepts worth remembering Summarize the section with as many key insights as
needed (no fixed limit) Mention any common beginner mistakes
🧩 Optional (as needed): Include interview-style Q&As to reinforce core ideas 🧠 Mini Quiz or Checkpoints If
helpful, create a checkpoint quiz with as many questions as necessary to ensure full understanding. For each
question, provide the correct answer immediately after it. Focus on concise, practical, and concept-reinforcing
questions.
➡ Please tailor your explanation to someone who is actively learning the MERN stack and building real-world
projects for interviews or portfolio development. Decide which format (detailed or lightweight) is appropriate
based on the content you receive next.
I'm ready to help you understand and retain the FullStackOpen course content! 🚀
1 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Based on your profile as a MERN stack developer focused on scalable systems and clean architecture, I'll
provide explanations that:
Connect concepts to real-world MERN applications (APIs, databases, frontend state management)
Explain from first principles without unnecessary abstraction
Include practical code examples and project structures
Highlight performance and scalability considerations
Provide actionable takeaways for your interview preparation
Please go ahead and share the section from FullStackOpen that you'd like me to explain! I'm excited to help
you master these concepts for your software engineering role preparation.
return (
<div>
<h1>Greetings</h1>
<Hello name="Maya" age={26 + 10} />
<Hello name={name} age={age} />
</div>
)
}
return (
<div>
<h1>User Dashboard</h1>
{users.map(user => (
<UserProfile key={user.id} user={user} />
))}
</div>
)
}
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(userData => setUser(userData))
}, [userId])
5 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
🧠 Interview-Style Q&A
Q: What happens if I pass an object as a prop and modify it inside the component? A: You're mutating
the original object, which breaks React's one-way data flow principle. This can cause unexpected re-renders
and bugs. Always treat props as immutable.
Q: Can I pass functions as props? A: Yes! Functions are first-class citizens in JavaScript. This is how you
handle events:
Q: What's the difference between name="Maya" and name={name}? A: name="Maya" passes a string literal.
name={name} passes the value of the variable name. Use curly braces for any JavaScript expression.
Answer: <h1>Hello Anonymous</h1> - When no name prop is passed, props.name is undefined, so the
fallback 'Anonymous' is used.
Answer: ❌ Mutating props! Should be: const newUsers = [...props.users, { name: 'New User' }]
Question 3: How would you pass a user object from an API response to a component?
7 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Answer: Missing key prop! Should be: {users.map(user => <UserCard key={user.id} name=
{user.name} />)}
// ✅ More readable
const Hello = ({ name, age }) => (
<div>
<p>Hello {name}, you are {age} years old</p>
</div>
)
2. Default Props
8 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
This foundation of props and component composition is essential for building scalable MERN applications.
Master this pattern, and you'll find state management and API integration much more intuitive! 🎯
return (
<div>
<p>Hello {props.name}, you are {props.age} years old</p>
<p>So you were probably born in {bornYear()}</p>
</div>
)
}
Key Benefits:
9 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return orders
.filter(order => order.status === 'completed')
.reduce((total, order) => total + order.amount, 0)
.toFixed(2)
}
return (
<div className="user-profile">
<h2>{user.name}</h2>
<p>Member since: {formatJoinDate()}</p>
<p>Total spent: ${calculateTotalSpent()}</p>
<p>Membership: {getMembershipLevel()}</p>
</div>
)
}
return (
<form onSubmit={(e) => { e.preventDefault(); onSubmit(formData) }}>
<input
type="email"
value={formData.email}
10 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<h3>{product.name}</h3>
<p>Price: ${calculatePrice()}</p>
<button disabled={!canAddToCart()}>Add to Cart</button>
</div>
)
}
2. Performance Considerations
11 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Logic is component-specific
Needs access to props/state
Used multiple times within the component
return (
<div>
<h3>{getDisplayName()}</h3>
<p>Age: {calculateAge(user.birthDate)}</p>
</div>
)
}
12 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// ❌ Missing parentheses
<p>Born in {bornYear}</p> // Displays function definition, not result
🧠 Interview-Style Q&As
Q: Why can the helper function access props without passing them as parameters? A: JavaScript
closures! The helper function is defined inside the component function scope, so it has access to all variables
in that scope, including props.
Q: When should you use useMemo vs helper functions? A: Use useMemo for expensive calculations that
depend on specific dependencies. Use helper functions for simple logic that doesn't need caching.
13 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Q: Can helper functions access state from hooks? A: Yes! They have access to all variables in the
component scope, including state from useState, useContext, etc.
Answer: It will display the function definition (something like "Double count: () => count * 2") because
getDoubleCount is missing parentheses. Should be {getDoubleCount()}
Answer:
14 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// Approach A
const formatPrice = (price) => `$${price.toFixed(2)}`
const Component = ({ product }) => <p>{formatPrice(product.price)}</p>
// Approach B
const Component = ({ product }) => {
const formatPrice = () => `$${product.price.toFixed(2)}`
return <p>{formatPrice()}</p>
}
Answer: Approach A uses a pure function that can be reused across components. Approach B creates a new
function on every render but has direct access to props. Use A for reusable logic, B for component-specific
logic.
This pattern of helper functions within components is extremely common in React development and essential
for organizing your component logic cleanly while maintaining access to component data! 🎯
return (
<div>
<p>Hello {name}, you are {age} years old</p>
<p>So you were probably born in {bornYear()}</p>
</div>
)
}
return (
<div>
<p>Hello {name}, you are {age} years old</p>
<p>So you were probably born in {bornYear()}</p>
</div>
)
}
return (
<div>
<p>Hello {name}, you are {age} years old</p>
<p>So you were probably born in {bornYear()}</p>
</div>
)
}
return (
<div className="user-profile">
<img src={avatar} alt={`${name}'s avatar`} />
<h2>{name}</h2>
<p>{email}</p>
<p>{bio}</p>
<p>Location: {location}</p>
<div>
<p>Notifications: {notifications ? 'On' : 'Off'}</p>
<p>Privacy: {privacy}</p>
</div>
</div>
)
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={username}
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
placeholder="Username"
/>
<input
17 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
type="email"
value={email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
placeholder="Password"
/>
<button type="submit">Register</button>
</form>
)
}
return (
<div className="product-card">
<img src={primaryImage} alt={name} />
<h3>{name}</h3>
<p>{description}</p>
<p>Category: {categoryName}</p>
<p>Price: ${price}</p>
<p>Rating: {average}/5 ({count} reviews)</p>
<p>Stock: {isInStock ? 'In Stock' : 'Out of Stock'}</p>
{additionalImages.length > 0 && (
<div className="additional-images">
{additionalImages.map((img, index) => (
<img key={index} src={img} alt={`${name} ${index + 1}`} />
))}
</div>
)}
</div>
18 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
)
}
return (
<div className={`user-card ${theme}`}>
<h3>{name}</h3>
{showEmail && <p>{email}</p>}
<p>Age: {age}</p>
</div>
)
}
19 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
{products.map(({ id: productId, name: productName, price }) => (
<div key={productId}>
<h3>{productName}</h3>
<p>${price}</p>
</div>
))}
</div>
)
}
4. Array Destructuring
2. Over-destructuring
🧠 Interview-Style Q&As
Q: What's the difference between const { name } = props and const name = props.name? A:
Functionally identical, but destructuring is more concise and allows extracting multiple properties in one line:
const { name, age, email } = props.
21 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Q: Can you destructure arrays in React? A: Yes! Common with hooks: const [count, setCount] =
useState(0) and API responses: const [users, products] = await Promise.all([fetchUsers(),
fetchProducts()]).
Q: What happens if you try to destructure a property that doesn't exist? A: It becomes undefined. Use
default values to prevent errors: const { missing = 'default' } = {}
Answer:
Answer: If user or user.settings is undefined, it will throw an error. Fix: const Profile = ({ user = {}
}) => { const { name, settings: { theme } = {} } = user
22 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
This destructuring pattern is essential for writing clean, readable React components and efficiently handling
API data in your MERN applications! 🎯
// App.jsx
const App = (props) => {
const {counter} = props
return <div>{counter}</div>
}
// main.jsx
let counter = 1
ReactDOM.createRoot(document.getElementById('root')).render(
<App counter={counter} />
)
Key Issue: React components are static after initial render. Even if you change the counter variable outside
the component, React doesn't know it should update the UI.
let counter = 1
const root = ReactDOM.createRoot(document.getElementById('root'))
23 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
What's happening:
setInterval(() => {
refresh()
counter += 1
}, 1000)
This creates a counter that updates every second, but it's fundamentally wrong for several reasons we'll
explore.
24 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
fetch('/api/orders').then(orderData => {
orders = orderData
refresh() // Re-renders entire app again
})
fetch('/api/notifications').then(notifData => {
notifications = notifData.length
refresh() // Yet another full re-render
})
Problems:
return (
<div>
<UserProfile user={user} /> {/* Loses form state */}
<OrderHistory orders={orders} /> {/* Loses pagination */}
<ProductList products={products} /> {/* Loses filters */}
<NotificationCenter count={notifications} /> {/* Loses read/unread state */}
</div>
)
}
Declarative UI: You describe what the UI should look like for any given state, not how to change it.
25 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// React compares:
// Previous Virtual DOM: <div>1</div>
// New Virtual DOM: <div>2</div>
// Real DOM update: Only changes the text content
Manual re-rendering breaks this optimization because React thinks it's rendering a completely new app
each time.
useEffect(() => {
console.log('Component mounted or data changed')
return () => console.log('Cleanup before next update')
}, [data])
return <div>{data}</div>
}
26 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
</div>
)
}
27 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
🧠 Interview-Style Q&A
Q: Why doesn't the component re-render when we change the counter variable outside React? A: React
doesn't monitor external variables. It only re-renders when you call the render method or when component
state/props change through React's state management system.
Q: What's wrong with calling render() multiple times? A: It bypasses React's optimization (reconciliation),
causes unnecessary re-creation of the entire component tree, loses component state, and breaks the
component lifecycle.
Q: In a real MERN app, how would frequent manual re-rendering affect API calls? A: Every re-render
could trigger useEffect hooks again, causing redundant API calls to your Express backend, increased server
load, and poor user experience with loading states.
Q: What happens to event listeners and timers with manual re-rendering? A: They might not be properly
cleaned up, leading to memory leaks. React's proper lifecycle ensures cleanup functions run when
components unmount or update.
refresh()
data.count = 5 // Changed the object
// No refresh() called
Answer: The UI will still show count: 0 because React doesn't know the data changed. You must call
refresh() after changing the data.
28 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<div>
{users.map(user => <UserCard key={user.id} user={user} />)}
{posts.map(post => <PostCard key={post.id} post={post} />)}
{comments.map(comment => <Comment key={comment.id} comment={comment} />)}
</div>
)
}
Answer: When any single piece of data changes, the entire dashboard re-renders, including all user cards,
posts, and comments. With proper state management, only the changed components would update.
Question 3: What's the problem with this ticking counter in a MERN app?
let counter = 0
setInterval(() => {
refresh()
counter += 1
}, 1000)
Answer:
Creates a new render every second even if user isn't looking at the counter
Breaks any forms or interactive elements the user might be using
Doesn't scale - imagine 100 users with 100 different counters
Cannot be paused, stopped, or controlled by the component
let loginAttempts = 0
const refresh = () => root.render(<LoginForm attempts={loginAttempts} />)
Answer: The form gets completely reset! User loses their typed username and password because manual re-
rendering destroys and recreates all form elements.
29 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1) // React handles re-rendering
}, 1000)
return <div>{count}</div>
}
This foundation understanding of React's rendering cycle is crucial for building efficient MERN applications.
Manual re-rendering might seem simpler, but it creates more problems than it solves. The proper state
management techniques you'll learn next are what make React truly powerful for building scalable web
applications! 🎯
30 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return <div>{counter}</div>
}
31 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
fetchUser()
}, [])
return (
<div className="user-profile">
<h2>{user.name}</h2>
<p>{user.email}</p>
<p>Member since: {new Date(user.createdAt).toLocaleDateString()}</p>
</div>
32 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
)
}
return (
<div className="shopping-cart">
<h2>Shopping Cart ({cartItems.length} items)</h2>
{cartItems.map(item => (
<div key={item.id} className="cart-item">
<span>{item.name}</span>
<span>${item.price}</span>
<button onClick={() => removeFromCart(item.id)}>Remove</button>
</div>
))}
<div className="cart-total">Total: ${total.toFixed(2)}</div>
</div>
33 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
)
}
useEffect(() => {
// Connect to Socket.io server (running on Express)
const socket = io('/api/chat')
if (response.ok) {
setNewMessage('') // Clear input - React re-renders form
}
}
return (
<div className="chat-container">
<div className="online-users">
Online: {onlineUsers.length} users
</div>
<div className="messages">
{messages.map(msg => (
<div key={msg.id} className="message">
34 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<strong>{msg.username}:</strong> {msg.text}
</div>
))}
</div>
<div className="message-input">
<input
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)} // React updates input
placeholder="Type a message..."
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
/>
<button onClick={sendMessage}>Send</button>
</div>
</div>
)
}
return (
<div>
{todos.map(todo => (
<div key={todo.id}>
<span>{todo.text}</span>
<button onClick={() => updateTodo(todo.id, { completed: !todo.completed
})}>
{todo.completed ? 'Undo' : 'Complete'}
35 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
</button>
</div>
))}
</div>
)
}
36 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
}
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleMultipleUpdates}>Wrong Way</button>
<button onClick={handleCorrectUpdates}>Correct Way</button>
</div>
)
}
37 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// ✅ Correct approach
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(result => setData(result))
}, []) // Empty dependency array - runs only once
}
Where you are: Mastering React's core state management What's next:
🧠 Interview-Style Q&A
Q: What happens when you call setCounter(counter + 1) multiple times in the same event handler? A:
React batches the updates, but since counter uses the same initial value in each call, only the last update
takes effect. Use functional updates: setCounter(prev => prev + 1) for sequential increments.
Q: Why does the useState initial value only run once, even though the component re-renders? A: React
keeps track of component state between renders. The initial value is only used on the very first render
(component mount). On subsequent renders, useState returns the current state value.
Q: In a MERN app, when should you put API data in local state vs global state? A: Local state for
component-specific data (form inputs, toggles, local UI state). Global state (Context/Redux) for data shared
across multiple components (user authentication, shopping cart, notifications).
39 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Answer: It will increment by 1 on each click, not 3. All three setCount calls use the same count value (0) due
to closure, so they're effectively setCount(0 + 1) three times. React batches them and the result is count =
1.
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(setData)
}) // What's wrong here?
Answer: Missing dependency array. Add [] as the second argument: useEffect(() => {...}, []) to run
only once on mount.
setTimeout(() => {
console.log('B:', value)
}, 0)
}
console.log('C:', value)
return <button onClick={handleUpdate}>Update</button>
}
Answer:
40 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Question 4: How would you implement this counter that starts from a user's last saved count from
MongoDB?
return <div>{count}</div>
}
Answer:
useEffect(() => {
fetch(`/api/users/${userId}/counter`)
.then(res => res.json())
.then(data => setCount(data.savedCount))
}, [userId])
return <div>{count}</div>
}
41 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
This useState hook is the foundation of all React interactivity. Master this concept, and you'll be ready to
build dynamic, responsive MERN applications that efficiently manage user interactions and API data! 🎯
42 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
This shift represents moving from passive components to interactive components that respond to user
input.
return (
<div>
<div>{counter}</div>
<button onClick={handleClick}>plus</button>
</div>
)
}
Benefits:
return (
<div>
<div>{counter}</div>
<button onClick={() => setCounter(counter + 1)}>plus</button>
<button onClick={() => setCounter(0)}>zero</button>
</div>
)
}
Benefits:
43 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>${product.price}</p>
44 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<div className="quantity-controls">
<button onClick={decrementQuantity}>-</button>
<span>{quantity}</span>
<button onClick={incrementQuantity}>+</button>
</div>
<div className="actions">
<button
onClick={handleAddToCart}
disabled={isLoading || product.stock === 0}
>
{isLoading ? 'Adding...' : 'Add to Cart'}
</button>
if (!newMessage.trim()) return
45 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
setIsTyping(false)
try {
// Send to Express server
const response = await fetch(`/api/chat/rooms/${roomId}/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: newMessage })
})
if (!isTyping) {
setIsTyping(true)
// Send typing indicator to other users via Socket.io
socket.emit('typing-start', { roomId, userId: currentUser._id })
}
return (
<div className="chat-room">
<div className="messages">
{messages.map(message => (
<div key={message._id || message.id} className="message">
<strong>{message.username}:</strong> {message.text}
{message.status === 'failed' && (
<button onClick={() => handleSendMessage({ preventDefault: () => {}
})}>
Retry
</button>
)}
</div>
))}
</div>
47 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// Real-time validation
if (field === 'email' && value && !isValidEmail(value)) {
setErrors(prev => ({ ...prev, email: 'Invalid email format' }))
}
}
setIsLoading(true)
setErrors({})
try {
// Call Express authentication endpoint
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
if (!response.ok) {
throw new Error(data.message || 'Login failed')
}
} catch (error) {
setErrors({ submit: error.message })
} finally {
setIsLoading(false)
}
}
return (
<form onSubmit={handleSubmit} className="login-form">
<div className="form-group">
<input
48 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
type="email"
value={credentials.email}
onChange={handleInputChange('email')}
placeholder="Email"
disabled={isLoading}
className={errors.email ? 'error' : ''}
/>
{errors.email && <span className="error-message">{errors.email}</span>}
</div>
<div className="form-group">
<div className="password-input">
<input
type={showPassword ? 'text' : 'password'}
value={credentials.password}
onChange={handleInputChange('password')}
placeholder="Password"
disabled={isLoading}
className={errors.password ? 'error' : ''}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="toggle-password"
>
{showPassword ? '👁' : ' '}
</button>
</div>
{errors.password && <span className="error-message">{errors.password}
</span>}
</div>
{errors.submit && (
<div className="submit-error">{errors.submit}</div>
)}
</form>
)
}
49 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" />
<button type="submit">Submit</button>
<a href="/external" onClick={handleLinkClick}>Custom Link</a>
</form>
)
}
switch (action) {
case 'toggle':
setTodos(prev => prev.map(todo =>
todo.id === todoId
? { ...todo, completed: !todo.completed }
: todo
))
break
case 'delete':
setTodos(prev => prev.filter(todo => todo.id !== todoId))
break
}
}
return (
<div className="todo-list" onClick={handleTodoAction}>
{todos.map(todo => (
50 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
setIsSearching(true)
const searchTimer = setTimeout(async () => {
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`)
const data = await response.json()
setResults(data.results)
} catch (error) {
console.error('Search failed:', error)
setResults([])
} finally {
setIsSearching(false)
}
}, 500)
return (
<div className="search-component">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search products..."
/>
51 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<div className="search-results">
{results.map(result => (
<div key={result.id} className="result-item">
{result.name}
</div>
))}
</div>
</div>
)
}
try {
const response = await fetch(`/api/users/${user._id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
})
if (response.ok) {
52 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleInputChange}
placeholder="Full Name"
/>
<input
name="email"
type="email"
value={formData.email}
onChange={handleInputChange}
placeholder="Email"
/>
<input
name="age"
type="number"
value={formData.age}
onChange={handleInputChange}
placeholder="Age"
/>
<textarea
name="bio"
value={formData.bio}
onChange={handleInputChange}
placeholder="Bio"
/>
<label>
<input
name="newsletter"
type="checkbox"
checked={formData.newsletter}
onChange={handleInputChange}
/>
Subscribe to newsletter
</label>
return (
<div>
{/* ❌ Wrong - calls function immediately on render */}
<button onClick={handleClick()}>Bad Button</button>
54 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Broken Increment</button>
<button onClick={handleClickFixed}>Working Increment</button>
</div>
)
}
return (
<div>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
{data && <p>Data: {JSON.stringify(data)}</p>}
55 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
useEffect(() => {
const handleResize = () => {
setWindowWidth(window.innerWidth)
}
API Interactions: Every button click, form submission triggers Express endpoints
User Experience: Smooth, responsive interactions separate good apps from great ones
Real-time Features: Chat, notifications, live updates all depend on event handling
Data Validation: Form validation happens in event handlers before sending to backend
Security: Proper event handling prevents XSS and ensures data integrity
Performance: Efficient event handling reduces server load and improves responsiveness
56 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
🧠 Interview-Style Q&A
Q: What's the difference between onClick={handleClick} and onClick={handleClick()}? A: The first
passes a function reference that React calls when clicked. The second immediately calls the function during
render, which is usually wrong and can cause infinite re-renders or errors.
Q: How would you handle a button click that needs to call multiple APIs in sequence? A: Use async/await
in the event handler with proper error handling:
Q: Why might you choose inline event handlers vs named functions? A: Inline for simple, one-off
operations (onClick={() => setShow(true)}). Named functions for complex logic, reusability, testability,
and better performance (avoid creating new functions on each render).
Q: How do you prevent a form from submitting to the server in React? A: Use event.preventDefault()
in the form's onSubmit handler, then handle the submission with JavaScript (usually an API call to your
Express backend).
return (
<button onClick={setCount(count + 1)}>
Count: {count}
</button>
)
}
Answer: Error! setCount(count + 1) is called immediately during render, not when clicked. It should be
onClick={() => setCount(count + 1)} or use a separate function.
Question 2: Fix this event handler to properly update the user's profile via API:
57 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<form onSubmit={handleSubmit}>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button type="submit">Save</button>
</form>
)
}
Answer:
try {
const response = await fetch(`/api/users/${user._id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name })
})
58 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<input
value={query}
onChange={(e) => {
setQuery(e.target.value)
handleSearch() // What's wrong here?
}}
/>
<div>{results.map(r => <div key={r.id}>{r.name}</div>)}</div>
</div>
)
}
Answer: API call triggers on every keystroke! This creates excessive server requests and potential race
conditions. Should use debouncing:
useEffect(() => {
if (!query) return
const timer = setTimeout(() => handleSearch(), 500)
return () => clearTimeout(timer)
}, [query])
Question 4: How would you implement a like button for a social media post?
return (
<div>
<p>{post.content}</p>
<button onClick={handleLike}>
{isLiked ? '❤' : '🤍'} {likeCount}
</button>
</div>
)
}
Answer:
59 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
try {
const response = await fetch(`/api/posts/${post._id}/like`, {
method: newIsLiked ? 'POST' : 'DELETE',
headers: { 'Content-Type': 'application/json' }
})
return (
<div>
{items.map(item =>
60 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
if (!sanitizedComment) return
await fetch('/api/comments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ comment: sanitizedComment })
})
}
return (
<form onSubmit={handleSubmit}>
<textarea
value={comment}
onChange={(e) => setComment(e.target.value)}
maxLength={1000}
/>
<button type="submit">Post Comment</button>
</form>
)
}
Event handling is the interactive backbone of your MERN applications. Master these patterns, and you'll be
able to create responsive, user-friendly interfaces that efficiently communicate with your Express backend and
maintain clean, predictable state! 🎯
61 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// This causes:
// 1. setCounter(1) runs during render
// 2. State changes to 1
// 3. Component re-renders
// 4. setCounter(1 + 1) runs during render again
// 5. State changes to 2
// 6. Component re-renders again
// 7. Infinite loop of re-renders!
The browser crashes with "Too many re-renders" error because React tries to prevent infinite loops.
62 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<button onClick={loadProduct}>Load Product</button> {/* Function reference
*/}
{loading && <p>Loading...</p>}
{product && <p>{product.name}</p>}
</div>
)
}
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
})
if (response.ok) {
console.log('User registered successfully')
}
} catch (error) {
console.error('Registration failed:', error)
}
}
return (
63 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div className="cart-item">
<span>{item.name}</span>
<div className="quantity-controls">
<button onClick={handleDecrease}>-</button>
<span>{item.quantity}</span>
<button onClick={handleIncrease}>+</button>
</div>
<button onClick={handleRemove}>Remove</button>
</div>
)
}
64 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<button onClick={handleIncrement}> {/* Function reference, no parentheses */}
Method 2: {count}
</button>
)
}
return (
<div>
<button onClick={createHandler(1)}>+1</button> {/* Call to get function */}
<button onClick={createHandler(5)}>+5</button>
<span>{count}</span>
</div>
)
}
65 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
button.addEventListener('click', handleClick)
66 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<form onSubmit={handleSubmit}>
<input
value={value}
onChange={handleChange}
onKeyPress={handleKeyPress}
/>
<button type="submit">Submit</button>
</form>
)
}
// ❌ Wrong
<button onClick={handleClick()}>Click</button>
// ✅ Correct
<button onClick={handleClick}>Click</button>
67 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
)
}
🧠 Interview-Style Q&As
Q: What's the difference between onClick={handleClick} and onClick={handleClick()}? A: The first
passes a function reference that React calls when the event occurs. The second immediately executes the
function during render, often causing infinite re-render loops.
Q: When would you use inline arrow functions vs named functions for event handlers? A: Use inline
arrow functions for simple, one-line operations. Use named functions for complex logic, better readability,
reusability, and easier debugging.
Q: How do you pass parameters to event handlers? A: Wrap in an arrow function: onClick={() =>
handleClick(param)} or create a function that returns a function: onClick={createHandler(param)}
return (
<div>
<p>Count: {count}</p>
<button onClick={setCount(count + 1)}>Increment</button>
</div>
)
}
Answer: Infinite re-render loop! setCount(count + 1) executes immediately on every render, causing state
to update continuously. Browser will show "Too many re-renders" error.
68 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Answer:
Question 3: Which of these event handlers is correct for a delete button that needs confirmation?
// Option A
<button onClick={confirm('Delete?') && deleteItem(id)}>Delete</button>
// Option B
<button onClick={() => confirm('Delete?') && deleteItem(id)}>Delete</button>
// Option C
<button onClick={() => {
if (confirm('Delete?')) {
deleteItem(id)
}
}}>Delete</button>
Answer: Option C is correct. Option A executes immediately on render. Option B works but Option C is more
readable and explicit about the conditional logic.
return (
<form onSubmit={
fetch('/api/login', {
69 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
method: 'POST',
body: JSON.stringify(credentials)
})
}>
<input type="email" />
<input type="password" />
<button type="submit">Login</button>
</form>
)
}
Answer: The onSubmit is making an API call immediately on every render instead of when the form is
submitted. Should be:
🚀 Best Practices
1. Use Named Functions for Complex Logic
// Validation
if (!product.name.trim()) {
alert('Product name is required')
return
}
if (product.price <= 0) {
alert('Price must be greater than 0')
return
}
// API call
try {
await onSave(product)
setProduct({ name: '', price: '' }) // Reset form
70 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
} catch (error) {
console.error('Save failed:', error)
}
}
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
</form>
)
}
return (
<div>
{items.map(item =>
<button key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</button>
)}
</div>
)
}
This distinction between function references and function calls is fundamental to React development.
Understanding this concept prevents one of the most common bugs that can crash your MERN applications
and helps you write more predictable, performant code! 🎯
71 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<div>{counter}</div> {/* Display logic mixed in */}
<button onClick={() => setCounter(counter + 1)}>plus</button> {/* Button
logic mixed in */}
<button onClick={() => setCounter(0)}>zero</button>
</div>
)
}
return (
<div>
<Display counter={counter} />
<Button onClick={increaseByOne} text="plus" />
<Button onClick={setToZero} text="zero" />
<Button onClick={decreaseByOne} text="minus" />
</div>
72 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
)
}
return (
<div className="price-display">
{discount > 0 && (
<span className="original-price">${price.toFixed(2)}</span>
)}
<span className="current-price">
{currency === 'USD' ? '$' : currency} {finalPrice.toFixed(2)}
</span>
{savings > 0 && (
<span className="savings">Save ${savings.toFixed(2)}</span>
)}
</div>
)
}
73 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
)
}
return (
<div className={`inventory-status ${isOutOfStock ? 'out-of-stock' : isLowStock
? 'low-stock' : 'in-stock'}`}>
{isOutOfStock ? (
<span>❌ Out of Stock</span>
) : isLowStock ? (
<span>⚠ Low Stock ({available} left)</span>
) : (
<span>✅ In Stock ({available} available)</span>
)}
</div>
)
}
fetchProducts()
}, [])
try {
const response = await fetch(`/api/products/${productId}/stock`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ stock: newStock })
74 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
})
try {
await fetch(`/api/products/${productId}`, { method: 'DELETE' })
setProducts(prev => prev.filter(product => product._id !== productId))
} catch (error) {
console.error('Delete failed:', error)
}
}
return (
<div className="product-manager">
<h1>Product Management</h1>
<div className="products-grid">
{products.map(product => (
75 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<ProductCard
key={product._id}
product={product}
onUpdateStock={handleUpdateStock}
onApplyDiscount={handleApplyDiscount}
onDelete={handleDeleteProduct}
loading={loading[product._id]}
error={errors[product._id]}
/>
))}
</div>
</div>
)
}
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<InventoryStatus
stock={product.stock}
reserved={product.reserved || 0}
/>
<div className="stock-management">
<input
type="number"
value={stockInput}
onChange={(e) => setStockInput(parseInt(e.target.value))}
min="0"
/>
<ActionButton
onClick={() => onUpdateStock(product._id, stockInput)}
text="Update Stock"
variant="secondary"
76 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
loading={loading}
disabled={stockInput === product.stock}
/>
</div>
<div className="discount-management">
<input
type="number"
value={discountInput}
onChange={(e) => setDiscountInput(parseInt(e.target.value))}
min="0"
max="100"
placeholder="Discount %"
/>
<ActionButton
onClick={() => onApplyDiscount(product._id, discountInput)}
text="Apply Discount"
variant="warning"
icon="🏷"
/>
</div>
<div className="product-actions">
<ActionButton
onClick={() => onDelete(product._id)}
text="Delete Product"
variant="danger"
icon="🗑"
/>
</div>
return (
<div
className={`message ${isOwnMessage ? 'own-message' : 'other-message'}`}
onMouseEnter={() => setShowActions(true)}
onMouseLeave={() => setShowActions(false)}
>
<div className="message-header">
<span className="username">{message.username}</span>
77 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<span className="timestamp">
{new Date(message.timestamp).toLocaleTimeString()}
</span>
</div>
<div className="message-content">{message.text}</div>
{showActions && (
<MessageActions
onReact={onReact}
onReply={onReply}
onDelete={isOwnMessage ? onDelete : null}
messageId={message._id}
/>
)}
</div>
)
}
return (
<div className="message-actions">
<div className="reactions">
{reactions.map(emoji => (
<button
key={emoji}
onClick={() => onReact(messageId, emoji)}
className="reaction-btn"
>
{emoji}
</button>
))}
</div>
<ActionButton
onClick={() => onReply(messageId)}
text="Reply"
variant="ghost"
icon="↩"
/>
{onDelete && (
<ActionButton
onClick={() => onDelete(messageId)}
text="Delete"
variant="danger"
icon="🗑"
78 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
/>
)}
</div>
)
}
<div className="user-info">
<span className="username">{user.name}</span>
<span className="user-status">{user.status}</span>
</div>
{!isCurrentUser && (
<div className="user-actions">
<ActionButton
onClick={() => onPrivateMessage(user._id)}
text="Message"
variant="ghost"
icon="💬"
/>
<ActionButton
onClick={() => onUserAction(user._id, 'block')}
text="Block"
variant="danger"
icon="🚫"
/>
</div>
)}
79 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
</div>
)
}
switch (data.type) {
case 'message':
setMessages(prev => [...prev, data.message])
break
case 'user_joined':
setUsers(prev => [...prev, data.user])
break
case 'user_left':
setUsers(prev => prev.filter(user => user._id !== data.userId))
break
case 'message_reaction':
setMessages(prev =>
prev.map(msg =>
msg._id === data.messageId
? { ...msg, reactions: data.reactions }
: msg
)
)
break
}
}
const messageData = {
text: newMessage,
userId: currentUser._id,
username: currentUser.name,
timestamp: new Date()
80 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
try {
// Send via WebSocket for real-time update
socket.send(JSON.stringify({ type: 'message', data: messageData }))
setNewMessage('')
} catch (error) {
console.error('Failed to send message:', error)
}
}
81 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div className="chat-room">
<div className="chat-header">
<h2>Chat Room: {roomId}</h2>
</div>
<div className="chat-body">
<div className="messages-container">
{messages.map(message => (
<Message
key={message._id}
message={message}
currentUser={currentUser}
onReact={handleReactToMessage}
onReply={() => {}} // Implement reply functionality
onDelete={handleDeleteMessage}
/>
))}
</div>
<UserList
users={users}
currentUser={currentUser}
onPrivateMessage={() => {}} // Implement private messaging
onUserAction={() => {}} // Implement user actions
/>
</div>
82 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
The Rule: When multiple components need to share the same state, lift it up to their closest common
ancestor.
return (
<div>
<CounterDisplay value={count} label="Counter 1" />
<CounterDisplay value={count} label="Counter 2" />
<CounterControls count={count} setCount={setCount} />
</div>
)
}
83 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Decision Rules:
Use State when: Component needs to track changing data, user interactions modify the data,
component lifecycle depends on the data
Use Props when: Data comes from parent, configuration values, event handlers, data is display-only for
this component
return (
<UserProfile
user={userData} // Data down
theme="dark" // Configuration down
/>
)
}
return (
<UserList
users={users}
onUserUpdate={handleUserUpdate} // Handler down
/>
)
}
84 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<UserList
onUserSelect={setSelectedUser} // Child 1 updates parent state
/>
<UserDetails
user={selectedUser} // Parent passes state to child 2
/>
</div>
)
}
85 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// ✅ Composition pattern
const Card = ({ children, title, actions }) => (
<div className="card">
{title && <div className="card-header">{title}</div>}
<div className="card-body">{children}</div>
{actions && <div className="card-footer">{actions}</div>}
</div>
)
86 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<UserContext.Provider value={{ user, setUser }}>
<Dashboard />
</UserContext.Provider>
)
}
2. Mutating Props
return (
<div>
<button onClick={handleSort}>Sort</button>
{users.map(user => <div key={user.id}>{user.name}</div>)}
</div>
)
}
return (
<div>
<button onClick={handleSort}>Sort</button>
{users.map(user => <div key={user.id}>{user.name}</div>)}
</div>
)
}
useEffect(() => {
setDisplayName(user.name) // Syncing props to state (code smell)
}, [user.name])
return <h1>{displayName}</h1>
}
return <h1>{displayName}</h1>
}
88 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
🧠 Interview-Style Q&A
Q: When should you lift state up vs keep it local to a component? A: Lift state up when multiple
components need to share the same data or when sibling components need to communicate. Keep state local
when only one component and its children need the data.
Q: What's the difference between controlled and uncontrolled components in the context of passing
state? A: Controlled components receive their value and change handlers via props (data flows down, events
flow up). Uncontrolled components manage their own internal state and only notify parents of changes.
89 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Q: How would you handle a scenario where deeply nested components need access to user
authentication data? A: Use React Context API to avoid prop drilling. Create a UserContext at the app level
and consume it directly in components that need user data, rather than passing through every intermediate
component.
Q: Explain the "children" prop and how it relates to component composition. A: The children prop
allows components to accept and render arbitrary JSX between their opening and closing tags, enabling
flexible composition patterns like layout components, modals, and wrappers.
Answer: State duplication! User data is fetched separately in each component. Should lift user state to App
component and pass down via props to avoid duplicate API calls and ensure consistency.
90 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Answer:
Question 3: What's the correct way to handle this shopping cart scenario?
return (
<div>
<ProductList products={products} />
<ShoppingCart items={cartItems} />
</div>
)
}
Answer:
91 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<ProductList products={products} onAddToCart={addToCart} />
<ShoppingCart items={cartItems} onUpdateCart={setCartItems} />
</div>
)
}
Question 4: Design a reusable Modal component that can display any content:
// Your task: Create a Modal component that can be used like this:
<Modal isOpen={showModal} onClose={handleClose} title="User Details">
<UserForm user={selectedUser} onSave={handleSave} />
</Modal>
Answer:
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h2>{title}</h2>
<button onClick={onClose}>×</button>
</div>
<div className="modal-body">
{children}
</div>
</div>
</div>
)
}
<table>
{/* Table implementation that can be extended without modification */}
</table>
)
This component composition pattern is the foundation of scalable React applications. Master these
concepts, and you'll be able to build maintainable, reusable, and efficient MERN applications that can grow
with your requirements! 🎯
return (
<div>
<Display counter={counter} />
<Button onClick={increaseByOne} text="plus" />
<Button onClick={setToZero} text="zero" />
<Button onClick={decreaseByOne} text="minus" />
</div>
)
}
94 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
try {
// Call Express API
const response = await fetch('/api/cart/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId, quantity })
})
} catch (error) {
console.error('Add to cart failed:', error)
}
}
setCartItems(prev => {
const newItems = prev.filter(item => item.id !== itemId)
95 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div className="shopping-cart">
<h2>Cart ({cartItems.length} items) - ${total.toFixed(2)}</h2>
{cartItems.map(item => (
<CartItem
key={item.id}
item={item}
onRemove={removeFromCart}
/>
))}
</div>
)
}
return (
<div className="cart-item">
<span>{item.name}</span>
<span>${item.price}</span>
<button onClick={() => onRemove(item.id)}>Remove</button>
</div>
)
}
useEffect(() => {
96 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
socket.onopen = () => {
console.log('Socket connected, updating status...')
setConnectionStatus('connected') // Triggers re-render
}
socket.onclose = () => {
console.log('Socket disconnected, updating status...')
setConnectionStatus('disconnected') // Triggers re-render
}
try {
const response = await fetch(`/api/chat/rooms/${roomId}/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: messageText })
})
prev.map(msg =>
msg.id === tempMessage.id
? { ...savedMessage, status: 'sent' }
: msg
)
) // Another re-render
} catch (error) {
console.error('Failed to send message:', error)
return (
<div className="chat-room">
<ConnectionIndicator status={connectionStatus} />
<MessageList messages={messages} />
<MessageInput onSend={sendMessage} disabled={connectionStatus !==
'connected'} />
</div>
)
}
try {
const token = localStorage.getItem('authToken')
if (!token) {
console.log('No token found, user not authenticated')
setLoading(false) // Re-render
98 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return
}
if (response.ok) {
const userData = await response.json()
console.log('Authentication verified, setting user:', userData.name)
setUser(userData) // Re-render with user data
} else {
console.log('Token invalid, clearing localStorage')
localStorage.removeItem('authToken')
}
} catch (err) {
console.error('Auth check failed:', err)
setError(err.message) // Re-render with error
} finally {
console.log('Auth check complete, clearing loading state')
setLoading(false) // Final re-render
}
}
checkAuth()
}, [])
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
if (response.ok) {
console.log('Login successful, setting user and token')
localStorage.setItem('authToken', data.token)
setUser(data.user) // Re-render - user is now logged in
} else {
console.log('Login failed:', data.message)
setError(data.message) // Re-render with error message
}
} catch (err) {
console.error('Login error:', err)
setError('Login failed. Please try again.') // Re-render with error
} finally {
99 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// Every state change above causes this component and all children to re-render
return (
<AuthContext.Provider value={{ user, loading, error, login, logout }}>
{children}
</AuthContext.Provider>
)
}
100 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<p>Count: {count}</p>
<p>Name: {name}</p>
<button onClick={handleClick}>Update Both</button>
</div>
)
}
return (
<div>
<Child1 value={parentState} /> {/* Re-renders when parentState changes */}
<Child2 /> {/* Also re-renders even though it doesn't
use parentState! */}
<button onClick={() => setParentState(parentState + 1)}>Update</button>
</div>
)
}
console.log('Form rendering:', {
formData,
errors: Object.keys(errors),
isSubmitting
})
setFormData(prev => {
const updated = { ...prev, [field]: value }
console.log('FormData after update:', updated)
return updated
})
try {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
})
if (response.ok) {
console.log('Form submitted successfully')
setFormData({ name: '', email: '' }) // Reset form
} else {
const errorData = await response.json()
console.log('Validation errors received:', errorData.errors)
setErrors(errorData.errors) // Show validation errors
}
} catch (error) {
console.error('Submission failed:', error)
} finally {
console.log('Submission complete, clearing loading state')
102 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<form onSubmit={handleSubmit}>
<input
value={formData.name}
onChange={handleInputChange('name')}
placeholder="Name"
/>
{errors.name && <span className="error">{errors.name}</span>}
<input
value={formData.email}
onChange={handleInputChange('email')}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
// ❌ Wrong assumption
const BadCounter = () => {
const [count, setCount] = useState(0)
// ✅ Correct approach
const GoodCounter = () => {
const [count, setCount] = useState(0)
103 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
setCount(newCount)
console.log(newCount) // Logs updated value
if (newCount === 5) {
alert('Reached 5!')
}
}
}
🧠 Interview-Style Q&As
Q: What happens to child components when a parent component's state changes? A: All child
components re-render by default, even if they don't use the changed state. This can be optimized using
React.memo, useMemo, or useCallback.
Q: Why might console.log show the old state value immediately after calling setState? A: setState is
asynchronous. The state update is scheduled, but the component hasn't re-rendered yet, so the current
variable still holds the old value.
Q: In a MERN app, how would you debug a component that's not updating after an API call? A: Add
console.log statements before and after setState calls, check if the API call is completing successfully, verify
that the state update is actually changing the state value, and ensure you're not mutating state directly.
104 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Answer:
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
console.log('API data received:', data)
userData = data // What's wrong here?
})
Answer: The variable userData is not state, so changing it doesn't trigger a re-render. Should use useState
and useEffect:
Question 3: Trace the console output for this shopping cart update:
Answer:
This understanding of React's re-rendering cycle is fundamental for debugging and optimizing MERN
applications. The console.log approach is invaluable for understanding exactly when and why your
components update! 🎯
106 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
// Before
const Button = (props) => {
return (
<button onClick={props.onClick}>
{props.text}
</button>
)
}
// After
const Button = ({ onClick, text }) => <button onClick={onClick}>{text}</button>
// Before refactoring
const UserCard = (props) => {
return (
<div className="user-card">
<img src={props.user.avatar} alt={props.user.name} />
<h3>{props.user.name}</h3>
<p>{props.user.email}</p>
<button onClick={props.onEdit}>Edit</button>
<button onClick={props.onDelete}>Delete</button>
</div>
)
}
// Reusable micro-components
const PriceDisplay = ({ price, discount = 0 }) => (
<div className="price">
{discount > 0 && <span className="original">${price}</span>}
<span className="current">${(price * (1 - discount)).toFixed(2)}</span>
</div>
)
108 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
Form Components
109 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
{...inputProps}
/>
{error && <span className="error-text">{error}</span>}
</div>
)
Dashboard Components
110 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000)
return () => clearInterval(timer)
}, [])
111 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<DataDisplay data={data} />
<button onClick={handleClick}>Process</button>
</div>
)
}
2. Destructuring Patterns
// Basic destructuring
const UserInfo = ({ user }) => (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
)
// Nested destructuring
const UserProfile = ({ user: { name, email, profile: { bio, avatar } } }) => (
<div>
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
<p>{bio}</p>
</div>
)
112 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
113 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
4. Performance Considerations
114 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div className={cardClass}>
<img src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
<span>{posts.length} posts</span>
<div className="card-actions">
<button onClick={onEdit}>Edit</button>
<button onClick={onDelete}>Delete</button>
<button onClick={onFollow}>
{isFollowing ? 'Unfollow' : 'Follow'}
</button>
</div>
</div>
)
}
115 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<h1>{fullName}</h1>
<p>{personal.contact.email}</p>
<p>{personal.contact.phone}</p>
<p>{professional.role} at {professional.company.name}</p>
</div>
)
}
// ✅ Safe defaults
116 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
const Button = ({
text = 'Button',
onClick = () => {},
disabled = false,
variant = 'primary'
}) => (
<button
className={`btn btn--${variant}`}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
)
🧠 Interview-Style Q&As
Q: When should you use the compact arrow function syntax vs the regular function body? A: Use
compact syntax for simple components that only return JSX without additional logic. Use regular function
body when you need variable declarations, multiple statements, or complex logic before the return.
Q: What are the benefits of destructuring props in React components? A: Cleaner code, easier to see
what props a component uses, better IntelliSense/autocomplete, prevents typos when accessing nested
properties, and makes components easier to refactor.
Q: How does component refactoring help with performance in React? A: Smaller, focused components
can be more easily memoized with React.memo, enable better code splitting, make it easier to identify
performance bottlenecks, and allow React's reconciliation to work more efficiently.
Answer:
117 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
<p>${product.price}</p>
<button onClick={onAddToCart}>Add to Cart</button>
</div>
)
Answer: It's all on one line, making it hard to read. The compact syntax should still be formatted properly:
Answer:
const StatusBadge = ({
status = 'default',
size = 'medium',
theme = 'light'
}) => (
<span className={`badge badge--${status} badge--${size} badge--${theme}`}>
{status === 'default' ? 'Unknown' : status}
</span>
)
Question 4: Which refactoring approach is better for this component and why?
118 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
return (
<div>
<h2>Order #{order.id}</h2>
<p>Customer: {customer.name}</p>
<p>Expected delivery: {formatDate(shipping.estimatedDate)}</p>
<p>Total: ${calculateTotal().toFixed(2)}</p>
<button onClick={onConfirm}>Confirm Order</button>
</div>
)
}
Answer: Keep the regular function body. This component has helper functions and logic that make it
unsuitable for compact syntax. The current structure is clear and readable. Converting to compact syntax
would hurt maintainability.
🚀 Best Practices
1. Consistent Formatting
2. TypeScript Integration
119 / 120
FullStackOpen Part 1 c Component state, event handlers.md 2025-08-08
3. Testability
return (
<div className="price-display">
{discount > 0 && (
<span className="original-price">{currency} {price.toFixed(2)}</span>
)}
<span className="current-price">{currency} {finalPrice.toFixed(2)}</span>
</div>
)
}
These refactoring techniques are essential for professional React development. They make your MERN
applications more maintainable, readable, and easier to debug. Clean, focused components are the
foundation of scalable frontend architecture! 🎯
120 / 120